UNPKG

2.66 MBJavaScriptView Raw
1/**
2* plotly.js (geo) v1.54.2
3* Copyright 2012-2020, Plotly, Inc.
4* All rights reserved.
5* Licensed under the MIT license
6*/
7(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.Plotly = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
8'use strict';
9
10var Lib = _dereq_('../src/lib');
11var rules = {
12 "X,X div": "direction:ltr;font-family:'Open Sans', verdana, arial, sans-serif;margin:0;padding:0;",
13 "X input,X button": "font-family:'Open Sans', verdana, arial, sans-serif;",
14 "X input:focus,X button:focus": "outline:none;",
15 "X a": "text-decoration:none;",
16 "X a:hover": "text-decoration:none;",
17 "X .crisp": "shape-rendering:crispEdges;",
18 "X .user-select-none": "-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;",
19 "X svg": "overflow:hidden;",
20 "X svg a": "fill:#447adb;",
21 "X svg a:hover": "fill:#3c6dc5;",
22 "X .main-svg": "position:absolute;top:0;left:0;pointer-events:none;",
23 "X .main-svg .draglayer": "pointer-events:all;",
24 "X .cursor-default": "cursor:default;",
25 "X .cursor-pointer": "cursor:pointer;",
26 "X .cursor-crosshair": "cursor:crosshair;",
27 "X .cursor-move": "cursor:move;",
28 "X .cursor-col-resize": "cursor:col-resize;",
29 "X .cursor-row-resize": "cursor:row-resize;",
30 "X .cursor-ns-resize": "cursor:ns-resize;",
31 "X .cursor-ew-resize": "cursor:ew-resize;",
32 "X .cursor-sw-resize": "cursor:sw-resize;",
33 "X .cursor-s-resize": "cursor:s-resize;",
34 "X .cursor-se-resize": "cursor:se-resize;",
35 "X .cursor-w-resize": "cursor:w-resize;",
36 "X .cursor-e-resize": "cursor:e-resize;",
37 "X .cursor-nw-resize": "cursor:nw-resize;",
38 "X .cursor-n-resize": "cursor:n-resize;",
39 "X .cursor-ne-resize": "cursor:ne-resize;",
40 "X .cursor-grab": "cursor:-webkit-grab;cursor:grab;",
41 "X .modebar": "position:absolute;top:2px;right:2px;",
42 "X .ease-bg": "-webkit-transition:background-color 0.3s ease 0s;-moz-transition:background-color 0.3s ease 0s;-ms-transition:background-color 0.3s ease 0s;-o-transition:background-color 0.3s ease 0s;transition:background-color 0.3s ease 0s;",
43 "X .modebar--hover>:not(.watermark)": "opacity:0;-webkit-transition:opacity 0.3s ease 0s;-moz-transition:opacity 0.3s ease 0s;-ms-transition:opacity 0.3s ease 0s;-o-transition:opacity 0.3s ease 0s;transition:opacity 0.3s ease 0s;",
44 "X:hover .modebar--hover .modebar-group": "opacity:1;",
45 "X .modebar-group": "float:left;display:inline-block;box-sizing:border-box;padding-left:8px;position:relative;vertical-align:middle;white-space:nowrap;",
46 "X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;",
47 "X .modebar-btn svg": "position:relative;top:2px;",
48 "X .modebar.vertical": "display:flex;flex-direction:column;flex-wrap:wrap;align-content:flex-end;max-height:100%;",
49 "X .modebar.vertical svg": "top:-1px;",
50 "X .modebar.vertical .modebar-group": "display:block;float:none;padding-left:0px;padding-bottom:8px;",
51 "X .modebar.vertical .modebar-group .modebar-btn": "display:block;text-align:center;",
52 "X [data-title]:before,X [data-title]:after": "position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);display:none;opacity:0;z-index:1001;pointer-events:none;top:110%;right:50%;",
53 "X [data-title]:hover:before,X [data-title]:hover:after": "display:block;opacity:1;",
54 "X [data-title]:before": "content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1002;margin-top:-12px;border-bottom-color:#69738a;margin-right:-6px;",
55 "X [data-title]:after": "content:attr(data-title);background:#69738a;color:white;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;margin-right:-18px;border-radius:2px;",
56 "X .vertical [data-title]:before,X .vertical [data-title]:after": "top:0%;right:200%;",
57 "X .vertical [data-title]:before": "border:6px solid transparent;border-left-color:#69738a;margin-top:8px;margin-right:-30px;",
58 "X .select-outline": "fill:none;stroke-width:1;shape-rendering:crispEdges;",
59 "X .select-outline-1": "stroke:white;",
60 "X .select-outline-2": "stroke:black;stroke-dasharray:2px 2px;",
61 Y: "font-family:'Open Sans', verdana, arial, sans-serif;position:fixed;top:50px;right:20px;z-index:10000;font-size:10pt;max-width:180px;",
62 "Y p": "margin:0;",
63 "Y .notifier-note": "min-width:180px;max-width:250px;border:1px solid #fff;z-index:3000;margin:0;background-color:#8c97af;background-color:rgba(140,151,175,0.9);color:#fff;padding:10px;overflow-wrap:break-word;word-wrap:break-word;-ms-hyphens:auto;-webkit-hyphens:auto;hyphens:auto;",
64 "Y .notifier-close": "color:#fff;opacity:0.8;float:right;padding:0 5px;background:none;border:none;font-size:20px;font-weight:bold;line-height:20px;",
65 "Y .notifier-close:hover": "color:#444;text-decoration:none;cursor:pointer;"
66};
67
68for(var selector in rules) {
69 var fullSelector = selector.replace(/^,/,' ,')
70 .replace(/X/g, '.js-plotly-plot .plotly')
71 .replace(/Y/g, '.plotly-notifier');
72 Lib.addStyleRule(fullSelector, rules[selector]);
73}
74
75},{"../src/lib":177}],2:[function(_dereq_,module,exports){
76/**
77* Copyright 2012-2020, Plotly, Inc.
78* All rights reserved.
79*
80* This source code is licensed under the MIT license found in the
81* LICENSE file in the root directory of this source tree.
82*/
83
84'use strict';
85
86module.exports = _dereq_('../src/traces/choropleth');
87
88},{"../src/traces/choropleth":286}],3:[function(_dereq_,module,exports){
89/**
90* Copyright 2012-2020, Plotly, Inc.
91* All rights reserved.
92*
93* This source code is licensed under the MIT license found in the
94* LICENSE file in the root directory of this source tree.
95*/
96
97'use strict';
98
99module.exports = _dereq_('../src/core');
100
101},{"../src/core":157}],4:[function(_dereq_,module,exports){
102/**
103* Copyright 2012-2020, Plotly, Inc.
104* All rights reserved.
105*
106* This source code is licensed under the MIT license found in the
107* LICENSE file in the root directory of this source tree.
108*/
109
110'use strict';
111
112var Plotly = _dereq_('./core');
113
114Plotly.register([
115 _dereq_('./scattergeo'),
116 _dereq_('./choropleth')
117]);
118
119module.exports = Plotly;
120
121},{"./choropleth":2,"./core":3,"./scattergeo":5}],5:[function(_dereq_,module,exports){
122/**
123* Copyright 2012-2020, Plotly, Inc.
124* All rights reserved.
125*
126* This source code is licensed under the MIT license found in the
127* LICENSE file in the root directory of this source tree.
128*/
129
130'use strict';
131
132module.exports = _dereq_('../src/traces/scattergeo');
133
134},{"../src/traces/scattergeo":327}],6:[function(_dereq_,module,exports){
135"use strict";
136Object.defineProperty(exports, "__esModule", { value: true });
137var meta_1 = _dereq_("@turf/meta");
138// Note: change RADIUS => earthRadius
139var RADIUS = 6378137;
140/**
141 * Takes one or more features and returns their area in square meters.
142 *
143 * @name area
144 * @param {GeoJSON} geojson input GeoJSON feature(s)
145 * @returns {number} area in square meters
146 * @example
147 * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
148 *
149 * var area = turf.area(polygon);
150 *
151 * //addToMap
152 * var addToMap = [polygon]
153 * polygon.properties.area = area
154 */
155function area(geojson) {
156 return meta_1.geomReduce(geojson, function (value, geom) {
157 return value + calculateArea(geom);
158 }, 0);
159}
160exports.default = area;
161/**
162 * Calculate Area
163 *
164 * @private
165 * @param {Geometry} geom GeoJSON Geometries
166 * @returns {number} area
167 */
168function calculateArea(geom) {
169 var total = 0;
170 var i;
171 switch (geom.type) {
172 case "Polygon":
173 return polygonArea(geom.coordinates);
174 case "MultiPolygon":
175 for (i = 0; i < geom.coordinates.length; i++) {
176 total += polygonArea(geom.coordinates[i]);
177 }
178 return total;
179 case "Point":
180 case "MultiPoint":
181 case "LineString":
182 case "MultiLineString":
183 return 0;
184 }
185 return 0;
186}
187function polygonArea(coords) {
188 var total = 0;
189 if (coords && coords.length > 0) {
190 total += Math.abs(ringArea(coords[0]));
191 for (var i = 1; i < coords.length; i++) {
192 total -= Math.abs(ringArea(coords[i]));
193 }
194 }
195 return total;
196}
197/**
198 * @private
199 * Calculate the approximate area of the polygon were it projected onto the earth.
200 * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
201 *
202 * Reference:
203 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere",
204 * JPL Publication 07-03, Jet Propulsion
205 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
206 *
207 * @param {Array<Array<number>>} coords Ring Coordinates
208 * @returns {number} The approximate signed geodesic area of the polygon in square meters.
209 */
210function ringArea(coords) {
211 var p1;
212 var p2;
213 var p3;
214 var lowerIndex;
215 var middleIndex;
216 var upperIndex;
217 var i;
218 var total = 0;
219 var coordsLength = coords.length;
220 if (coordsLength > 2) {
221 for (i = 0; i < coordsLength; i++) {
222 if (i === coordsLength - 2) {
223 lowerIndex = coordsLength - 2;
224 middleIndex = coordsLength - 1;
225 upperIndex = 0;
226 }
227 else if (i === coordsLength - 1) {
228 lowerIndex = coordsLength - 1;
229 middleIndex = 0;
230 upperIndex = 1;
231 }
232 else {
233 lowerIndex = i;
234 middleIndex = i + 1;
235 upperIndex = i + 2;
236 }
237 p1 = coords[lowerIndex];
238 p2 = coords[middleIndex];
239 p3 = coords[upperIndex];
240 total += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
241 }
242 total = total * RADIUS * RADIUS / 2;
243 }
244 return total;
245}
246function rad(num) {
247 return num * Math.PI / 180;
248}
249
250},{"@turf/meta":10}],7:[function(_dereq_,module,exports){
251"use strict";
252Object.defineProperty(exports, "__esModule", { value: true });
253var meta_1 = _dereq_("@turf/meta");
254/**
255 * Takes a set of features, calculates the bbox of all input features, and returns a bounding box.
256 *
257 * @name bbox
258 * @param {GeoJSON} geojson any GeoJSON object
259 * @returns {BBox} bbox extent in [minX, minY, maxX, maxY] order
260 * @example
261 * var line = turf.lineString([[-74, 40], [-78, 42], [-82, 35]]);
262 * var bbox = turf.bbox(line);
263 * var bboxPolygon = turf.bboxPolygon(bbox);
264 *
265 * //addToMap
266 * var addToMap = [line, bboxPolygon]
267 */
268function bbox(geojson) {
269 var result = [Infinity, Infinity, -Infinity, -Infinity];
270 meta_1.coordEach(geojson, function (coord) {
271 if (result[0] > coord[0]) {
272 result[0] = coord[0];
273 }
274 if (result[1] > coord[1]) {
275 result[1] = coord[1];
276 }
277 if (result[2] < coord[0]) {
278 result[2] = coord[0];
279 }
280 if (result[3] < coord[1]) {
281 result[3] = coord[1];
282 }
283 });
284 return result;
285}
286exports.default = bbox;
287
288},{"@turf/meta":10}],8:[function(_dereq_,module,exports){
289"use strict";
290Object.defineProperty(exports, "__esModule", { value: true });
291var meta_1 = _dereq_("@turf/meta");
292var helpers_1 = _dereq_("@turf/helpers");
293/**
294 * Takes one or more features and calculates the centroid using the mean of all vertices.
295 * This lessens the effect of small islands and artifacts when calculating the centroid of a set of polygons.
296 *
297 * @name centroid
298 * @param {GeoJSON} geojson GeoJSON to be centered
299 * @param {Object} [options={}] Optional Parameters
300 * @param {Object} [options.properties={}] an Object that is used as the {@link Feature}'s properties
301 * @returns {Feature<Point>} the centroid of the input features
302 * @example
303 * var polygon = turf.polygon([[[-81, 41], [-88, 36], [-84, 31], [-80, 33], [-77, 39], [-81, 41]]]);
304 *
305 * var centroid = turf.centroid(polygon);
306 *
307 * //addToMap
308 * var addToMap = [polygon, centroid]
309 */
310function centroid(geojson, options) {
311 if (options === void 0) { options = {}; }
312 var xSum = 0;
313 var ySum = 0;
314 var len = 0;
315 meta_1.coordEach(geojson, function (coord) {
316 xSum += coord[0];
317 ySum += coord[1];
318 len++;
319 });
320 return helpers_1.point([xSum / len, ySum / len], options.properties);
321}
322exports.default = centroid;
323
324},{"@turf/helpers":9,"@turf/meta":10}],9:[function(_dereq_,module,exports){
325"use strict";
326Object.defineProperty(exports, "__esModule", { value: true });
327/**
328 * @module helpers
329 */
330/**
331 * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
332 *
333 * @memberof helpers
334 * @type {number}
335 */
336exports.earthRadius = 6371008.8;
337/**
338 * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
339 *
340 * @memberof helpers
341 * @type {Object}
342 */
343exports.factors = {
344 centimeters: exports.earthRadius * 100,
345 centimetres: exports.earthRadius * 100,
346 degrees: exports.earthRadius / 111325,
347 feet: exports.earthRadius * 3.28084,
348 inches: exports.earthRadius * 39.370,
349 kilometers: exports.earthRadius / 1000,
350 kilometres: exports.earthRadius / 1000,
351 meters: exports.earthRadius,
352 metres: exports.earthRadius,
353 miles: exports.earthRadius / 1609.344,
354 millimeters: exports.earthRadius * 1000,
355 millimetres: exports.earthRadius * 1000,
356 nauticalmiles: exports.earthRadius / 1852,
357 radians: 1,
358 yards: exports.earthRadius / 1.0936,
359};
360/**
361 * Units of measurement factors based on 1 meter.
362 *
363 * @memberof helpers
364 * @type {Object}
365 */
366exports.unitsFactors = {
367 centimeters: 100,
368 centimetres: 100,
369 degrees: 1 / 111325,
370 feet: 3.28084,
371 inches: 39.370,
372 kilometers: 1 / 1000,
373 kilometres: 1 / 1000,
374 meters: 1,
375 metres: 1,
376 miles: 1 / 1609.344,
377 millimeters: 1000,
378 millimetres: 1000,
379 nauticalmiles: 1 / 1852,
380 radians: 1 / exports.earthRadius,
381 yards: 1 / 1.0936,
382};
383/**
384 * Area of measurement factors based on 1 square meter.
385 *
386 * @memberof helpers
387 * @type {Object}
388 */
389exports.areaFactors = {
390 acres: 0.000247105,
391 centimeters: 10000,
392 centimetres: 10000,
393 feet: 10.763910417,
394 inches: 1550.003100006,
395 kilometers: 0.000001,
396 kilometres: 0.000001,
397 meters: 1,
398 metres: 1,
399 miles: 3.86e-7,
400 millimeters: 1000000,
401 millimetres: 1000000,
402 yards: 1.195990046,
403};
404/**
405 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
406 *
407 * @name feature
408 * @param {Geometry} geometry input geometry
409 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
410 * @param {Object} [options={}] Optional Parameters
411 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
412 * @param {string|number} [options.id] Identifier associated with the Feature
413 * @returns {Feature} a GeoJSON Feature
414 * @example
415 * var geometry = {
416 * "type": "Point",
417 * "coordinates": [110, 50]
418 * };
419 *
420 * var feature = turf.feature(geometry);
421 *
422 * //=feature
423 */
424function feature(geom, properties, options) {
425 if (options === void 0) { options = {}; }
426 var feat = { type: "Feature" };
427 if (options.id === 0 || options.id) {
428 feat.id = options.id;
429 }
430 if (options.bbox) {
431 feat.bbox = options.bbox;
432 }
433 feat.properties = properties || {};
434 feat.geometry = geom;
435 return feat;
436}
437exports.feature = feature;
438/**
439 * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
440 * For GeometryCollection type use `helpers.geometryCollection`
441 *
442 * @name geometry
443 * @param {string} type Geometry Type
444 * @param {Array<any>} coordinates Coordinates
445 * @param {Object} [options={}] Optional Parameters
446 * @returns {Geometry} a GeoJSON Geometry
447 * @example
448 * var type = "Point";
449 * var coordinates = [110, 50];
450 * var geometry = turf.geometry(type, coordinates);
451 * // => geometry
452 */
453function geometry(type, coordinates, options) {
454 if (options === void 0) { options = {}; }
455 switch (type) {
456 case "Point": return point(coordinates).geometry;
457 case "LineString": return lineString(coordinates).geometry;
458 case "Polygon": return polygon(coordinates).geometry;
459 case "MultiPoint": return multiPoint(coordinates).geometry;
460 case "MultiLineString": return multiLineString(coordinates).geometry;
461 case "MultiPolygon": return multiPolygon(coordinates).geometry;
462 default: throw new Error(type + " is invalid");
463 }
464}
465exports.geometry = geometry;
466/**
467 * Creates a {@link Point} {@link Feature} from a Position.
468 *
469 * @name point
470 * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
471 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
472 * @param {Object} [options={}] Optional Parameters
473 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
474 * @param {string|number} [options.id] Identifier associated with the Feature
475 * @returns {Feature<Point>} a Point feature
476 * @example
477 * var point = turf.point([-75.343, 39.984]);
478 *
479 * //=point
480 */
481function point(coordinates, properties, options) {
482 if (options === void 0) { options = {}; }
483 var geom = {
484 type: "Point",
485 coordinates: coordinates,
486 };
487 return feature(geom, properties, options);
488}
489exports.point = point;
490/**
491 * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
492 *
493 * @name points
494 * @param {Array<Array<number>>} coordinates an array of Points
495 * @param {Object} [properties={}] Translate these properties to each Feature
496 * @param {Object} [options={}] Optional Parameters
497 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
498 * associated with the FeatureCollection
499 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
500 * @returns {FeatureCollection<Point>} Point Feature
501 * @example
502 * var points = turf.points([
503 * [-75, 39],
504 * [-80, 45],
505 * [-78, 50]
506 * ]);
507 *
508 * //=points
509 */
510function points(coordinates, properties, options) {
511 if (options === void 0) { options = {}; }
512 return featureCollection(coordinates.map(function (coords) {
513 return point(coords, properties);
514 }), options);
515}
516exports.points = points;
517/**
518 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
519 *
520 * @name polygon
521 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
522 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
523 * @param {Object} [options={}] Optional Parameters
524 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
525 * @param {string|number} [options.id] Identifier associated with the Feature
526 * @returns {Feature<Polygon>} Polygon Feature
527 * @example
528 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
529 *
530 * //=polygon
531 */
532function polygon(coordinates, properties, options) {
533 if (options === void 0) { options = {}; }
534 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
535 var ring = coordinates_1[_i];
536 if (ring.length < 4) {
537 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
538 }
539 for (var j = 0; j < ring[ring.length - 1].length; j++) {
540 // Check if first point of Polygon contains two numbers
541 if (ring[ring.length - 1][j] !== ring[0][j]) {
542 throw new Error("First and last Position are not equivalent.");
543 }
544 }
545 }
546 var geom = {
547 type: "Polygon",
548 coordinates: coordinates,
549 };
550 return feature(geom, properties, options);
551}
552exports.polygon = polygon;
553/**
554 * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
555 *
556 * @name polygons
557 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
558 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
559 * @param {Object} [options={}] Optional Parameters
560 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
561 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
562 * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
563 * @example
564 * var polygons = turf.polygons([
565 * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
566 * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
567 * ]);
568 *
569 * //=polygons
570 */
571function polygons(coordinates, properties, options) {
572 if (options === void 0) { options = {}; }
573 return featureCollection(coordinates.map(function (coords) {
574 return polygon(coords, properties);
575 }), options);
576}
577exports.polygons = polygons;
578/**
579 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
580 *
581 * @name lineString
582 * @param {Array<Array<number>>} coordinates an array of Positions
583 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
584 * @param {Object} [options={}] Optional Parameters
585 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
586 * @param {string|number} [options.id] Identifier associated with the Feature
587 * @returns {Feature<LineString>} LineString Feature
588 * @example
589 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
590 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
591 *
592 * //=linestring1
593 * //=linestring2
594 */
595function lineString(coordinates, properties, options) {
596 if (options === void 0) { options = {}; }
597 if (coordinates.length < 2) {
598 throw new Error("coordinates must be an array of two or more positions");
599 }
600 var geom = {
601 type: "LineString",
602 coordinates: coordinates,
603 };
604 return feature(geom, properties, options);
605}
606exports.lineString = lineString;
607/**
608 * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
609 *
610 * @name lineStrings
611 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
612 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
613 * @param {Object} [options={}] Optional Parameters
614 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
615 * associated with the FeatureCollection
616 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
617 * @returns {FeatureCollection<LineString>} LineString FeatureCollection
618 * @example
619 * var linestrings = turf.lineStrings([
620 * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
621 * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
622 * ]);
623 *
624 * //=linestrings
625 */
626function lineStrings(coordinates, properties, options) {
627 if (options === void 0) { options = {}; }
628 return featureCollection(coordinates.map(function (coords) {
629 return lineString(coords, properties);
630 }), options);
631}
632exports.lineStrings = lineStrings;
633/**
634 * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
635 *
636 * @name featureCollection
637 * @param {Feature[]} features input features
638 * @param {Object} [options={}] Optional Parameters
639 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
640 * @param {string|number} [options.id] Identifier associated with the Feature
641 * @returns {FeatureCollection} FeatureCollection of Features
642 * @example
643 * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
644 * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
645 * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
646 *
647 * var collection = turf.featureCollection([
648 * locationA,
649 * locationB,
650 * locationC
651 * ]);
652 *
653 * //=collection
654 */
655function featureCollection(features, options) {
656 if (options === void 0) { options = {}; }
657 var fc = { type: "FeatureCollection" };
658 if (options.id) {
659 fc.id = options.id;
660 }
661 if (options.bbox) {
662 fc.bbox = options.bbox;
663 }
664 fc.features = features;
665 return fc;
666}
667exports.featureCollection = featureCollection;
668/**
669 * Creates a {@link Feature<MultiLineString>} based on a
670 * coordinate array. Properties can be added optionally.
671 *
672 * @name multiLineString
673 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
674 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
675 * @param {Object} [options={}] Optional Parameters
676 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
677 * @param {string|number} [options.id] Identifier associated with the Feature
678 * @returns {Feature<MultiLineString>} a MultiLineString feature
679 * @throws {Error} if no coordinates are passed
680 * @example
681 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
682 *
683 * //=multiLine
684 */
685function multiLineString(coordinates, properties, options) {
686 if (options === void 0) { options = {}; }
687 var geom = {
688 type: "MultiLineString",
689 coordinates: coordinates,
690 };
691 return feature(geom, properties, options);
692}
693exports.multiLineString = multiLineString;
694/**
695 * Creates a {@link Feature<MultiPoint>} based on a
696 * coordinate array. Properties can be added optionally.
697 *
698 * @name multiPoint
699 * @param {Array<Array<number>>} coordinates an array of Positions
700 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
701 * @param {Object} [options={}] Optional Parameters
702 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
703 * @param {string|number} [options.id] Identifier associated with the Feature
704 * @returns {Feature<MultiPoint>} a MultiPoint feature
705 * @throws {Error} if no coordinates are passed
706 * @example
707 * var multiPt = turf.multiPoint([[0,0],[10,10]]);
708 *
709 * //=multiPt
710 */
711function multiPoint(coordinates, properties, options) {
712 if (options === void 0) { options = {}; }
713 var geom = {
714 type: "MultiPoint",
715 coordinates: coordinates,
716 };
717 return feature(geom, properties, options);
718}
719exports.multiPoint = multiPoint;
720/**
721 * Creates a {@link Feature<MultiPolygon>} based on a
722 * coordinate array. Properties can be added optionally.
723 *
724 * @name multiPolygon
725 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
726 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
727 * @param {Object} [options={}] Optional Parameters
728 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
729 * @param {string|number} [options.id] Identifier associated with the Feature
730 * @returns {Feature<MultiPolygon>} a multipolygon feature
731 * @throws {Error} if no coordinates are passed
732 * @example
733 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
734 *
735 * //=multiPoly
736 *
737 */
738function multiPolygon(coordinates, properties, options) {
739 if (options === void 0) { options = {}; }
740 var geom = {
741 type: "MultiPolygon",
742 coordinates: coordinates,
743 };
744 return feature(geom, properties, options);
745}
746exports.multiPolygon = multiPolygon;
747/**
748 * Creates a {@link Feature<GeometryCollection>} based on a
749 * coordinate array. Properties can be added optionally.
750 *
751 * @name geometryCollection
752 * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
753 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
754 * @param {Object} [options={}] Optional Parameters
755 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
756 * @param {string|number} [options.id] Identifier associated with the Feature
757 * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
758 * @example
759 * var pt = turf.geometry("Point", [100, 0]);
760 * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
761 * var collection = turf.geometryCollection([pt, line]);
762 *
763 * // => collection
764 */
765function geometryCollection(geometries, properties, options) {
766 if (options === void 0) { options = {}; }
767 var geom = {
768 type: "GeometryCollection",
769 geometries: geometries,
770 };
771 return feature(geom, properties, options);
772}
773exports.geometryCollection = geometryCollection;
774/**
775 * Round number to precision
776 *
777 * @param {number} num Number
778 * @param {number} [precision=0] Precision
779 * @returns {number} rounded number
780 * @example
781 * turf.round(120.4321)
782 * //=120
783 *
784 * turf.round(120.4321, 2)
785 * //=120.43
786 */
787function round(num, precision) {
788 if (precision === void 0) { precision = 0; }
789 if (precision && !(precision >= 0)) {
790 throw new Error("precision must be a positive number");
791 }
792 var multiplier = Math.pow(10, precision || 0);
793 return Math.round(num * multiplier) / multiplier;
794}
795exports.round = round;
796/**
797 * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
798 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
799 *
800 * @name radiansToLength
801 * @param {number} radians in radians across the sphere
802 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
803 * meters, kilometres, kilometers.
804 * @returns {number} distance
805 */
806function radiansToLength(radians, units) {
807 if (units === void 0) { units = "kilometers"; }
808 var factor = exports.factors[units];
809 if (!factor) {
810 throw new Error(units + " units is invalid");
811 }
812 return radians * factor;
813}
814exports.radiansToLength = radiansToLength;
815/**
816 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
817 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
818 *
819 * @name lengthToRadians
820 * @param {number} distance in real units
821 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
822 * meters, kilometres, kilometers.
823 * @returns {number} radians
824 */
825function lengthToRadians(distance, units) {
826 if (units === void 0) { units = "kilometers"; }
827 var factor = exports.factors[units];
828 if (!factor) {
829 throw new Error(units + " units is invalid");
830 }
831 return distance / factor;
832}
833exports.lengthToRadians = lengthToRadians;
834/**
835 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
836 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
837 *
838 * @name lengthToDegrees
839 * @param {number} distance in real units
840 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
841 * meters, kilometres, kilometers.
842 * @returns {number} degrees
843 */
844function lengthToDegrees(distance, units) {
845 return radiansToDegrees(lengthToRadians(distance, units));
846}
847exports.lengthToDegrees = lengthToDegrees;
848/**
849 * Converts any bearing angle from the north line direction (positive clockwise)
850 * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
851 *
852 * @name bearingToAzimuth
853 * @param {number} bearing angle, between -180 and +180 degrees
854 * @returns {number} angle between 0 and 360 degrees
855 */
856function bearingToAzimuth(bearing) {
857 var angle = bearing % 360;
858 if (angle < 0) {
859 angle += 360;
860 }
861 return angle;
862}
863exports.bearingToAzimuth = bearingToAzimuth;
864/**
865 * Converts an angle in radians to degrees
866 *
867 * @name radiansToDegrees
868 * @param {number} radians angle in radians
869 * @returns {number} degrees between 0 and 360 degrees
870 */
871function radiansToDegrees(radians) {
872 var degrees = radians % (2 * Math.PI);
873 return degrees * 180 / Math.PI;
874}
875exports.radiansToDegrees = radiansToDegrees;
876/**
877 * Converts an angle in degrees to radians
878 *
879 * @name degreesToRadians
880 * @param {number} degrees angle between 0 and 360 degrees
881 * @returns {number} angle in radians
882 */
883function degreesToRadians(degrees) {
884 var radians = degrees % 360;
885 return radians * Math.PI / 180;
886}
887exports.degreesToRadians = degreesToRadians;
888/**
889 * Converts a length to the requested unit.
890 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
891 *
892 * @param {number} length to be converted
893 * @param {Units} [originalUnit="kilometers"] of the length
894 * @param {Units} [finalUnit="kilometers"] returned unit
895 * @returns {number} the converted length
896 */
897function convertLength(length, originalUnit, finalUnit) {
898 if (originalUnit === void 0) { originalUnit = "kilometers"; }
899 if (finalUnit === void 0) { finalUnit = "kilometers"; }
900 if (!(length >= 0)) {
901 throw new Error("length must be a positive number");
902 }
903 return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
904}
905exports.convertLength = convertLength;
906/**
907 * Converts a area to the requested unit.
908 * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
909 * @param {number} area to be converted
910 * @param {Units} [originalUnit="meters"] of the distance
911 * @param {Units} [finalUnit="kilometers"] returned unit
912 * @returns {number} the converted distance
913 */
914function convertArea(area, originalUnit, finalUnit) {
915 if (originalUnit === void 0) { originalUnit = "meters"; }
916 if (finalUnit === void 0) { finalUnit = "kilometers"; }
917 if (!(area >= 0)) {
918 throw new Error("area must be a positive number");
919 }
920 var startFactor = exports.areaFactors[originalUnit];
921 if (!startFactor) {
922 throw new Error("invalid original units");
923 }
924 var finalFactor = exports.areaFactors[finalUnit];
925 if (!finalFactor) {
926 throw new Error("invalid final units");
927 }
928 return (area / startFactor) * finalFactor;
929}
930exports.convertArea = convertArea;
931/**
932 * isNumber
933 *
934 * @param {*} num Number to validate
935 * @returns {boolean} true/false
936 * @example
937 * turf.isNumber(123)
938 * //=true
939 * turf.isNumber('foo')
940 * //=false
941 */
942function isNumber(num) {
943 return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
944}
945exports.isNumber = isNumber;
946/**
947 * isObject
948 *
949 * @param {*} input variable to validate
950 * @returns {boolean} true/false
951 * @example
952 * turf.isObject({elevation: 10})
953 * //=true
954 * turf.isObject('foo')
955 * //=false
956 */
957function isObject(input) {
958 return (!!input) && (input.constructor === Object);
959}
960exports.isObject = isObject;
961/**
962 * Validate BBox
963 *
964 * @private
965 * @param {Array<number>} bbox BBox to validate
966 * @returns {void}
967 * @throws Error if BBox is not valid
968 * @example
969 * validateBBox([-180, -40, 110, 50])
970 * //=OK
971 * validateBBox([-180, -40])
972 * //=Error
973 * validateBBox('Foo')
974 * //=Error
975 * validateBBox(5)
976 * //=Error
977 * validateBBox(null)
978 * //=Error
979 * validateBBox(undefined)
980 * //=Error
981 */
982function validateBBox(bbox) {
983 if (!bbox) {
984 throw new Error("bbox is required");
985 }
986 if (!Array.isArray(bbox)) {
987 throw new Error("bbox must be an Array");
988 }
989 if (bbox.length !== 4 && bbox.length !== 6) {
990 throw new Error("bbox must be an Array of 4 or 6 numbers");
991 }
992 bbox.forEach(function (num) {
993 if (!isNumber(num)) {
994 throw new Error("bbox must only contain numbers");
995 }
996 });
997}
998exports.validateBBox = validateBBox;
999/**
1000 * Validate Id
1001 *
1002 * @private
1003 * @param {string|number} id Id to validate
1004 * @returns {void}
1005 * @throws Error if Id is not valid
1006 * @example
1007 * validateId([-180, -40, 110, 50])
1008 * //=Error
1009 * validateId([-180, -40])
1010 * //=Error
1011 * validateId('Foo')
1012 * //=OK
1013 * validateId(5)
1014 * //=OK
1015 * validateId(null)
1016 * //=Error
1017 * validateId(undefined)
1018 * //=Error
1019 */
1020function validateId(id) {
1021 if (!id) {
1022 throw new Error("id is required");
1023 }
1024 if (["string", "number"].indexOf(typeof id) === -1) {
1025 throw new Error("id must be a number or a string");
1026 }
1027}
1028exports.validateId = validateId;
1029// Deprecated methods
1030function radians2degrees() {
1031 throw new Error("method has been renamed to `radiansToDegrees`");
1032}
1033exports.radians2degrees = radians2degrees;
1034function degrees2radians() {
1035 throw new Error("method has been renamed to `degreesToRadians`");
1036}
1037exports.degrees2radians = degrees2radians;
1038function distanceToDegrees() {
1039 throw new Error("method has been renamed to `lengthToDegrees`");
1040}
1041exports.distanceToDegrees = distanceToDegrees;
1042function distanceToRadians() {
1043 throw new Error("method has been renamed to `lengthToRadians`");
1044}
1045exports.distanceToRadians = distanceToRadians;
1046function radiansToDistance() {
1047 throw new Error("method has been renamed to `radiansToLength`");
1048}
1049exports.radiansToDistance = radiansToDistance;
1050function bearingToAngle() {
1051 throw new Error("method has been renamed to `bearingToAzimuth`");
1052}
1053exports.bearingToAngle = bearingToAngle;
1054function convertDistance() {
1055 throw new Error("method has been renamed to `convertLength`");
1056}
1057exports.convertDistance = convertDistance;
1058
1059},{}],10:[function(_dereq_,module,exports){
1060'use strict';
1061
1062Object.defineProperty(exports, '__esModule', { value: true });
1063
1064var helpers = _dereq_('@turf/helpers');
1065
1066/**
1067 * Callback for coordEach
1068 *
1069 * @callback coordEachCallback
1070 * @param {Array<number>} currentCoord The current coordinate being processed.
1071 * @param {number} coordIndex The current index of the coordinate being processed.
1072 * @param {number} featureIndex The current index of the Feature being processed.
1073 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1074 * @param {number} geometryIndex The current index of the Geometry being processed.
1075 */
1076
1077/**
1078 * Iterate over coordinates in any GeoJSON object, similar to Array.forEach()
1079 *
1080 * @name coordEach
1081 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1082 * @param {Function} callback a method that takes (currentCoord, coordIndex, featureIndex, multiFeatureIndex)
1083 * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration.
1084 * @returns {void}
1085 * @example
1086 * var features = turf.featureCollection([
1087 * turf.point([26, 37], {"foo": "bar"}),
1088 * turf.point([36, 53], {"hello": "world"})
1089 * ]);
1090 *
1091 * turf.coordEach(features, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) {
1092 * //=currentCoord
1093 * //=coordIndex
1094 * //=featureIndex
1095 * //=multiFeatureIndex
1096 * //=geometryIndex
1097 * });
1098 */
1099function coordEach(geojson, callback, excludeWrapCoord) {
1100 // Handles null Geometry -- Skips this GeoJSON
1101 if (geojson === null) return;
1102 var j, k, l, geometry, stopG, coords,
1103 geometryMaybeCollection,
1104 wrapShrink = 0,
1105 coordIndex = 0,
1106 isGeometryCollection,
1107 type = geojson.type,
1108 isFeatureCollection = type === 'FeatureCollection',
1109 isFeature = type === 'Feature',
1110 stop = isFeatureCollection ? geojson.features.length : 1;
1111
1112 // This logic may look a little weird. The reason why it is that way
1113 // is because it's trying to be fast. GeoJSON supports multiple kinds
1114 // of objects at its root: FeatureCollection, Features, Geometries.
1115 // This function has the responsibility of handling all of them, and that
1116 // means that some of the `for` loops you see below actually just don't apply
1117 // to certain inputs. For instance, if you give this just a
1118 // Point geometry, then both loops are short-circuited and all we do
1119 // is gradually rename the input until it's called 'geometry'.
1120 //
1121 // This also aims to allocate as few resources as possible: just a
1122 // few numbers and booleans, rather than any temporary arrays as would
1123 // be required with the normalization approach.
1124 for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
1125 geometryMaybeCollection = (isFeatureCollection ? geojson.features[featureIndex].geometry :
1126 (isFeature ? geojson.geometry : geojson));
1127 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
1128 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
1129
1130 for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
1131 var multiFeatureIndex = 0;
1132 var geometryIndex = 0;
1133 geometry = isGeometryCollection ?
1134 geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
1135
1136 // Handles null Geometry -- Skips this geometry
1137 if (geometry === null) continue;
1138 coords = geometry.coordinates;
1139 var geomType = geometry.type;
1140
1141 wrapShrink = (excludeWrapCoord && (geomType === 'Polygon' || geomType === 'MultiPolygon')) ? 1 : 0;
1142
1143 switch (geomType) {
1144 case null:
1145 break;
1146 case 'Point':
1147 if (callback(coords, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false;
1148 coordIndex++;
1149 multiFeatureIndex++;
1150 break;
1151 case 'LineString':
1152 case 'MultiPoint':
1153 for (j = 0; j < coords.length; j++) {
1154 if (callback(coords[j], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false;
1155 coordIndex++;
1156 if (geomType === 'MultiPoint') multiFeatureIndex++;
1157 }
1158 if (geomType === 'LineString') multiFeatureIndex++;
1159 break;
1160 case 'Polygon':
1161 case 'MultiLineString':
1162 for (j = 0; j < coords.length; j++) {
1163 for (k = 0; k < coords[j].length - wrapShrink; k++) {
1164 if (callback(coords[j][k], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false;
1165 coordIndex++;
1166 }
1167 if (geomType === 'MultiLineString') multiFeatureIndex++;
1168 if (geomType === 'Polygon') geometryIndex++;
1169 }
1170 if (geomType === 'Polygon') multiFeatureIndex++;
1171 break;
1172 case 'MultiPolygon':
1173 for (j = 0; j < coords.length; j++) {
1174 geometryIndex = 0;
1175 for (k = 0; k < coords[j].length; k++) {
1176 for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
1177 if (callback(coords[j][k][l], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false;
1178 coordIndex++;
1179 }
1180 geometryIndex++;
1181 }
1182 multiFeatureIndex++;
1183 }
1184 break;
1185 case 'GeometryCollection':
1186 for (j = 0; j < geometry.geometries.length; j++)
1187 if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false) return false;
1188 break;
1189 default:
1190 throw new Error('Unknown Geometry Type');
1191 }
1192 }
1193 }
1194}
1195
1196/**
1197 * Callback for coordReduce
1198 *
1199 * The first time the callback function is called, the values provided as arguments depend
1200 * on whether the reduce method has an initialValue argument.
1201 *
1202 * If an initialValue is provided to the reduce method:
1203 * - The previousValue argument is initialValue.
1204 * - The currentValue argument is the value of the first element present in the array.
1205 *
1206 * If an initialValue is not provided:
1207 * - The previousValue argument is the value of the first element present in the array.
1208 * - The currentValue argument is the value of the second element present in the array.
1209 *
1210 * @callback coordReduceCallback
1211 * @param {*} previousValue The accumulated value previously returned in the last invocation
1212 * of the callback, or initialValue, if supplied.
1213 * @param {Array<number>} currentCoord The current coordinate being processed.
1214 * @param {number} coordIndex The current index of the coordinate being processed.
1215 * Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
1216 * @param {number} featureIndex The current index of the Feature being processed.
1217 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1218 * @param {number} geometryIndex The current index of the Geometry being processed.
1219 */
1220
1221/**
1222 * Reduce coordinates in any GeoJSON object, similar to Array.reduce()
1223 *
1224 * @name coordReduce
1225 * @param {FeatureCollection|Geometry|Feature} geojson any GeoJSON object
1226 * @param {Function} callback a method that takes (previousValue, currentCoord, coordIndex)
1227 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1228 * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration.
1229 * @returns {*} The value that results from the reduction.
1230 * @example
1231 * var features = turf.featureCollection([
1232 * turf.point([26, 37], {"foo": "bar"}),
1233 * turf.point([36, 53], {"hello": "world"})
1234 * ]);
1235 *
1236 * turf.coordReduce(features, function (previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) {
1237 * //=previousValue
1238 * //=currentCoord
1239 * //=coordIndex
1240 * //=featureIndex
1241 * //=multiFeatureIndex
1242 * //=geometryIndex
1243 * return currentCoord;
1244 * });
1245 */
1246function coordReduce(geojson, callback, initialValue, excludeWrapCoord) {
1247 var previousValue = initialValue;
1248 coordEach(geojson, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) {
1249 if (coordIndex === 0 && initialValue === undefined) previousValue = currentCoord;
1250 else previousValue = callback(previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex);
1251 }, excludeWrapCoord);
1252 return previousValue;
1253}
1254
1255/**
1256 * Callback for propEach
1257 *
1258 * @callback propEachCallback
1259 * @param {Object} currentProperties The current Properties being processed.
1260 * @param {number} featureIndex The current index of the Feature being processed.
1261 */
1262
1263/**
1264 * Iterate over properties in any GeoJSON object, similar to Array.forEach()
1265 *
1266 * @name propEach
1267 * @param {FeatureCollection|Feature} geojson any GeoJSON object
1268 * @param {Function} callback a method that takes (currentProperties, featureIndex)
1269 * @returns {void}
1270 * @example
1271 * var features = turf.featureCollection([
1272 * turf.point([26, 37], {foo: 'bar'}),
1273 * turf.point([36, 53], {hello: 'world'})
1274 * ]);
1275 *
1276 * turf.propEach(features, function (currentProperties, featureIndex) {
1277 * //=currentProperties
1278 * //=featureIndex
1279 * });
1280 */
1281function propEach(geojson, callback) {
1282 var i;
1283 switch (geojson.type) {
1284 case 'FeatureCollection':
1285 for (i = 0; i < geojson.features.length; i++) {
1286 if (callback(geojson.features[i].properties, i) === false) break;
1287 }
1288 break;
1289 case 'Feature':
1290 callback(geojson.properties, 0);
1291 break;
1292 }
1293}
1294
1295
1296/**
1297 * Callback for propReduce
1298 *
1299 * The first time the callback function is called, the values provided as arguments depend
1300 * on whether the reduce method has an initialValue argument.
1301 *
1302 * If an initialValue is provided to the reduce method:
1303 * - The previousValue argument is initialValue.
1304 * - The currentValue argument is the value of the first element present in the array.
1305 *
1306 * If an initialValue is not provided:
1307 * - The previousValue argument is the value of the first element present in the array.
1308 * - The currentValue argument is the value of the second element present in the array.
1309 *
1310 * @callback propReduceCallback
1311 * @param {*} previousValue The accumulated value previously returned in the last invocation
1312 * of the callback, or initialValue, if supplied.
1313 * @param {*} currentProperties The current Properties being processed.
1314 * @param {number} featureIndex The current index of the Feature being processed.
1315 */
1316
1317/**
1318 * Reduce properties in any GeoJSON object into a single value,
1319 * similar to how Array.reduce works. However, in this case we lazily run
1320 * the reduction, so an array of all properties is unnecessary.
1321 *
1322 * @name propReduce
1323 * @param {FeatureCollection|Feature} geojson any GeoJSON object
1324 * @param {Function} callback a method that takes (previousValue, currentProperties, featureIndex)
1325 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1326 * @returns {*} The value that results from the reduction.
1327 * @example
1328 * var features = turf.featureCollection([
1329 * turf.point([26, 37], {foo: 'bar'}),
1330 * turf.point([36, 53], {hello: 'world'})
1331 * ]);
1332 *
1333 * turf.propReduce(features, function (previousValue, currentProperties, featureIndex) {
1334 * //=previousValue
1335 * //=currentProperties
1336 * //=featureIndex
1337 * return currentProperties
1338 * });
1339 */
1340function propReduce(geojson, callback, initialValue) {
1341 var previousValue = initialValue;
1342 propEach(geojson, function (currentProperties, featureIndex) {
1343 if (featureIndex === 0 && initialValue === undefined) previousValue = currentProperties;
1344 else previousValue = callback(previousValue, currentProperties, featureIndex);
1345 });
1346 return previousValue;
1347}
1348
1349/**
1350 * Callback for featureEach
1351 *
1352 * @callback featureEachCallback
1353 * @param {Feature<any>} currentFeature The current Feature being processed.
1354 * @param {number} featureIndex The current index of the Feature being processed.
1355 */
1356
1357/**
1358 * Iterate over features in any GeoJSON object, similar to
1359 * Array.forEach.
1360 *
1361 * @name featureEach
1362 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1363 * @param {Function} callback a method that takes (currentFeature, featureIndex)
1364 * @returns {void}
1365 * @example
1366 * var features = turf.featureCollection([
1367 * turf.point([26, 37], {foo: 'bar'}),
1368 * turf.point([36, 53], {hello: 'world'})
1369 * ]);
1370 *
1371 * turf.featureEach(features, function (currentFeature, featureIndex) {
1372 * //=currentFeature
1373 * //=featureIndex
1374 * });
1375 */
1376function featureEach(geojson, callback) {
1377 if (geojson.type === 'Feature') {
1378 callback(geojson, 0);
1379 } else if (geojson.type === 'FeatureCollection') {
1380 for (var i = 0; i < geojson.features.length; i++) {
1381 if (callback(geojson.features[i], i) === false) break;
1382 }
1383 }
1384}
1385
1386/**
1387 * Callback for featureReduce
1388 *
1389 * The first time the callback function is called, the values provided as arguments depend
1390 * on whether the reduce method has an initialValue argument.
1391 *
1392 * If an initialValue is provided to the reduce method:
1393 * - The previousValue argument is initialValue.
1394 * - The currentValue argument is the value of the first element present in the array.
1395 *
1396 * If an initialValue is not provided:
1397 * - The previousValue argument is the value of the first element present in the array.
1398 * - The currentValue argument is the value of the second element present in the array.
1399 *
1400 * @callback featureReduceCallback
1401 * @param {*} previousValue The accumulated value previously returned in the last invocation
1402 * of the callback, or initialValue, if supplied.
1403 * @param {Feature} currentFeature The current Feature being processed.
1404 * @param {number} featureIndex The current index of the Feature being processed.
1405 */
1406
1407/**
1408 * Reduce features in any GeoJSON object, similar to Array.reduce().
1409 *
1410 * @name featureReduce
1411 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1412 * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex)
1413 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1414 * @returns {*} The value that results from the reduction.
1415 * @example
1416 * var features = turf.featureCollection([
1417 * turf.point([26, 37], {"foo": "bar"}),
1418 * turf.point([36, 53], {"hello": "world"})
1419 * ]);
1420 *
1421 * turf.featureReduce(features, function (previousValue, currentFeature, featureIndex) {
1422 * //=previousValue
1423 * //=currentFeature
1424 * //=featureIndex
1425 * return currentFeature
1426 * });
1427 */
1428function featureReduce(geojson, callback, initialValue) {
1429 var previousValue = initialValue;
1430 featureEach(geojson, function (currentFeature, featureIndex) {
1431 if (featureIndex === 0 && initialValue === undefined) previousValue = currentFeature;
1432 else previousValue = callback(previousValue, currentFeature, featureIndex);
1433 });
1434 return previousValue;
1435}
1436
1437/**
1438 * Get all coordinates from any GeoJSON object.
1439 *
1440 * @name coordAll
1441 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1442 * @returns {Array<Array<number>>} coordinate position array
1443 * @example
1444 * var features = turf.featureCollection([
1445 * turf.point([26, 37], {foo: 'bar'}),
1446 * turf.point([36, 53], {hello: 'world'})
1447 * ]);
1448 *
1449 * var coords = turf.coordAll(features);
1450 * //= [[26, 37], [36, 53]]
1451 */
1452function coordAll(geojson) {
1453 var coords = [];
1454 coordEach(geojson, function (coord) {
1455 coords.push(coord);
1456 });
1457 return coords;
1458}
1459
1460/**
1461 * Callback for geomEach
1462 *
1463 * @callback geomEachCallback
1464 * @param {Geometry} currentGeometry The current Geometry being processed.
1465 * @param {number} featureIndex The current index of the Feature being processed.
1466 * @param {Object} featureProperties The current Feature Properties being processed.
1467 * @param {Array<number>} featureBBox The current Feature BBox being processed.
1468 * @param {number|string} featureId The current Feature Id being processed.
1469 */
1470
1471/**
1472 * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
1473 *
1474 * @name geomEach
1475 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1476 * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
1477 * @returns {void}
1478 * @example
1479 * var features = turf.featureCollection([
1480 * turf.point([26, 37], {foo: 'bar'}),
1481 * turf.point([36, 53], {hello: 'world'})
1482 * ]);
1483 *
1484 * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
1485 * //=currentGeometry
1486 * //=featureIndex
1487 * //=featureProperties
1488 * //=featureBBox
1489 * //=featureId
1490 * });
1491 */
1492function geomEach(geojson, callback) {
1493 var i, j, g, geometry, stopG,
1494 geometryMaybeCollection,
1495 isGeometryCollection,
1496 featureProperties,
1497 featureBBox,
1498 featureId,
1499 featureIndex = 0,
1500 isFeatureCollection = geojson.type === 'FeatureCollection',
1501 isFeature = geojson.type === 'Feature',
1502 stop = isFeatureCollection ? geojson.features.length : 1;
1503
1504 // This logic may look a little weird. The reason why it is that way
1505 // is because it's trying to be fast. GeoJSON supports multiple kinds
1506 // of objects at its root: FeatureCollection, Features, Geometries.
1507 // This function has the responsibility of handling all of them, and that
1508 // means that some of the `for` loops you see below actually just don't apply
1509 // to certain inputs. For instance, if you give this just a
1510 // Point geometry, then both loops are short-circuited and all we do
1511 // is gradually rename the input until it's called 'geometry'.
1512 //
1513 // This also aims to allocate as few resources as possible: just a
1514 // few numbers and booleans, rather than any temporary arrays as would
1515 // be required with the normalization approach.
1516 for (i = 0; i < stop; i++) {
1517
1518 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
1519 (isFeature ? geojson.geometry : geojson));
1520 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
1521 (isFeature ? geojson.properties : {}));
1522 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
1523 (isFeature ? geojson.bbox : undefined));
1524 featureId = (isFeatureCollection ? geojson.features[i].id :
1525 (isFeature ? geojson.id : undefined));
1526 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
1527 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
1528
1529 for (g = 0; g < stopG; g++) {
1530 geometry = isGeometryCollection ?
1531 geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
1532
1533 // Handle null Geometry
1534 if (geometry === null) {
1535 if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
1536 continue;
1537 }
1538 switch (geometry.type) {
1539 case 'Point':
1540 case 'LineString':
1541 case 'MultiPoint':
1542 case 'Polygon':
1543 case 'MultiLineString':
1544 case 'MultiPolygon': {
1545 if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
1546 break;
1547 }
1548 case 'GeometryCollection': {
1549 for (j = 0; j < geometry.geometries.length; j++) {
1550 if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) return false;
1551 }
1552 break;
1553 }
1554 default:
1555 throw new Error('Unknown Geometry Type');
1556 }
1557 }
1558 // Only increase `featureIndex` per each feature
1559 featureIndex++;
1560 }
1561}
1562
1563/**
1564 * Callback for geomReduce
1565 *
1566 * The first time the callback function is called, the values provided as arguments depend
1567 * on whether the reduce method has an initialValue argument.
1568 *
1569 * If an initialValue is provided to the reduce method:
1570 * - The previousValue argument is initialValue.
1571 * - The currentValue argument is the value of the first element present in the array.
1572 *
1573 * If an initialValue is not provided:
1574 * - The previousValue argument is the value of the first element present in the array.
1575 * - The currentValue argument is the value of the second element present in the array.
1576 *
1577 * @callback geomReduceCallback
1578 * @param {*} previousValue The accumulated value previously returned in the last invocation
1579 * of the callback, or initialValue, if supplied.
1580 * @param {Geometry} currentGeometry The current Geometry being processed.
1581 * @param {number} featureIndex The current index of the Feature being processed.
1582 * @param {Object} featureProperties The current Feature Properties being processed.
1583 * @param {Array<number>} featureBBox The current Feature BBox being processed.
1584 * @param {number|string} featureId The current Feature Id being processed.
1585 */
1586
1587/**
1588 * Reduce geometry in any GeoJSON object, similar to Array.reduce().
1589 *
1590 * @name geomReduce
1591 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1592 * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
1593 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1594 * @returns {*} The value that results from the reduction.
1595 * @example
1596 * var features = turf.featureCollection([
1597 * turf.point([26, 37], {foo: 'bar'}),
1598 * turf.point([36, 53], {hello: 'world'})
1599 * ]);
1600 *
1601 * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
1602 * //=previousValue
1603 * //=currentGeometry
1604 * //=featureIndex
1605 * //=featureProperties
1606 * //=featureBBox
1607 * //=featureId
1608 * return currentGeometry
1609 * });
1610 */
1611function geomReduce(geojson, callback, initialValue) {
1612 var previousValue = initialValue;
1613 geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
1614 if (featureIndex === 0 && initialValue === undefined) previousValue = currentGeometry;
1615 else previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId);
1616 });
1617 return previousValue;
1618}
1619
1620/**
1621 * Callback for flattenEach
1622 *
1623 * @callback flattenEachCallback
1624 * @param {Feature} currentFeature The current flattened feature being processed.
1625 * @param {number} featureIndex The current index of the Feature being processed.
1626 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1627 */
1628
1629/**
1630 * Iterate over flattened features in any GeoJSON object, similar to
1631 * Array.forEach.
1632 *
1633 * @name flattenEach
1634 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1635 * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
1636 * @example
1637 * var features = turf.featureCollection([
1638 * turf.point([26, 37], {foo: 'bar'}),
1639 * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
1640 * ]);
1641 *
1642 * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
1643 * //=currentFeature
1644 * //=featureIndex
1645 * //=multiFeatureIndex
1646 * });
1647 */
1648function flattenEach(geojson, callback) {
1649 geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
1650 // Callback for single geometry
1651 var type = (geometry === null) ? null : geometry.type;
1652 switch (type) {
1653 case null:
1654 case 'Point':
1655 case 'LineString':
1656 case 'Polygon':
1657 if (callback(helpers.feature(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) return false;
1658 return;
1659 }
1660
1661 var geomType;
1662
1663 // Callback for multi-geometry
1664 switch (type) {
1665 case 'MultiPoint':
1666 geomType = 'Point';
1667 break;
1668 case 'MultiLineString':
1669 geomType = 'LineString';
1670 break;
1671 case 'MultiPolygon':
1672 geomType = 'Polygon';
1673 break;
1674 }
1675
1676 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
1677 var coordinate = geometry.coordinates[multiFeatureIndex];
1678 var geom = {
1679 type: geomType,
1680 coordinates: coordinate
1681 };
1682 if (callback(helpers.feature(geom, properties), featureIndex, multiFeatureIndex) === false) return false;
1683 }
1684 });
1685}
1686
1687/**
1688 * Callback for flattenReduce
1689 *
1690 * The first time the callback function is called, the values provided as arguments depend
1691 * on whether the reduce method has an initialValue argument.
1692 *
1693 * If an initialValue is provided to the reduce method:
1694 * - The previousValue argument is initialValue.
1695 * - The currentValue argument is the value of the first element present in the array.
1696 *
1697 * If an initialValue is not provided:
1698 * - The previousValue argument is the value of the first element present in the array.
1699 * - The currentValue argument is the value of the second element present in the array.
1700 *
1701 * @callback flattenReduceCallback
1702 * @param {*} previousValue The accumulated value previously returned in the last invocation
1703 * of the callback, or initialValue, if supplied.
1704 * @param {Feature} currentFeature The current Feature being processed.
1705 * @param {number} featureIndex The current index of the Feature being processed.
1706 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1707 */
1708
1709/**
1710 * Reduce flattened features in any GeoJSON object, similar to Array.reduce().
1711 *
1712 * @name flattenReduce
1713 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
1714 * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex, multiFeatureIndex)
1715 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1716 * @returns {*} The value that results from the reduction.
1717 * @example
1718 * var features = turf.featureCollection([
1719 * turf.point([26, 37], {foo: 'bar'}),
1720 * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
1721 * ]);
1722 *
1723 * turf.flattenReduce(features, function (previousValue, currentFeature, featureIndex, multiFeatureIndex) {
1724 * //=previousValue
1725 * //=currentFeature
1726 * //=featureIndex
1727 * //=multiFeatureIndex
1728 * return currentFeature
1729 * });
1730 */
1731function flattenReduce(geojson, callback, initialValue) {
1732 var previousValue = initialValue;
1733 flattenEach(geojson, function (currentFeature, featureIndex, multiFeatureIndex) {
1734 if (featureIndex === 0 && multiFeatureIndex === 0 && initialValue === undefined) previousValue = currentFeature;
1735 else previousValue = callback(previousValue, currentFeature, featureIndex, multiFeatureIndex);
1736 });
1737 return previousValue;
1738}
1739
1740/**
1741 * Callback for segmentEach
1742 *
1743 * @callback segmentEachCallback
1744 * @param {Feature<LineString>} currentSegment The current Segment being processed.
1745 * @param {number} featureIndex The current index of the Feature being processed.
1746 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1747 * @param {number} geometryIndex The current index of the Geometry being processed.
1748 * @param {number} segmentIndex The current index of the Segment being processed.
1749 * @returns {void}
1750 */
1751
1752/**
1753 * Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach()
1754 * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
1755 *
1756 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
1757 * @param {Function} callback a method that takes (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex)
1758 * @returns {void}
1759 * @example
1760 * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
1761 *
1762 * // Iterate over GeoJSON by 2-vertex segments
1763 * turf.segmentEach(polygon, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
1764 * //=currentSegment
1765 * //=featureIndex
1766 * //=multiFeatureIndex
1767 * //=geometryIndex
1768 * //=segmentIndex
1769 * });
1770 *
1771 * // Calculate the total number of segments
1772 * var total = 0;
1773 * turf.segmentEach(polygon, function () {
1774 * total++;
1775 * });
1776 */
1777function segmentEach(geojson, callback) {
1778 flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
1779 var segmentIndex = 0;
1780
1781 // Exclude null Geometries
1782 if (!feature.geometry) return;
1783 // (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
1784 var type = feature.geometry.type;
1785 if (type === 'Point' || type === 'MultiPoint') return;
1786
1787 // Generate 2-vertex line segments
1788 var previousCoords;
1789 var previousFeatureIndex = 0;
1790 var previousMultiIndex = 0;
1791 var prevGeomIndex = 0;
1792 if (coordEach(feature, function (currentCoord, coordIndex, featureIndexCoord, multiPartIndexCoord, geometryIndex) {
1793 // Simulating a meta.coordReduce() since `reduce` operations cannot be stopped by returning `false`
1794 if (previousCoords === undefined || featureIndex > previousFeatureIndex || multiPartIndexCoord > previousMultiIndex || geometryIndex > prevGeomIndex) {
1795 previousCoords = currentCoord;
1796 previousFeatureIndex = featureIndex;
1797 previousMultiIndex = multiPartIndexCoord;
1798 prevGeomIndex = geometryIndex;
1799 segmentIndex = 0;
1800 return;
1801 }
1802 var currentSegment = helpers.lineString([previousCoords, currentCoord], feature.properties);
1803 if (callback(currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) === false) return false;
1804 segmentIndex++;
1805 previousCoords = currentCoord;
1806 }) === false) return false;
1807 });
1808}
1809
1810/**
1811 * Callback for segmentReduce
1812 *
1813 * The first time the callback function is called, the values provided as arguments depend
1814 * on whether the reduce method has an initialValue argument.
1815 *
1816 * If an initialValue is provided to the reduce method:
1817 * - The previousValue argument is initialValue.
1818 * - The currentValue argument is the value of the first element present in the array.
1819 *
1820 * If an initialValue is not provided:
1821 * - The previousValue argument is the value of the first element present in the array.
1822 * - The currentValue argument is the value of the second element present in the array.
1823 *
1824 * @callback segmentReduceCallback
1825 * @param {*} previousValue The accumulated value previously returned in the last invocation
1826 * of the callback, or initialValue, if supplied.
1827 * @param {Feature<LineString>} currentSegment The current Segment being processed.
1828 * @param {number} featureIndex The current index of the Feature being processed.
1829 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
1830 * @param {number} geometryIndex The current index of the Geometry being processed.
1831 * @param {number} segmentIndex The current index of the Segment being processed.
1832 */
1833
1834/**
1835 * Reduce 2-vertex line segment in any GeoJSON object, similar to Array.reduce()
1836 * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
1837 *
1838 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
1839 * @param {Function} callback a method that takes (previousValue, currentSegment, currentIndex)
1840 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1841 * @returns {void}
1842 * @example
1843 * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
1844 *
1845 * // Iterate over GeoJSON by 2-vertex segments
1846 * turf.segmentReduce(polygon, function (previousSegment, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
1847 * //= previousSegment
1848 * //= currentSegment
1849 * //= featureIndex
1850 * //= multiFeatureIndex
1851 * //= geometryIndex
1852 * //= segmentInex
1853 * return currentSegment
1854 * });
1855 *
1856 * // Calculate the total number of segments
1857 * var initialValue = 0
1858 * var total = turf.segmentReduce(polygon, function (previousValue) {
1859 * previousValue++;
1860 * return previousValue;
1861 * }, initialValue);
1862 */
1863function segmentReduce(geojson, callback, initialValue) {
1864 var previousValue = initialValue;
1865 var started = false;
1866 segmentEach(geojson, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
1867 if (started === false && initialValue === undefined) previousValue = currentSegment;
1868 else previousValue = callback(previousValue, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex);
1869 started = true;
1870 });
1871 return previousValue;
1872}
1873
1874/**
1875 * Callback for lineEach
1876 *
1877 * @callback lineEachCallback
1878 * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed
1879 * @param {number} featureIndex The current index of the Feature being processed
1880 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed
1881 * @param {number} geometryIndex The current index of the Geometry being processed
1882 */
1883
1884/**
1885 * Iterate over line or ring coordinates in LineString, Polygon, MultiLineString, MultiPolygon Features or Geometries,
1886 * similar to Array.forEach.
1887 *
1888 * @name lineEach
1889 * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object
1890 * @param {Function} callback a method that takes (currentLine, featureIndex, multiFeatureIndex, geometryIndex)
1891 * @example
1892 * var multiLine = turf.multiLineString([
1893 * [[26, 37], [35, 45]],
1894 * [[36, 53], [38, 50], [41, 55]]
1895 * ]);
1896 *
1897 * turf.lineEach(multiLine, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
1898 * //=currentLine
1899 * //=featureIndex
1900 * //=multiFeatureIndex
1901 * //=geometryIndex
1902 * });
1903 */
1904function lineEach(geojson, callback) {
1905 // validation
1906 if (!geojson) throw new Error('geojson is required');
1907
1908 flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
1909 if (feature.geometry === null) return;
1910 var type = feature.geometry.type;
1911 var coords = feature.geometry.coordinates;
1912 switch (type) {
1913 case 'LineString':
1914 if (callback(feature, featureIndex, multiFeatureIndex, 0, 0) === false) return false;
1915 break;
1916 case 'Polygon':
1917 for (var geometryIndex = 0; geometryIndex < coords.length; geometryIndex++) {
1918 if (callback(helpers.lineString(coords[geometryIndex], feature.properties), featureIndex, multiFeatureIndex, geometryIndex) === false) return false;
1919 }
1920 break;
1921 }
1922 });
1923}
1924
1925/**
1926 * Callback for lineReduce
1927 *
1928 * The first time the callback function is called, the values provided as arguments depend
1929 * on whether the reduce method has an initialValue argument.
1930 *
1931 * If an initialValue is provided to the reduce method:
1932 * - The previousValue argument is initialValue.
1933 * - The currentValue argument is the value of the first element present in the array.
1934 *
1935 * If an initialValue is not provided:
1936 * - The previousValue argument is the value of the first element present in the array.
1937 * - The currentValue argument is the value of the second element present in the array.
1938 *
1939 * @callback lineReduceCallback
1940 * @param {*} previousValue The accumulated value previously returned in the last invocation
1941 * of the callback, or initialValue, if supplied.
1942 * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed.
1943 * @param {number} featureIndex The current index of the Feature being processed
1944 * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed
1945 * @param {number} geometryIndex The current index of the Geometry being processed
1946 */
1947
1948/**
1949 * Reduce features in any GeoJSON object, similar to Array.reduce().
1950 *
1951 * @name lineReduce
1952 * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object
1953 * @param {Function} callback a method that takes (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex)
1954 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
1955 * @returns {*} The value that results from the reduction.
1956 * @example
1957 * var multiPoly = turf.multiPolygon([
1958 * turf.polygon([[[12,48],[2,41],[24,38],[12,48]], [[9,44],[13,41],[13,45],[9,44]]]),
1959 * turf.polygon([[[5, 5], [0, 0], [2, 2], [4, 4], [5, 5]]])
1960 * ]);
1961 *
1962 * turf.lineReduce(multiPoly, function (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
1963 * //=previousValue
1964 * //=currentLine
1965 * //=featureIndex
1966 * //=multiFeatureIndex
1967 * //=geometryIndex
1968 * return currentLine
1969 * });
1970 */
1971function lineReduce(geojson, callback, initialValue) {
1972 var previousValue = initialValue;
1973 lineEach(geojson, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
1974 if (featureIndex === 0 && initialValue === undefined) previousValue = currentLine;
1975 else previousValue = callback(previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex);
1976 });
1977 return previousValue;
1978}
1979
1980/**
1981 * Finds a particular 2-vertex LineString Segment from a GeoJSON using `@turf/meta` indexes.
1982 *
1983 * Negative indexes are permitted.
1984 * Point & MultiPoint will always return null.
1985 *
1986 * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry
1987 * @param {Object} [options={}] Optional parameters
1988 * @param {number} [options.featureIndex=0] Feature Index
1989 * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index
1990 * @param {number} [options.geometryIndex=0] Geometry Index
1991 * @param {number} [options.segmentIndex=0] Segment Index
1992 * @param {Object} [options.properties={}] Translate Properties to output LineString
1993 * @param {BBox} [options.bbox={}] Translate BBox to output LineString
1994 * @param {number|string} [options.id={}] Translate Id to output LineString
1995 * @returns {Feature<LineString>} 2-vertex GeoJSON Feature LineString
1996 * @example
1997 * var multiLine = turf.multiLineString([
1998 * [[10, 10], [50, 30], [30, 40]],
1999 * [[-10, -10], [-50, -30], [-30, -40]]
2000 * ]);
2001 *
2002 * // First Segment (defaults are 0)
2003 * turf.findSegment(multiLine);
2004 * // => Feature<LineString<[[10, 10], [50, 30]]>>
2005 *
2006 * // First Segment of 2nd Multi Feature
2007 * turf.findSegment(multiLine, {multiFeatureIndex: 1});
2008 * // => Feature<LineString<[[-10, -10], [-50, -30]]>>
2009 *
2010 * // Last Segment of Last Multi Feature
2011 * turf.findSegment(multiLine, {multiFeatureIndex: -1, segmentIndex: -1});
2012 * // => Feature<LineString<[[-50, -30], [-30, -40]]>>
2013 */
2014function findSegment(geojson, options) {
2015 // Optional Parameters
2016 options = options || {};
2017 if (!helpers.isObject(options)) throw new Error('options is invalid');
2018 var featureIndex = options.featureIndex || 0;
2019 var multiFeatureIndex = options.multiFeatureIndex || 0;
2020 var geometryIndex = options.geometryIndex || 0;
2021 var segmentIndex = options.segmentIndex || 0;
2022
2023 // Find FeatureIndex
2024 var properties = options.properties;
2025 var geometry;
2026
2027 switch (geojson.type) {
2028 case 'FeatureCollection':
2029 if (featureIndex < 0) featureIndex = geojson.features.length + featureIndex;
2030 properties = properties || geojson.features[featureIndex].properties;
2031 geometry = geojson.features[featureIndex].geometry;
2032 break;
2033 case 'Feature':
2034 properties = properties || geojson.properties;
2035 geometry = geojson.geometry;
2036 break;
2037 case 'Point':
2038 case 'MultiPoint':
2039 return null;
2040 case 'LineString':
2041 case 'Polygon':
2042 case 'MultiLineString':
2043 case 'MultiPolygon':
2044 geometry = geojson;
2045 break;
2046 default:
2047 throw new Error('geojson is invalid');
2048 }
2049
2050 // Find SegmentIndex
2051 if (geometry === null) return null;
2052 var coords = geometry.coordinates;
2053 switch (geometry.type) {
2054 case 'Point':
2055 case 'MultiPoint':
2056 return null;
2057 case 'LineString':
2058 if (segmentIndex < 0) segmentIndex = coords.length + segmentIndex - 1;
2059 return helpers.lineString([coords[segmentIndex], coords[segmentIndex + 1]], properties, options);
2060 case 'Polygon':
2061 if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
2062 if (segmentIndex < 0) segmentIndex = coords[geometryIndex].length + segmentIndex - 1;
2063 return helpers.lineString([coords[geometryIndex][segmentIndex], coords[geometryIndex][segmentIndex + 1]], properties, options);
2064 case 'MultiLineString':
2065 if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex;
2066 if (segmentIndex < 0) segmentIndex = coords[multiFeatureIndex].length + segmentIndex - 1;
2067 return helpers.lineString([coords[multiFeatureIndex][segmentIndex], coords[multiFeatureIndex][segmentIndex + 1]], properties, options);
2068 case 'MultiPolygon':
2069 if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex;
2070 if (geometryIndex < 0) geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
2071 if (segmentIndex < 0) segmentIndex = coords[multiFeatureIndex][geometryIndex].length - segmentIndex - 1;
2072 return helpers.lineString([coords[multiFeatureIndex][geometryIndex][segmentIndex], coords[multiFeatureIndex][geometryIndex][segmentIndex + 1]], properties, options);
2073 }
2074 throw new Error('geojson is invalid');
2075}
2076
2077/**
2078 * Finds a particular Point from a GeoJSON using `@turf/meta` indexes.
2079 *
2080 * Negative indexes are permitted.
2081 *
2082 * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry
2083 * @param {Object} [options={}] Optional parameters
2084 * @param {number} [options.featureIndex=0] Feature Index
2085 * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index
2086 * @param {number} [options.geometryIndex=0] Geometry Index
2087 * @param {number} [options.coordIndex=0] Coord Index
2088 * @param {Object} [options.properties={}] Translate Properties to output Point
2089 * @param {BBox} [options.bbox={}] Translate BBox to output Point
2090 * @param {number|string} [options.id={}] Translate Id to output Point
2091 * @returns {Feature<Point>} 2-vertex GeoJSON Feature Point
2092 * @example
2093 * var multiLine = turf.multiLineString([
2094 * [[10, 10], [50, 30], [30, 40]],
2095 * [[-10, -10], [-50, -30], [-30, -40]]
2096 * ]);
2097 *
2098 * // First Segment (defaults are 0)
2099 * turf.findPoint(multiLine);
2100 * // => Feature<Point<[10, 10]>>
2101 *
2102 * // First Segment of the 2nd Multi-Feature
2103 * turf.findPoint(multiLine, {multiFeatureIndex: 1});
2104 * // => Feature<Point<[-10, -10]>>
2105 *
2106 * // Last Segment of last Multi-Feature
2107 * turf.findPoint(multiLine, {multiFeatureIndex: -1, coordIndex: -1});
2108 * // => Feature<Point<[-30, -40]>>
2109 */
2110function findPoint(geojson, options) {
2111 // Optional Parameters
2112 options = options || {};
2113 if (!helpers.isObject(options)) throw new Error('options is invalid');
2114 var featureIndex = options.featureIndex || 0;
2115 var multiFeatureIndex = options.multiFeatureIndex || 0;
2116 var geometryIndex = options.geometryIndex || 0;
2117 var coordIndex = options.coordIndex || 0;
2118
2119 // Find FeatureIndex
2120 var properties = options.properties;
2121 var geometry;
2122
2123 switch (geojson.type) {
2124 case 'FeatureCollection':
2125 if (featureIndex < 0) featureIndex = geojson.features.length + featureIndex;
2126 properties = properties || geojson.features[featureIndex].properties;
2127 geometry = geojson.features[featureIndex].geometry;
2128 break;
2129 case 'Feature':
2130 properties = properties || geojson.properties;
2131 geometry = geojson.geometry;
2132 break;
2133 case 'Point':
2134 case 'MultiPoint':
2135 return null;
2136 case 'LineString':
2137 case 'Polygon':
2138 case 'MultiLineString':
2139 case 'MultiPolygon':
2140 geometry = geojson;
2141 break;
2142 default:
2143 throw new Error('geojson is invalid');
2144 }
2145
2146 // Find Coord Index
2147 if (geometry === null) return null;
2148 var coords = geometry.coordinates;
2149 switch (geometry.type) {
2150 case 'Point':
2151 return helpers.point(coords, properties, options);
2152 case 'MultiPoint':
2153 if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex;
2154 return helpers.point(coords[multiFeatureIndex], properties, options);
2155 case 'LineString':
2156 if (coordIndex < 0) coordIndex = coords.length + coordIndex;
2157 return helpers.point(coords[coordIndex], properties, options);
2158 case 'Polygon':
2159 if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
2160 if (coordIndex < 0) coordIndex = coords[geometryIndex].length + coordIndex;
2161 return helpers.point(coords[geometryIndex][coordIndex], properties, options);
2162 case 'MultiLineString':
2163 if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex;
2164 if (coordIndex < 0) coordIndex = coords[multiFeatureIndex].length + coordIndex;
2165 return helpers.point(coords[multiFeatureIndex][coordIndex], properties, options);
2166 case 'MultiPolygon':
2167 if (multiFeatureIndex < 0) multiFeatureIndex = coords.length + multiFeatureIndex;
2168 if (geometryIndex < 0) geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
2169 if (coordIndex < 0) coordIndex = coords[multiFeatureIndex][geometryIndex].length - coordIndex;
2170 return helpers.point(coords[multiFeatureIndex][geometryIndex][coordIndex], properties, options);
2171 }
2172 throw new Error('geojson is invalid');
2173}
2174
2175exports.coordEach = coordEach;
2176exports.coordReduce = coordReduce;
2177exports.propEach = propEach;
2178exports.propReduce = propReduce;
2179exports.featureEach = featureEach;
2180exports.featureReduce = featureReduce;
2181exports.coordAll = coordAll;
2182exports.geomEach = geomEach;
2183exports.geomReduce = geomReduce;
2184exports.flattenEach = flattenEach;
2185exports.flattenReduce = flattenReduce;
2186exports.segmentEach = segmentEach;
2187exports.segmentReduce = segmentReduce;
2188exports.lineEach = lineEach;
2189exports.lineReduce = lineReduce;
2190exports.findSegment = findSegment;
2191exports.findPoint = findPoint;
2192
2193},{"@turf/helpers":9}],11:[function(_dereq_,module,exports){
2194// Copyright Joyent, Inc. and other Node contributors.
2195//
2196// Permission is hereby granted, free of charge, to any person obtaining a
2197// copy of this software and associated documentation files (the
2198// "Software"), to deal in the Software without restriction, including
2199// without limitation the rights to use, copy, modify, merge, publish,
2200// distribute, sublicense, and/or sell copies of the Software, and to permit
2201// persons to whom the Software is furnished to do so, subject to the
2202// following conditions:
2203//
2204// The above copyright notice and this permission notice shall be included
2205// in all copies or substantial portions of the Software.
2206//
2207// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
2208// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2209// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
2210// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
2211// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2212// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2213// USE OR OTHER DEALINGS IN THE SOFTWARE.
2214
2215var objectCreate = Object.create || objectCreatePolyfill
2216var objectKeys = Object.keys || objectKeysPolyfill
2217var bind = Function.prototype.bind || functionBindPolyfill
2218
2219function EventEmitter() {
2220 if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
2221 this._events = objectCreate(null);
2222 this._eventsCount = 0;
2223 }
2224
2225 this._maxListeners = this._maxListeners || undefined;
2226}
2227module.exports = EventEmitter;
2228
2229// Backwards-compat with node 0.10.x
2230EventEmitter.EventEmitter = EventEmitter;
2231
2232EventEmitter.prototype._events = undefined;
2233EventEmitter.prototype._maxListeners = undefined;
2234
2235// By default EventEmitters will print a warning if more than 10 listeners are
2236// added to it. This is a useful default which helps finding memory leaks.
2237var defaultMaxListeners = 10;
2238
2239var hasDefineProperty;
2240try {
2241 var o = {};
2242 if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
2243 hasDefineProperty = o.x === 0;
2244} catch (err) { hasDefineProperty = false }
2245if (hasDefineProperty) {
2246 Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
2247 enumerable: true,
2248 get: function() {
2249 return defaultMaxListeners;
2250 },
2251 set: function(arg) {
2252 // check whether the input is a positive number (whose value is zero or
2253 // greater and not a NaN).
2254 if (typeof arg !== 'number' || arg < 0 || arg !== arg)
2255 throw new TypeError('"defaultMaxListeners" must be a positive number');
2256 defaultMaxListeners = arg;
2257 }
2258 });
2259} else {
2260 EventEmitter.defaultMaxListeners = defaultMaxListeners;
2261}
2262
2263// Obviously not all Emitters should be limited to 10. This function allows
2264// that to be increased. Set to zero for unlimited.
2265EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
2266 if (typeof n !== 'number' || n < 0 || isNaN(n))
2267 throw new TypeError('"n" argument must be a positive number');
2268 this._maxListeners = n;
2269 return this;
2270};
2271
2272function $getMaxListeners(that) {
2273 if (that._maxListeners === undefined)
2274 return EventEmitter.defaultMaxListeners;
2275 return that._maxListeners;
2276}
2277
2278EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
2279 return $getMaxListeners(this);
2280};
2281
2282// These standalone emit* functions are used to optimize calling of event
2283// handlers for fast cases because emit() itself often has a variable number of
2284// arguments and can be deoptimized because of that. These functions always have
2285// the same number of arguments and thus do not get deoptimized, so the code
2286// inside them can execute faster.
2287function emitNone(handler, isFn, self) {
2288 if (isFn)
2289 handler.call(self);
2290 else {
2291 var len = handler.length;
2292 var listeners = arrayClone(handler, len);
2293 for (var i = 0; i < len; ++i)
2294 listeners[i].call(self);
2295 }
2296}
2297function emitOne(handler, isFn, self, arg1) {
2298 if (isFn)
2299 handler.call(self, arg1);
2300 else {
2301 var len = handler.length;
2302 var listeners = arrayClone(handler, len);
2303 for (var i = 0; i < len; ++i)
2304 listeners[i].call(self, arg1);
2305 }
2306}
2307function emitTwo(handler, isFn, self, arg1, arg2) {
2308 if (isFn)
2309 handler.call(self, arg1, arg2);
2310 else {
2311 var len = handler.length;
2312 var listeners = arrayClone(handler, len);
2313 for (var i = 0; i < len; ++i)
2314 listeners[i].call(self, arg1, arg2);
2315 }
2316}
2317function emitThree(handler, isFn, self, arg1, arg2, arg3) {
2318 if (isFn)
2319 handler.call(self, arg1, arg2, arg3);
2320 else {
2321 var len = handler.length;
2322 var listeners = arrayClone(handler, len);
2323 for (var i = 0; i < len; ++i)
2324 listeners[i].call(self, arg1, arg2, arg3);
2325 }
2326}
2327
2328function emitMany(handler, isFn, self, args) {
2329 if (isFn)
2330 handler.apply(self, args);
2331 else {
2332 var len = handler.length;
2333 var listeners = arrayClone(handler, len);
2334 for (var i = 0; i < len; ++i)
2335 listeners[i].apply(self, args);
2336 }
2337}
2338
2339EventEmitter.prototype.emit = function emit(type) {
2340 var er, handler, len, args, i, events;
2341 var doError = (type === 'error');
2342
2343 events = this._events;
2344 if (events)
2345 doError = (doError && events.error == null);
2346 else if (!doError)
2347 return false;
2348
2349 // If there is no 'error' event listener then throw.
2350 if (doError) {
2351 if (arguments.length > 1)
2352 er = arguments[1];
2353 if (er instanceof Error) {
2354 throw er; // Unhandled 'error' event
2355 } else {
2356 // At least give some kind of context to the user
2357 var err = new Error('Unhandled "error" event. (' + er + ')');
2358 err.context = er;
2359 throw err;
2360 }
2361 return false;
2362 }
2363
2364 handler = events[type];
2365
2366 if (!handler)
2367 return false;
2368
2369 var isFn = typeof handler === 'function';
2370 len = arguments.length;
2371 switch (len) {
2372 // fast cases
2373 case 1:
2374 emitNone(handler, isFn, this);
2375 break;
2376 case 2:
2377 emitOne(handler, isFn, this, arguments[1]);
2378 break;
2379 case 3:
2380 emitTwo(handler, isFn, this, arguments[1], arguments[2]);
2381 break;
2382 case 4:
2383 emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
2384 break;
2385 // slower
2386 default:
2387 args = new Array(len - 1);
2388 for (i = 1; i < len; i++)
2389 args[i - 1] = arguments[i];
2390 emitMany(handler, isFn, this, args);
2391 }
2392
2393 return true;
2394};
2395
2396function _addListener(target, type, listener, prepend) {
2397 var m;
2398 var events;
2399 var existing;
2400
2401 if (typeof listener !== 'function')
2402 throw new TypeError('"listener" argument must be a function');
2403
2404 events = target._events;
2405 if (!events) {
2406 events = target._events = objectCreate(null);
2407 target._eventsCount = 0;
2408 } else {
2409 // To avoid recursion in the case that type === "newListener"! Before
2410 // adding it to the listeners, first emit "newListener".
2411 if (events.newListener) {
2412 target.emit('newListener', type,
2413 listener.listener ? listener.listener : listener);
2414
2415 // Re-assign `events` because a newListener handler could have caused the
2416 // this._events to be assigned to a new object
2417 events = target._events;
2418 }
2419 existing = events[type];
2420 }
2421
2422 if (!existing) {
2423 // Optimize the case of one listener. Don't need the extra array object.
2424 existing = events[type] = listener;
2425 ++target._eventsCount;
2426 } else {
2427 if (typeof existing === 'function') {
2428 // Adding the second element, need to change to array.
2429 existing = events[type] =
2430 prepend ? [listener, existing] : [existing, listener];
2431 } else {
2432 // If we've already got an array, just append.
2433 if (prepend) {
2434 existing.unshift(listener);
2435 } else {
2436 existing.push(listener);
2437 }
2438 }
2439
2440 // Check for listener leak
2441 if (!existing.warned) {
2442 m = $getMaxListeners(target);
2443 if (m && m > 0 && existing.length > m) {
2444 existing.warned = true;
2445 var w = new Error('Possible EventEmitter memory leak detected. ' +
2446 existing.length + ' "' + String(type) + '" listeners ' +
2447 'added. Use emitter.setMaxListeners() to ' +
2448 'increase limit.');
2449 w.name = 'MaxListenersExceededWarning';
2450 w.emitter = target;
2451 w.type = type;
2452 w.count = existing.length;
2453 if (typeof console === 'object' && console.warn) {
2454 console.warn('%s: %s', w.name, w.message);
2455 }
2456 }
2457 }
2458 }
2459
2460 return target;
2461}
2462
2463EventEmitter.prototype.addListener = function addListener(type, listener) {
2464 return _addListener(this, type, listener, false);
2465};
2466
2467EventEmitter.prototype.on = EventEmitter.prototype.addListener;
2468
2469EventEmitter.prototype.prependListener =
2470 function prependListener(type, listener) {
2471 return _addListener(this, type, listener, true);
2472 };
2473
2474function onceWrapper() {
2475 if (!this.fired) {
2476 this.target.removeListener(this.type, this.wrapFn);
2477 this.fired = true;
2478 switch (arguments.length) {
2479 case 0:
2480 return this.listener.call(this.target);
2481 case 1:
2482 return this.listener.call(this.target, arguments[0]);
2483 case 2:
2484 return this.listener.call(this.target, arguments[0], arguments[1]);
2485 case 3:
2486 return this.listener.call(this.target, arguments[0], arguments[1],
2487 arguments[2]);
2488 default:
2489 var args = new Array(arguments.length);
2490 for (var i = 0; i < args.length; ++i)
2491 args[i] = arguments[i];
2492 this.listener.apply(this.target, args);
2493 }
2494 }
2495}
2496
2497function _onceWrap(target, type, listener) {
2498 var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
2499 var wrapped = bind.call(onceWrapper, state);
2500 wrapped.listener = listener;
2501 state.wrapFn = wrapped;
2502 return wrapped;
2503}
2504
2505EventEmitter.prototype.once = function once(type, listener) {
2506 if (typeof listener !== 'function')
2507 throw new TypeError('"listener" argument must be a function');
2508 this.on(type, _onceWrap(this, type, listener));
2509 return this;
2510};
2511
2512EventEmitter.prototype.prependOnceListener =
2513 function prependOnceListener(type, listener) {
2514 if (typeof listener !== 'function')
2515 throw new TypeError('"listener" argument must be a function');
2516 this.prependListener(type, _onceWrap(this, type, listener));
2517 return this;
2518 };
2519
2520// Emits a 'removeListener' event if and only if the listener was removed.
2521EventEmitter.prototype.removeListener =
2522 function removeListener(type, listener) {
2523 var list, events, position, i, originalListener;
2524
2525 if (typeof listener !== 'function')
2526 throw new TypeError('"listener" argument must be a function');
2527
2528 events = this._events;
2529 if (!events)
2530 return this;
2531
2532 list = events[type];
2533 if (!list)
2534 return this;
2535
2536 if (list === listener || list.listener === listener) {
2537 if (--this._eventsCount === 0)
2538 this._events = objectCreate(null);
2539 else {
2540 delete events[type];
2541 if (events.removeListener)
2542 this.emit('removeListener', type, list.listener || listener);
2543 }
2544 } else if (typeof list !== 'function') {
2545 position = -1;
2546
2547 for (i = list.length - 1; i >= 0; i--) {
2548 if (list[i] === listener || list[i].listener === listener) {
2549 originalListener = list[i].listener;
2550 position = i;
2551 break;
2552 }
2553 }
2554
2555 if (position < 0)
2556 return this;
2557
2558 if (position === 0)
2559 list.shift();
2560 else
2561 spliceOne(list, position);
2562
2563 if (list.length === 1)
2564 events[type] = list[0];
2565
2566 if (events.removeListener)
2567 this.emit('removeListener', type, originalListener || listener);
2568 }
2569
2570 return this;
2571 };
2572
2573EventEmitter.prototype.removeAllListeners =
2574 function removeAllListeners(type) {
2575 var listeners, events, i;
2576
2577 events = this._events;
2578 if (!events)
2579 return this;
2580
2581 // not listening for removeListener, no need to emit
2582 if (!events.removeListener) {
2583 if (arguments.length === 0) {
2584 this._events = objectCreate(null);
2585 this._eventsCount = 0;
2586 } else if (events[type]) {
2587 if (--this._eventsCount === 0)
2588 this._events = objectCreate(null);
2589 else
2590 delete events[type];
2591 }
2592 return this;
2593 }
2594
2595 // emit removeListener for all listeners on all events
2596 if (arguments.length === 0) {
2597 var keys = objectKeys(events);
2598 var key;
2599 for (i = 0; i < keys.length; ++i) {
2600 key = keys[i];
2601 if (key === 'removeListener') continue;
2602 this.removeAllListeners(key);
2603 }
2604 this.removeAllListeners('removeListener');
2605 this._events = objectCreate(null);
2606 this._eventsCount = 0;
2607 return this;
2608 }
2609
2610 listeners = events[type];
2611
2612 if (typeof listeners === 'function') {
2613 this.removeListener(type, listeners);
2614 } else if (listeners) {
2615 // LIFO order
2616 for (i = listeners.length - 1; i >= 0; i--) {
2617 this.removeListener(type, listeners[i]);
2618 }
2619 }
2620
2621 return this;
2622 };
2623
2624function _listeners(target, type, unwrap) {
2625 var events = target._events;
2626
2627 if (!events)
2628 return [];
2629
2630 var evlistener = events[type];
2631 if (!evlistener)
2632 return [];
2633
2634 if (typeof evlistener === 'function')
2635 return unwrap ? [evlistener.listener || evlistener] : [evlistener];
2636
2637 return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
2638}
2639
2640EventEmitter.prototype.listeners = function listeners(type) {
2641 return _listeners(this, type, true);
2642};
2643
2644EventEmitter.prototype.rawListeners = function rawListeners(type) {
2645 return _listeners(this, type, false);
2646};
2647
2648EventEmitter.listenerCount = function(emitter, type) {
2649 if (typeof emitter.listenerCount === 'function') {
2650 return emitter.listenerCount(type);
2651 } else {
2652 return listenerCount.call(emitter, type);
2653 }
2654};
2655
2656EventEmitter.prototype.listenerCount = listenerCount;
2657function listenerCount(type) {
2658 var events = this._events;
2659
2660 if (events) {
2661 var evlistener = events[type];
2662
2663 if (typeof evlistener === 'function') {
2664 return 1;
2665 } else if (evlistener) {
2666 return evlistener.length;
2667 }
2668 }
2669
2670 return 0;
2671}
2672
2673EventEmitter.prototype.eventNames = function eventNames() {
2674 return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
2675};
2676
2677// About 1.5x faster than the two-arg version of Array#splice().
2678function spliceOne(list, index) {
2679 for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
2680 list[i] = list[k];
2681 list.pop();
2682}
2683
2684function arrayClone(arr, n) {
2685 var copy = new Array(n);
2686 for (var i = 0; i < n; ++i)
2687 copy[i] = arr[i];
2688 return copy;
2689}
2690
2691function unwrapListeners(arr) {
2692 var ret = new Array(arr.length);
2693 for (var i = 0; i < ret.length; ++i) {
2694 ret[i] = arr[i].listener || arr[i];
2695 }
2696 return ret;
2697}
2698
2699function objectCreatePolyfill(proto) {
2700 var F = function() {};
2701 F.prototype = proto;
2702 return new F;
2703}
2704function objectKeysPolyfill(obj) {
2705 var keys = [];
2706 for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
2707 keys.push(k);
2708 }
2709 return k;
2710}
2711function functionBindPolyfill(context) {
2712 var fn = this;
2713 return function () {
2714 return fn.apply(context, arguments);
2715 };
2716}
2717
2718},{}],12:[function(_dereq_,module,exports){
2719module.exports = {
2720 AFG: 'afghan',
2721 ALA: '\\b\\wland',
2722 ALB: 'albania',
2723 DZA: 'algeria',
2724 ASM: '^(?=.*americ).*samoa',
2725 AND: 'andorra',
2726 AGO: 'angola',
2727 AIA: 'anguill?a',
2728 ATA: 'antarctica',
2729 ATG: 'antigua',
2730 ARG: 'argentin',
2731 ARM: 'armenia',
2732 ABW: '^(?!.*bonaire).*\\baruba',
2733 AUS: 'australia',
2734 AUT: '^(?!.*hungary).*austria|\\baustri.*\\bemp',
2735 AZE: 'azerbaijan',
2736 BHS: 'bahamas',
2737 BHR: 'bahrain',
2738 BGD: 'bangladesh|^(?=.*east).*paki?stan',
2739 BRB: 'barbados',
2740 BLR: 'belarus|byelo',
2741 BEL: '^(?!.*luxem).*belgium',
2742 BLZ: 'belize|^(?=.*british).*honduras',
2743 BEN: 'benin|dahome',
2744 BMU: 'bermuda',
2745 BTN: 'bhutan',
2746 BOL: 'bolivia',
2747 BES: '^(?=.*bonaire).*eustatius|^(?=.*carib).*netherlands|\\bbes.?islands',
2748 BIH: 'herzegovina|bosnia',
2749 BWA: 'botswana|bechuana',
2750 BVT: 'bouvet',
2751 BRA: 'brazil',
2752 IOT: 'british.?indian.?ocean',
2753 BRN: 'brunei',
2754 BGR: 'bulgaria',
2755 BFA: 'burkina|\\bfaso|upper.?volta',
2756 BDI: 'burundi',
2757 CPV: 'verde',
2758 KHM: 'cambodia|kampuchea|khmer',
2759 CMR: 'cameroon',
2760 CAN: 'canada',
2761 CYM: 'cayman',
2762 CAF: '\\bcentral.african.republic',
2763 TCD: '\\bchad',
2764 CHL: '\\bchile',
2765 CHN: '^(?!.*\\bmac)(?!.*\\bhong)(?!.*\\btai)(?!.*\\brep).*china|^(?=.*peo)(?=.*rep).*china',
2766 CXR: 'christmas',
2767 CCK: '\\bcocos|keeling',
2768 COL: 'colombia',
2769 COM: 'comoro',
2770 COG: '^(?!.*\\bdem)(?!.*\\bd[\\.]?r)(?!.*kinshasa)(?!.*zaire)(?!.*belg)(?!.*l.opoldville)(?!.*free).*\\bcongo',
2771 COK: '\\bcook',
2772 CRI: 'costa.?rica',
2773 CIV: 'ivoire|ivory',
2774 HRV: 'croatia',
2775 CUB: '\\bcuba',
2776 CUW: '^(?!.*bonaire).*\\bcura(c|ç)ao',
2777 CYP: 'cyprus',
2778 CSK: 'czechoslovakia',
2779 CZE: '^(?=.*rep).*czech|czechia|bohemia',
2780 COD: '\\bdem.*congo|congo.*\\bdem|congo.*\\bd[\\.]?r|\\bd[\\.]?r.*congo|belgian.?congo|congo.?free.?state|kinshasa|zaire|l.opoldville|drc|droc|rdc',
2781 DNK: 'denmark',
2782 DJI: 'djibouti',
2783 DMA: 'dominica(?!n)',
2784 DOM: 'dominican.rep',
2785 ECU: 'ecuador',
2786 EGY: 'egypt',
2787 SLV: 'el.?salvador',
2788 GNQ: 'guine.*eq|eq.*guine|^(?=.*span).*guinea',
2789 ERI: 'eritrea',
2790 EST: 'estonia',
2791 ETH: 'ethiopia|abyssinia',
2792 FLK: 'falkland|malvinas',
2793 FRO: 'faroe|faeroe',
2794 FJI: 'fiji',
2795 FIN: 'finland',
2796 FRA: '^(?!.*\\bdep)(?!.*martinique).*france|french.?republic|\\bgaul',
2797 GUF: '^(?=.*french).*guiana',
2798 PYF: 'french.?polynesia|tahiti',
2799 ATF: 'french.?southern',
2800 GAB: 'gabon',
2801 GMB: 'gambia',
2802 GEO: '^(?!.*south).*georgia',
2803 DDR: 'german.?democratic.?republic|democratic.?republic.*germany|east.germany',
2804 DEU: '^(?!.*east).*germany|^(?=.*\\bfed.*\\brep).*german',
2805 GHA: 'ghana|gold.?coast',
2806 GIB: 'gibraltar',
2807 GRC: 'greece|hellenic|hellas',
2808 GRL: 'greenland',
2809 GRD: 'grenada',
2810 GLP: 'guadeloupe',
2811 GUM: '\\bguam',
2812 GTM: 'guatemala',
2813 GGY: 'guernsey',
2814 GIN: '^(?!.*eq)(?!.*span)(?!.*bissau)(?!.*portu)(?!.*new).*guinea',
2815 GNB: 'bissau|^(?=.*portu).*guinea',
2816 GUY: 'guyana|british.?guiana',
2817 HTI: 'haiti',
2818 HMD: 'heard.*mcdonald',
2819 VAT: 'holy.?see|vatican|papal.?st',
2820 HND: '^(?!.*brit).*honduras',
2821 HKG: 'hong.?kong',
2822 HUN: '^(?!.*austr).*hungary',
2823 ISL: 'iceland',
2824 IND: 'india(?!.*ocea)',
2825 IDN: 'indonesia',
2826 IRN: '\\biran|persia',
2827 IRQ: '\\biraq|mesopotamia',
2828 IRL: '(^ireland)|(^republic.*ireland)',
2829 IMN: '^(?=.*isle).*\\bman',
2830 ISR: 'israel',
2831 ITA: 'italy',
2832 JAM: 'jamaica',
2833 JPN: 'japan',
2834 JEY: 'jersey',
2835 JOR: 'jordan',
2836 KAZ: 'kazak',
2837 KEN: 'kenya|british.?east.?africa|east.?africa.?prot',
2838 KIR: 'kiribati',
2839 PRK: '^(?=.*democrat|people|north|d.*p.*.r).*\\bkorea|dprk|korea.*(d.*p.*r)',
2840 KWT: 'kuwait',
2841 KGZ: 'kyrgyz|kirghiz',
2842 LAO: '\\blaos?\\b',
2843 LVA: 'latvia',
2844 LBN: 'lebanon',
2845 LSO: 'lesotho|basuto',
2846 LBR: 'liberia',
2847 LBY: 'libya',
2848 LIE: 'liechtenstein',
2849 LTU: 'lithuania',
2850 LUX: '^(?!.*belg).*luxem',
2851 MAC: 'maca(o|u)',
2852 MDG: 'madagascar|malagasy',
2853 MWI: 'malawi|nyasa',
2854 MYS: 'malaysia',
2855 MDV: 'maldive',
2856 MLI: '\\bmali\\b',
2857 MLT: '\\bmalta',
2858 MHL: 'marshall',
2859 MTQ: 'martinique',
2860 MRT: 'mauritania',
2861 MUS: 'mauritius',
2862 MYT: '\\bmayotte',
2863 MEX: '\\bmexic',
2864 FSM: 'fed.*micronesia|micronesia.*fed',
2865 MCO: 'monaco',
2866 MNG: 'mongolia',
2867 MNE: '^(?!.*serbia).*montenegro',
2868 MSR: 'montserrat',
2869 MAR: 'morocco|\\bmaroc',
2870 MOZ: 'mozambique',
2871 MMR: 'myanmar|burma',
2872 NAM: 'namibia',
2873 NRU: 'nauru',
2874 NPL: 'nepal',
2875 NLD: '^(?!.*\\bant)(?!.*\\bcarib).*netherlands',
2876 ANT: '^(?=.*\\bant).*(nether|dutch)',
2877 NCL: 'new.?caledonia',
2878 NZL: 'new.?zealand',
2879 NIC: 'nicaragua',
2880 NER: '\\bniger(?!ia)',
2881 NGA: 'nigeria',
2882 NIU: 'niue',
2883 NFK: 'norfolk',
2884 MNP: 'mariana',
2885 NOR: 'norway',
2886 OMN: '\\boman|trucial',
2887 PAK: '^(?!.*east).*paki?stan',
2888 PLW: 'palau',
2889 PSE: 'palestin|\\bgaza|west.?bank',
2890 PAN: 'panama',
2891 PNG: 'papua|new.?guinea',
2892 PRY: 'paraguay',
2893 PER: 'peru',
2894 PHL: 'philippines',
2895 PCN: 'pitcairn',
2896 POL: 'poland',
2897 PRT: 'portugal',
2898 PRI: 'puerto.?rico',
2899 QAT: 'qatar',
2900 KOR: '^(?!.*d.*p.*r)(?!.*democrat)(?!.*people)(?!.*north).*\\bkorea(?!.*d.*p.*r)',
2901 MDA: 'moldov|b(a|e)ssarabia',
2902 REU: 'r(e|é)union',
2903 ROU: 'r(o|u|ou)mania',
2904 RUS: '\\brussia|soviet.?union|u\\.?s\\.?s\\.?r|socialist.?republics',
2905 RWA: 'rwanda',
2906 BLM: 'barth(e|é)lemy',
2907 SHN: 'helena',
2908 KNA: 'kitts|\\bnevis',
2909 LCA: '\\blucia',
2910 MAF: '^(?=.*collectivity).*martin|^(?=.*france).*martin(?!ique)|^(?=.*french).*martin(?!ique)',
2911 SPM: 'miquelon',
2912 VCT: 'vincent',
2913 WSM: '^(?!.*amer).*samoa',
2914 SMR: 'san.?marino',
2915 STP: '\\bs(a|ã)o.?tom(e|é)',
2916 SAU: '\\bsa\\w*.?arabia',
2917 SEN: 'senegal',
2918 SRB: '^(?!.*monte).*serbia',
2919 SYC: 'seychell',
2920 SLE: 'sierra',
2921 SGP: 'singapore',
2922 SXM: '^(?!.*martin)(?!.*saba).*maarten',
2923 SVK: '^(?!.*cze).*slovak',
2924 SVN: 'slovenia',
2925 SLB: 'solomon',
2926 SOM: 'somali',
2927 ZAF: 'south.africa|s\\\\..?africa',
2928 SGS: 'south.?georgia|sandwich',
2929 SSD: '\\bs\\w*.?sudan',
2930 ESP: 'spain',
2931 LKA: 'sri.?lanka|ceylon',
2932 SDN: '^(?!.*\\bs(?!u)).*sudan',
2933 SUR: 'surinam|dutch.?guiana',
2934 SJM: 'svalbard',
2935 SWZ: 'swaziland',
2936 SWE: 'sweden',
2937 CHE: 'switz|swiss',
2938 SYR: 'syria',
2939 TWN: 'taiwan|taipei|formosa|^(?!.*peo)(?=.*rep).*china',
2940 TJK: 'tajik',
2941 THA: 'thailand|\\bsiam',
2942 MKD: 'macedonia|fyrom',
2943 TLS: '^(?=.*leste).*timor|^(?=.*east).*timor',
2944 TGO: 'togo',
2945 TKL: 'tokelau',
2946 TON: 'tonga',
2947 TTO: 'trinidad|tobago',
2948 TUN: 'tunisia',
2949 TUR: 'turkey',
2950 TKM: 'turkmen',
2951 TCA: 'turks',
2952 TUV: 'tuvalu',
2953 UGA: 'uganda',
2954 UKR: 'ukrain',
2955 ARE: 'emirates|^u\\.?a\\.?e\\.?$|united.?arab.?em',
2956 GBR: 'united.?kingdom|britain|^u\\.?k\\.?$',
2957 TZA: 'tanzania',
2958 USA: 'united.?states\\b(?!.*islands)|\\bu\\.?s\\.?a\\.?\\b|^\\s*u\\.?s\\.?\\b(?!.*islands)',
2959 UMI: 'minor.?outlying.?is',
2960 URY: 'uruguay',
2961 UZB: 'uzbek',
2962 VUT: 'vanuatu|new.?hebrides',
2963 VEN: 'venezuela',
2964 VNM: '^(?!.*republic).*viet.?nam|^(?=.*socialist).*viet.?nam',
2965 VGB: '^(?=.*\\bu\\.?\\s?k).*virgin|^(?=.*brit).*virgin|^(?=.*kingdom).*virgin',
2966 VIR: '^(?=.*\\bu\\.?\\s?s).*virgin|^(?=.*states).*virgin',
2967 WLF: 'futuna|wallis',
2968 ESH: 'western.sahara',
2969 YEM: '^(?!.*arab)(?!.*north)(?!.*sana)(?!.*peo)(?!.*dem)(?!.*south)(?!.*aden)(?!.*\\bp\\.?d\\.?r).*yemen',
2970 YMD: '^(?=.*peo).*yemen|^(?!.*rep)(?=.*dem).*yemen|^(?=.*south).*yemen|^(?=.*aden).*yemen|^(?=.*\\bp\\.?d\\.?r).*yemen',
2971 YUG: 'yugoslavia',
2972 ZMB: 'zambia|northern.?rhodesia',
2973 EAZ: 'zanzibar',
2974 ZWE: 'zimbabwe|^(?!.*northern).*rhodesia'
2975}
2976
2977},{}],13:[function(_dereq_,module,exports){
2978!function() {
2979 var d3 = {
2980 version: "3.5.17"
2981 };
2982 var d3_arraySlice = [].slice, d3_array = function(list) {
2983 return d3_arraySlice.call(list);
2984 };
2985 var d3_document = this.document;
2986 function d3_documentElement(node) {
2987 return node && (node.ownerDocument || node.document || node).documentElement;
2988 }
2989 function d3_window(node) {
2990 return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView);
2991 }
2992 if (d3_document) {
2993 try {
2994 d3_array(d3_document.documentElement.childNodes)[0].nodeType;
2995 } catch (e) {
2996 d3_array = function(list) {
2997 var i = list.length, array = new Array(i);
2998 while (i--) array[i] = list[i];
2999 return array;
3000 };
3001 }
3002 }
3003 if (!Date.now) Date.now = function() {
3004 return +new Date();
3005 };
3006 if (d3_document) {
3007 try {
3008 d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
3009 } catch (error) {
3010 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;
3011 d3_element_prototype.setAttribute = function(name, value) {
3012 d3_element_setAttribute.call(this, name, value + "");
3013 };
3014 d3_element_prototype.setAttributeNS = function(space, local, value) {
3015 d3_element_setAttributeNS.call(this, space, local, value + "");
3016 };
3017 d3_style_prototype.setProperty = function(name, value, priority) {
3018 d3_style_setProperty.call(this, name, value + "", priority);
3019 };
3020 }
3021 }
3022 d3.ascending = d3_ascending;
3023 function d3_ascending(a, b) {
3024 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
3025 }
3026 d3.descending = function(a, b) {
3027 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
3028 };
3029 d3.min = function(array, f) {
3030 var i = -1, n = array.length, a, b;
3031 if (arguments.length === 1) {
3032 while (++i < n) if ((b = array[i]) != null && b >= b) {
3033 a = b;
3034 break;
3035 }
3036 while (++i < n) if ((b = array[i]) != null && a > b) a = b;
3037 } else {
3038 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
3039 a = b;
3040 break;
3041 }
3042 while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
3043 }
3044 return a;
3045 };
3046 d3.max = function(array, f) {
3047 var i = -1, n = array.length, a, b;
3048 if (arguments.length === 1) {
3049 while (++i < n) if ((b = array[i]) != null && b >= b) {
3050 a = b;
3051 break;
3052 }
3053 while (++i < n) if ((b = array[i]) != null && b > a) a = b;
3054 } else {
3055 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
3056 a = b;
3057 break;
3058 }
3059 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
3060 }
3061 return a;
3062 };
3063 d3.extent = function(array, f) {
3064 var i = -1, n = array.length, a, b, c;
3065 if (arguments.length === 1) {
3066 while (++i < n) if ((b = array[i]) != null && b >= b) {
3067 a = c = b;
3068 break;
3069 }
3070 while (++i < n) if ((b = array[i]) != null) {
3071 if (a > b) a = b;
3072 if (c < b) c = b;
3073 }
3074 } else {
3075 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
3076 a = c = b;
3077 break;
3078 }
3079 while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
3080 if (a > b) a = b;
3081 if (c < b) c = b;
3082 }
3083 }
3084 return [ a, c ];
3085 };
3086 function d3_number(x) {
3087 return x === null ? NaN : +x;
3088 }
3089 function d3_numeric(x) {
3090 return !isNaN(x);
3091 }
3092 d3.sum = function(array, f) {
3093 var s = 0, n = array.length, a, i = -1;
3094 if (arguments.length === 1) {
3095 while (++i < n) if (d3_numeric(a = +array[i])) s += a;
3096 } else {
3097 while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
3098 }
3099 return s;
3100 };
3101 d3.mean = function(array, f) {
3102 var s = 0, n = array.length, a, i = -1, j = n;
3103 if (arguments.length === 1) {
3104 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
3105 } else {
3106 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
3107 }
3108 if (j) return s / j;
3109 };
3110 d3.quantile = function(values, p) {
3111 var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
3112 return e ? v + e * (values[h] - v) : v;
3113 };
3114 d3.median = function(array, f) {
3115 var numbers = [], n = array.length, a, i = -1;
3116 if (arguments.length === 1) {
3117 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
3118 } else {
3119 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
3120 }
3121 if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
3122 };
3123 d3.variance = function(array, f) {
3124 var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
3125 if (arguments.length === 1) {
3126 while (++i < n) {
3127 if (d3_numeric(a = d3_number(array[i]))) {
3128 d = a - m;
3129 m += d / ++j;
3130 s += d * (a - m);
3131 }
3132 }
3133 } else {
3134 while (++i < n) {
3135 if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
3136 d = a - m;
3137 m += d / ++j;
3138 s += d * (a - m);
3139 }
3140 }
3141 }
3142 if (j > 1) return s / (j - 1);
3143 };
3144 d3.deviation = function() {
3145 var v = d3.variance.apply(this, arguments);
3146 return v ? Math.sqrt(v) : v;
3147 };
3148 function d3_bisector(compare) {
3149 return {
3150 left: function(a, x, lo, hi) {
3151 if (arguments.length < 3) lo = 0;
3152 if (arguments.length < 4) hi = a.length;
3153 while (lo < hi) {
3154 var mid = lo + hi >>> 1;
3155 if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
3156 }
3157 return lo;
3158 },
3159 right: function(a, x, lo, hi) {
3160 if (arguments.length < 3) lo = 0;
3161 if (arguments.length < 4) hi = a.length;
3162 while (lo < hi) {
3163 var mid = lo + hi >>> 1;
3164 if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
3165 }
3166 return lo;
3167 }
3168 };
3169 }
3170 var d3_bisect = d3_bisector(d3_ascending);
3171 d3.bisectLeft = d3_bisect.left;
3172 d3.bisect = d3.bisectRight = d3_bisect.right;
3173 d3.bisector = function(f) {
3174 return d3_bisector(f.length === 1 ? function(d, x) {
3175 return d3_ascending(f(d), x);
3176 } : f);
3177 };
3178 d3.shuffle = function(array, i0, i1) {
3179 if ((m = arguments.length) < 3) {
3180 i1 = array.length;
3181 if (m < 2) i0 = 0;
3182 }
3183 var m = i1 - i0, t, i;
3184 while (m) {
3185 i = Math.random() * m-- | 0;
3186 t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
3187 }
3188 return array;
3189 };
3190 d3.permute = function(array, indexes) {
3191 var i = indexes.length, permutes = new Array(i);
3192 while (i--) permutes[i] = array[indexes[i]];
3193 return permutes;
3194 };
3195 d3.pairs = function(array) {
3196 var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
3197 while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
3198 return pairs;
3199 };
3200 d3.transpose = function(matrix) {
3201 if (!(n = matrix.length)) return [];
3202 for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) {
3203 for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) {
3204 row[j] = matrix[j][i];
3205 }
3206 }
3207 return transpose;
3208 };
3209 function d3_transposeLength(d) {
3210 return d.length;
3211 }
3212 d3.zip = function() {
3213 return d3.transpose(arguments);
3214 };
3215 d3.keys = function(map) {
3216 var keys = [];
3217 for (var key in map) keys.push(key);
3218 return keys;
3219 };
3220 d3.values = function(map) {
3221 var values = [];
3222 for (var key in map) values.push(map[key]);
3223 return values;
3224 };
3225 d3.entries = function(map) {
3226 var entries = [];
3227 for (var key in map) entries.push({
3228 key: key,
3229 value: map[key]
3230 });
3231 return entries;
3232 };
3233 d3.merge = function(arrays) {
3234 var n = arrays.length, m, i = -1, j = 0, merged, array;
3235 while (++i < n) j += arrays[i].length;
3236 merged = new Array(j);
3237 while (--n >= 0) {
3238 array = arrays[n];
3239 m = array.length;
3240 while (--m >= 0) {
3241 merged[--j] = array[m];
3242 }
3243 }
3244 return merged;
3245 };
3246 var abs = Math.abs;
3247 d3.range = function(start, stop, step) {
3248 if (arguments.length < 3) {
3249 step = 1;
3250 if (arguments.length < 2) {
3251 stop = start;
3252 start = 0;
3253 }
3254 }
3255 if ((stop - start) / step === Infinity) throw new Error("infinite range");
3256 var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
3257 start *= k, stop *= k, step *= k;
3258 if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
3259 return range;
3260 };
3261 function d3_range_integerScale(x) {
3262 var k = 1;
3263 while (x * k % 1) k *= 10;
3264 return k;
3265 }
3266 function d3_class(ctor, properties) {
3267 for (var key in properties) {
3268 Object.defineProperty(ctor.prototype, key, {
3269 value: properties[key],
3270 enumerable: false
3271 });
3272 }
3273 }
3274 d3.map = function(object, f) {
3275 var map = new d3_Map();
3276 if (object instanceof d3_Map) {
3277 object.forEach(function(key, value) {
3278 map.set(key, value);
3279 });
3280 } else if (Array.isArray(object)) {
3281 var i = -1, n = object.length, o;
3282 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);
3283 } else {
3284 for (var key in object) map.set(key, object[key]);
3285 }
3286 return map;
3287 };
3288 function d3_Map() {
3289 this._ = Object.create(null);
3290 }
3291 var d3_map_proto = "__proto__", d3_map_zero = "\x00";
3292 d3_class(d3_Map, {
3293 has: d3_map_has,
3294 get: function(key) {
3295 return this._[d3_map_escape(key)];
3296 },
3297 set: function(key, value) {
3298 return this._[d3_map_escape(key)] = value;
3299 },
3300 remove: d3_map_remove,
3301 keys: d3_map_keys,
3302 values: function() {
3303 var values = [];
3304 for (var key in this._) values.push(this._[key]);
3305 return values;
3306 },
3307 entries: function() {
3308 var entries = [];
3309 for (var key in this._) entries.push({
3310 key: d3_map_unescape(key),
3311 value: this._[key]
3312 });
3313 return entries;
3314 },
3315 size: d3_map_size,
3316 empty: d3_map_empty,
3317 forEach: function(f) {
3318 for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
3319 }
3320 });
3321 function d3_map_escape(key) {
3322 return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
3323 }
3324 function d3_map_unescape(key) {
3325 return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
3326 }
3327 function d3_map_has(key) {
3328 return d3_map_escape(key) in this._;
3329 }
3330 function d3_map_remove(key) {
3331 return (key = d3_map_escape(key)) in this._ && delete this._[key];
3332 }
3333 function d3_map_keys() {
3334 var keys = [];
3335 for (var key in this._) keys.push(d3_map_unescape(key));
3336 return keys;
3337 }
3338 function d3_map_size() {
3339 var size = 0;
3340 for (var key in this._) ++size;
3341 return size;
3342 }
3343 function d3_map_empty() {
3344 for (var key in this._) return false;
3345 return true;
3346 }
3347 d3.nest = function() {
3348 var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
3349 function map(mapType, array, depth) {
3350 if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
3351 var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
3352 while (++i < n) {
3353 if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
3354 values.push(object);
3355 } else {
3356 valuesByKey.set(keyValue, [ object ]);
3357 }
3358 }
3359 if (mapType) {
3360 object = mapType();
3361 setter = function(keyValue, values) {
3362 object.set(keyValue, map(mapType, values, depth));
3363 };
3364 } else {
3365 object = {};
3366 setter = function(keyValue, values) {
3367 object[keyValue] = map(mapType, values, depth);
3368 };
3369 }
3370 valuesByKey.forEach(setter);
3371 return object;
3372 }
3373 function entries(map, depth) {
3374 if (depth >= keys.length) return map;
3375 var array = [], sortKey = sortKeys[depth++];
3376 map.forEach(function(key, keyMap) {
3377 array.push({
3378 key: key,
3379 values: entries(keyMap, depth)
3380 });
3381 });
3382 return sortKey ? array.sort(function(a, b) {
3383 return sortKey(a.key, b.key);
3384 }) : array;
3385 }
3386 nest.map = function(array, mapType) {
3387 return map(mapType, array, 0);
3388 };
3389 nest.entries = function(array) {
3390 return entries(map(d3.map, array, 0), 0);
3391 };
3392 nest.key = function(d) {
3393 keys.push(d);
3394 return nest;
3395 };
3396 nest.sortKeys = function(order) {
3397 sortKeys[keys.length - 1] = order;
3398 return nest;
3399 };
3400 nest.sortValues = function(order) {
3401 sortValues = order;
3402 return nest;
3403 };
3404 nest.rollup = function(f) {
3405 rollup = f;
3406 return nest;
3407 };
3408 return nest;
3409 };
3410 d3.set = function(array) {
3411 var set = new d3_Set();
3412 if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
3413 return set;
3414 };
3415 function d3_Set() {
3416 this._ = Object.create(null);
3417 }
3418 d3_class(d3_Set, {
3419 has: d3_map_has,
3420 add: function(key) {
3421 this._[d3_map_escape(key += "")] = true;
3422 return key;
3423 },
3424 remove: d3_map_remove,
3425 values: d3_map_keys,
3426 size: d3_map_size,
3427 empty: d3_map_empty,
3428 forEach: function(f) {
3429 for (var key in this._) f.call(this, d3_map_unescape(key));
3430 }
3431 });
3432 d3.behavior = {};
3433 function d3_identity(d) {
3434 return d;
3435 }
3436 d3.rebind = function(target, source) {
3437 var i = 1, n = arguments.length, method;
3438 while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
3439 return target;
3440 };
3441 function d3_rebind(target, source, method) {
3442 return function() {
3443 var value = method.apply(source, arguments);
3444 return value === source ? target : value;
3445 };
3446 }
3447 function d3_vendorSymbol(object, name) {
3448 if (name in object) return name;
3449 name = name.charAt(0).toUpperCase() + name.slice(1);
3450 for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
3451 var prefixName = d3_vendorPrefixes[i] + name;
3452 if (prefixName in object) return prefixName;
3453 }
3454 }
3455 var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
3456 function d3_noop() {}
3457 d3.dispatch = function() {
3458 var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
3459 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
3460 return dispatch;
3461 };
3462 function d3_dispatch() {}
3463 d3_dispatch.prototype.on = function(type, listener) {
3464 var i = type.indexOf("."), name = "";
3465 if (i >= 0) {
3466 name = type.slice(i + 1);
3467 type = type.slice(0, i);
3468 }
3469 if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
3470 if (arguments.length === 2) {
3471 if (listener == null) for (type in this) {
3472 if (this.hasOwnProperty(type)) this[type].on(name, null);
3473 }
3474 return this;
3475 }
3476 };
3477 function d3_dispatch_event(dispatch) {
3478 var listeners = [], listenerByName = new d3_Map();
3479 function event() {
3480 var z = listeners, i = -1, n = z.length, l;
3481 while (++i < n) if (l = z[i].on) l.apply(this, arguments);
3482 return dispatch;
3483 }
3484 event.on = function(name, listener) {
3485 var l = listenerByName.get(name), i;
3486 if (arguments.length < 2) return l && l.on;
3487 if (l) {
3488 l.on = null;
3489 listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
3490 listenerByName.remove(name);
3491 }
3492 if (listener) listeners.push(listenerByName.set(name, {
3493 on: listener
3494 }));
3495 return dispatch;
3496 };
3497 return event;
3498 }
3499 d3.event = null;
3500 function d3_eventPreventDefault() {
3501 d3.event.preventDefault();
3502 }
3503 function d3_eventSource() {
3504 var e = d3.event, s;
3505 while (s = e.sourceEvent) e = s;
3506 return e;
3507 }
3508 function d3_eventDispatch(target) {
3509 var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
3510 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
3511 dispatch.of = function(thiz, argumentz) {
3512 return function(e1) {
3513 try {
3514 var e0 = e1.sourceEvent = d3.event;
3515 e1.target = target;
3516 d3.event = e1;
3517 dispatch[e1.type].apply(thiz, argumentz);
3518 } finally {
3519 d3.event = e0;
3520 }
3521 };
3522 };
3523 return dispatch;
3524 }
3525 d3.requote = function(s) {
3526 return s.replace(d3_requote_re, "\\$&");
3527 };
3528 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
3529 var d3_subclass = {}.__proto__ ? function(object, prototype) {
3530 object.__proto__ = prototype;
3531 } : function(object, prototype) {
3532 for (var property in prototype) object[property] = prototype[property];
3533 };
3534 function d3_selection(groups) {
3535 d3_subclass(groups, d3_selectionPrototype);
3536 return groups;
3537 }
3538 var d3_select = function(s, n) {
3539 return n.querySelector(s);
3540 }, d3_selectAll = function(s, n) {
3541 return n.querySelectorAll(s);
3542 }, d3_selectMatches = function(n, s) {
3543 var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
3544 d3_selectMatches = function(n, s) {
3545 return d3_selectMatcher.call(n, s);
3546 };
3547 return d3_selectMatches(n, s);
3548 };
3549 if (typeof Sizzle === "function") {
3550 d3_select = function(s, n) {
3551 return Sizzle(s, n)[0] || null;
3552 };
3553 d3_selectAll = Sizzle;
3554 d3_selectMatches = Sizzle.matchesSelector;
3555 }
3556 d3.selection = function() {
3557 return d3.select(d3_document.documentElement);
3558 };
3559 var d3_selectionPrototype = d3.selection.prototype = [];
3560 d3_selectionPrototype.select = function(selector) {
3561 var subgroups = [], subgroup, subnode, group, node;
3562 selector = d3_selection_selector(selector);
3563 for (var j = -1, m = this.length; ++j < m; ) {
3564 subgroups.push(subgroup = []);
3565 subgroup.parentNode = (group = this[j]).parentNode;
3566 for (var i = -1, n = group.length; ++i < n; ) {
3567 if (node = group[i]) {
3568 subgroup.push(subnode = selector.call(node, node.__data__, i, j));
3569 if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
3570 } else {
3571 subgroup.push(null);
3572 }
3573 }
3574 }
3575 return d3_selection(subgroups);
3576 };
3577 function d3_selection_selector(selector) {
3578 return typeof selector === "function" ? selector : function() {
3579 return d3_select(selector, this);
3580 };
3581 }
3582 d3_selectionPrototype.selectAll = function(selector) {
3583 var subgroups = [], subgroup, node;
3584 selector = d3_selection_selectorAll(selector);
3585 for (var j = -1, m = this.length; ++j < m; ) {
3586 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
3587 if (node = group[i]) {
3588 subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
3589 subgroup.parentNode = node;
3590 }
3591 }
3592 }
3593 return d3_selection(subgroups);
3594 };
3595 function d3_selection_selectorAll(selector) {
3596 return typeof selector === "function" ? selector : function() {
3597 return d3_selectAll(selector, this);
3598 };
3599 }
3600 var d3_nsXhtml = "http://www.w3.org/1999/xhtml";
3601 var d3_nsPrefix = {
3602 svg: "http://www.w3.org/2000/svg",
3603 xhtml: d3_nsXhtml,
3604 xlink: "http://www.w3.org/1999/xlink",
3605 xml: "http://www.w3.org/XML/1998/namespace",
3606 xmlns: "http://www.w3.org/2000/xmlns/"
3607 };
3608 d3.ns = {
3609 prefix: d3_nsPrefix,
3610 qualify: function(name) {
3611 var i = name.indexOf(":"), prefix = name;
3612 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
3613 return d3_nsPrefix.hasOwnProperty(prefix) ? {
3614 space: d3_nsPrefix[prefix],
3615 local: name
3616 } : name;
3617 }
3618 };
3619 d3_selectionPrototype.attr = function(name, value) {
3620 if (arguments.length < 2) {
3621 if (typeof name === "string") {
3622 var node = this.node();
3623 name = d3.ns.qualify(name);
3624 return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
3625 }
3626 for (value in name) this.each(d3_selection_attr(value, name[value]));
3627 return this;
3628 }
3629 return this.each(d3_selection_attr(name, value));
3630 };
3631 function d3_selection_attr(name, value) {
3632 name = d3.ns.qualify(name);
3633 function attrNull() {
3634 this.removeAttribute(name);
3635 }
3636 function attrNullNS() {
3637 this.removeAttributeNS(name.space, name.local);
3638 }
3639 function attrConstant() {
3640 this.setAttribute(name, value);
3641 }
3642 function attrConstantNS() {
3643 this.setAttributeNS(name.space, name.local, value);
3644 }
3645 function attrFunction() {
3646 var x = value.apply(this, arguments);
3647 if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
3648 }
3649 function attrFunctionNS() {
3650 var x = value.apply(this, arguments);
3651 if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
3652 }
3653 return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
3654 }
3655 function d3_collapse(s) {
3656 return s.trim().replace(/\s+/g, " ");
3657 }
3658 d3_selectionPrototype.classed = function(name, value) {
3659 if (arguments.length < 2) {
3660 if (typeof name === "string") {
3661 var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
3662 if (value = node.classList) {
3663 while (++i < n) if (!value.contains(name[i])) return false;
3664 } else {
3665 value = node.getAttribute("class");
3666 while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
3667 }
3668 return true;
3669 }
3670 for (value in name) this.each(d3_selection_classed(value, name[value]));
3671 return this;
3672 }
3673 return this.each(d3_selection_classed(name, value));
3674 };
3675 function d3_selection_classedRe(name) {
3676 return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
3677 }
3678 function d3_selection_classes(name) {
3679 return (name + "").trim().split(/^|\s+/);
3680 }
3681 function d3_selection_classed(name, value) {
3682 name = d3_selection_classes(name).map(d3_selection_classedName);
3683 var n = name.length;
3684 function classedConstant() {
3685 var i = -1;
3686 while (++i < n) name[i](this, value);
3687 }
3688 function classedFunction() {
3689 var i = -1, x = value.apply(this, arguments);
3690 while (++i < n) name[i](this, x);
3691 }
3692 return typeof value === "function" ? classedFunction : classedConstant;
3693 }
3694 function d3_selection_classedName(name) {
3695 var re = d3_selection_classedRe(name);
3696 return function(node, value) {
3697 if (c = node.classList) return value ? c.add(name) : c.remove(name);
3698 var c = node.getAttribute("class") || "";
3699 if (value) {
3700 re.lastIndex = 0;
3701 if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
3702 } else {
3703 node.setAttribute("class", d3_collapse(c.replace(re, " ")));
3704 }
3705 };
3706 }
3707 d3_selectionPrototype.style = function(name, value, priority) {
3708 var n = arguments.length;
3709 if (n < 3) {
3710 if (typeof name !== "string") {
3711 if (n < 2) value = "";
3712 for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
3713 return this;
3714 }
3715 if (n < 2) {
3716 var node = this.node();
3717 return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
3718 }
3719 priority = "";
3720 }
3721 return this.each(d3_selection_style(name, value, priority));
3722 };
3723 function d3_selection_style(name, value, priority) {
3724 function styleNull() {
3725 this.style.removeProperty(name);
3726 }
3727 function styleConstant() {
3728 this.style.setProperty(name, value, priority);
3729 }
3730 function styleFunction() {
3731 var x = value.apply(this, arguments);
3732 if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
3733 }
3734 return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
3735 }
3736 d3_selectionPrototype.property = function(name, value) {
3737 if (arguments.length < 2) {
3738 if (typeof name === "string") return this.node()[name];
3739 for (value in name) this.each(d3_selection_property(value, name[value]));
3740 return this;
3741 }
3742 return this.each(d3_selection_property(name, value));
3743 };
3744 function d3_selection_property(name, value) {
3745 function propertyNull() {
3746 delete this[name];
3747 }
3748 function propertyConstant() {
3749 this[name] = value;
3750 }
3751 function propertyFunction() {
3752 var x = value.apply(this, arguments);
3753 if (x == null) delete this[name]; else this[name] = x;
3754 }
3755 return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
3756 }
3757 d3_selectionPrototype.text = function(value) {
3758 return arguments.length ? this.each(typeof value === "function" ? function() {
3759 var v = value.apply(this, arguments);
3760 this.textContent = v == null ? "" : v;
3761 } : value == null ? function() {
3762 this.textContent = "";
3763 } : function() {
3764 this.textContent = value;
3765 }) : this.node().textContent;
3766 };
3767 d3_selectionPrototype.html = function(value) {
3768 return arguments.length ? this.each(typeof value === "function" ? function() {
3769 var v = value.apply(this, arguments);
3770 this.innerHTML = v == null ? "" : v;
3771 } : value == null ? function() {
3772 this.innerHTML = "";
3773 } : function() {
3774 this.innerHTML = value;
3775 }) : this.node().innerHTML;
3776 };
3777 d3_selectionPrototype.append = function(name) {
3778 name = d3_selection_creator(name);
3779 return this.select(function() {
3780 return this.appendChild(name.apply(this, arguments));
3781 });
3782 };
3783 function d3_selection_creator(name) {
3784 function create() {
3785 var document = this.ownerDocument, namespace = this.namespaceURI;
3786 return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name);
3787 }
3788 function createNS() {
3789 return this.ownerDocument.createElementNS(name.space, name.local);
3790 }
3791 return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
3792 }
3793 d3_selectionPrototype.insert = function(name, before) {
3794 name = d3_selection_creator(name);
3795 before = d3_selection_selector(before);
3796 return this.select(function() {
3797 return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
3798 });
3799 };
3800 d3_selectionPrototype.remove = function() {
3801 return this.each(d3_selectionRemove);
3802 };
3803 function d3_selectionRemove() {
3804 var parent = this.parentNode;
3805 if (parent) parent.removeChild(this);
3806 }
3807 d3_selectionPrototype.data = function(value, key) {
3808 var i = -1, n = this.length, group, node;
3809 if (!arguments.length) {
3810 value = new Array(n = (group = this[0]).length);
3811 while (++i < n) {
3812 if (node = group[i]) {
3813 value[i] = node.__data__;
3814 }
3815 }
3816 return value;
3817 }
3818 function bind(group, groupData) {
3819 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;
3820 if (key) {
3821 var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
3822 for (i = -1; ++i < n; ) {
3823 if (node = group[i]) {
3824 if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) {
3825 exitNodes[i] = node;
3826 } else {
3827 nodeByKeyValue.set(keyValue, node);
3828 }
3829 keyValues[i] = keyValue;
3830 }
3831 }
3832 for (i = -1; ++i < m; ) {
3833 if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
3834 enterNodes[i] = d3_selection_dataNode(nodeData);
3835 } else if (node !== true) {
3836 updateNodes[i] = node;
3837 node.__data__ = nodeData;
3838 }
3839 nodeByKeyValue.set(keyValue, true);
3840 }
3841 for (i = -1; ++i < n; ) {
3842 if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) {
3843 exitNodes[i] = group[i];
3844 }
3845 }
3846 } else {
3847 for (i = -1; ++i < n0; ) {
3848 node = group[i];
3849 nodeData = groupData[i];
3850 if (node) {
3851 node.__data__ = nodeData;
3852 updateNodes[i] = node;
3853 } else {
3854 enterNodes[i] = d3_selection_dataNode(nodeData);
3855 }
3856 }
3857 for (;i < m; ++i) {
3858 enterNodes[i] = d3_selection_dataNode(groupData[i]);
3859 }
3860 for (;i < n; ++i) {
3861 exitNodes[i] = group[i];
3862 }
3863 }
3864 enterNodes.update = updateNodes;
3865 enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
3866 enter.push(enterNodes);
3867 update.push(updateNodes);
3868 exit.push(exitNodes);
3869 }
3870 var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
3871 if (typeof value === "function") {
3872 while (++i < n) {
3873 bind(group = this[i], value.call(group, group.parentNode.__data__, i));
3874 }
3875 } else {
3876 while (++i < n) {
3877 bind(group = this[i], value);
3878 }
3879 }
3880 update.enter = function() {
3881 return enter;
3882 };
3883 update.exit = function() {
3884 return exit;
3885 };
3886 return update;
3887 };
3888 function d3_selection_dataNode(data) {
3889 return {
3890 __data__: data
3891 };
3892 }
3893 d3_selectionPrototype.datum = function(value) {
3894 return arguments.length ? this.property("__data__", value) : this.property("__data__");
3895 };
3896 d3_selectionPrototype.filter = function(filter) {
3897 var subgroups = [], subgroup, group, node;
3898 if (typeof filter !== "function") filter = d3_selection_filter(filter);
3899 for (var j = 0, m = this.length; j < m; j++) {
3900 subgroups.push(subgroup = []);
3901 subgroup.parentNode = (group = this[j]).parentNode;
3902 for (var i = 0, n = group.length; i < n; i++) {
3903 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
3904 subgroup.push(node);
3905 }
3906 }
3907 }
3908 return d3_selection(subgroups);
3909 };
3910 function d3_selection_filter(selector) {
3911 return function() {
3912 return d3_selectMatches(this, selector);
3913 };
3914 }
3915 d3_selectionPrototype.order = function() {
3916 for (var j = -1, m = this.length; ++j < m; ) {
3917 for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
3918 if (node = group[i]) {
3919 if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
3920 next = node;
3921 }
3922 }
3923 }
3924 return this;
3925 };
3926 d3_selectionPrototype.sort = function(comparator) {
3927 comparator = d3_selection_sortComparator.apply(this, arguments);
3928 for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
3929 return this.order();
3930 };
3931 function d3_selection_sortComparator(comparator) {
3932 if (!arguments.length) comparator = d3_ascending;
3933 return function(a, b) {
3934 return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
3935 };
3936 }
3937 d3_selectionPrototype.each = function(callback) {
3938 return d3_selection_each(this, function(node, i, j) {
3939 callback.call(node, node.__data__, i, j);
3940 });
3941 };
3942 function d3_selection_each(groups, callback) {
3943 for (var j = 0, m = groups.length; j < m; j++) {
3944 for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
3945 if (node = group[i]) callback(node, i, j);
3946 }
3947 }
3948 return groups;
3949 }
3950 d3_selectionPrototype.call = function(callback) {
3951 var args = d3_array(arguments);
3952 callback.apply(args[0] = this, args);
3953 return this;
3954 };
3955 d3_selectionPrototype.empty = function() {
3956 return !this.node();
3957 };
3958 d3_selectionPrototype.node = function() {
3959 for (var j = 0, m = this.length; j < m; j++) {
3960 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
3961 var node = group[i];
3962 if (node) return node;
3963 }
3964 }
3965 return null;
3966 };
3967 d3_selectionPrototype.size = function() {
3968 var n = 0;
3969 d3_selection_each(this, function() {
3970 ++n;
3971 });
3972 return n;
3973 };
3974 function d3_selection_enter(selection) {
3975 d3_subclass(selection, d3_selection_enterPrototype);
3976 return selection;
3977 }
3978 var d3_selection_enterPrototype = [];
3979 d3.selection.enter = d3_selection_enter;
3980 d3.selection.enter.prototype = d3_selection_enterPrototype;
3981 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
3982 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
3983 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
3984 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
3985 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
3986 d3_selection_enterPrototype.select = function(selector) {
3987 var subgroups = [], subgroup, subnode, upgroup, group, node;
3988 for (var j = -1, m = this.length; ++j < m; ) {
3989 upgroup = (group = this[j]).update;
3990 subgroups.push(subgroup = []);
3991 subgroup.parentNode = group.parentNode;
3992 for (var i = -1, n = group.length; ++i < n; ) {
3993 if (node = group[i]) {
3994 subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
3995 subnode.__data__ = node.__data__;
3996 } else {
3997 subgroup.push(null);
3998 }
3999 }
4000 }
4001 return d3_selection(subgroups);
4002 };
4003 d3_selection_enterPrototype.insert = function(name, before) {
4004 if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
4005 return d3_selectionPrototype.insert.call(this, name, before);
4006 };
4007 function d3_selection_enterInsertBefore(enter) {
4008 var i0, j0;
4009 return function(d, i, j) {
4010 var group = enter[j].update, n = group.length, node;
4011 if (j != j0) j0 = j, i0 = 0;
4012 if (i >= i0) i0 = i + 1;
4013 while (!(node = group[i0]) && ++i0 < n) ;
4014 return node;
4015 };
4016 }
4017 d3.select = function(node) {
4018 var group;
4019 if (typeof node === "string") {
4020 group = [ d3_select(node, d3_document) ];
4021 group.parentNode = d3_document.documentElement;
4022 } else {
4023 group = [ node ];
4024 group.parentNode = d3_documentElement(node);
4025 }
4026 return d3_selection([ group ]);
4027 };
4028 d3.selectAll = function(nodes) {
4029 var group;
4030 if (typeof nodes === "string") {
4031 group = d3_array(d3_selectAll(nodes, d3_document));
4032 group.parentNode = d3_document.documentElement;
4033 } else {
4034 group = d3_array(nodes);
4035 group.parentNode = null;
4036 }
4037 return d3_selection([ group ]);
4038 };
4039 d3_selectionPrototype.on = function(type, listener, capture) {
4040 var n = arguments.length;
4041 if (n < 3) {
4042 if (typeof type !== "string") {
4043 if (n < 2) listener = false;
4044 for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
4045 return this;
4046 }
4047 if (n < 2) return (n = this.node()["__on" + type]) && n._;
4048 capture = false;
4049 }
4050 return this.each(d3_selection_on(type, listener, capture));
4051 };
4052 function d3_selection_on(type, listener, capture) {
4053 var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
4054 if (i > 0) type = type.slice(0, i);
4055 var filter = d3_selection_onFilters.get(type);
4056 if (filter) type = filter, wrap = d3_selection_onFilter;
4057 function onRemove() {
4058 var l = this[name];
4059 if (l) {
4060 this.removeEventListener(type, l, l.$);
4061 delete this[name];
4062 }
4063 }
4064 function onAdd() {
4065 var l = wrap(listener, d3_array(arguments));
4066 onRemove.call(this);
4067 this.addEventListener(type, this[name] = l, l.$ = capture);
4068 l._ = listener;
4069 }
4070 function removeAll() {
4071 var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
4072 for (var name in this) {
4073 if (match = name.match(re)) {
4074 var l = this[name];
4075 this.removeEventListener(match[1], l, l.$);
4076 delete this[name];
4077 }
4078 }
4079 }
4080 return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
4081 }
4082 var d3_selection_onFilters = d3.map({
4083 mouseenter: "mouseover",
4084 mouseleave: "mouseout"
4085 });
4086 if (d3_document) {
4087 d3_selection_onFilters.forEach(function(k) {
4088 if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
4089 });
4090 }
4091 function d3_selection_onListener(listener, argumentz) {
4092 return function(e) {
4093 var o = d3.event;
4094 d3.event = e;
4095 argumentz[0] = this.__data__;
4096 try {
4097 listener.apply(this, argumentz);
4098 } finally {
4099 d3.event = o;
4100 }
4101 };
4102 }
4103 function d3_selection_onFilter(listener, argumentz) {
4104 var l = d3_selection_onListener(listener, argumentz);
4105 return function(e) {
4106 var target = this, related = e.relatedTarget;
4107 if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
4108 l.call(target, e);
4109 }
4110 };
4111 }
4112 var d3_event_dragSelect, d3_event_dragId = 0;
4113 function d3_event_dragSuppress(node) {
4114 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);
4115 if (d3_event_dragSelect == null) {
4116 d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect");
4117 }
4118 if (d3_event_dragSelect) {
4119 var style = d3_documentElement(node).style, select = style[d3_event_dragSelect];
4120 style[d3_event_dragSelect] = "none";
4121 }
4122 return function(suppressClick) {
4123 w.on(name, null);
4124 if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
4125 if (suppressClick) {
4126 var off = function() {
4127 w.on(click, null);
4128 };
4129 w.on(click, function() {
4130 d3_eventPreventDefault();
4131 off();
4132 }, true);
4133 setTimeout(off, 0);
4134 }
4135 };
4136 }
4137 d3.mouse = function(container) {
4138 return d3_mousePoint(container, d3_eventSource());
4139 };
4140 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
4141 function d3_mousePoint(container, e) {
4142 if (e.changedTouches) e = e.changedTouches[0];
4143 var svg = container.ownerSVGElement || container;
4144 if (svg.createSVGPoint) {
4145 var point = svg.createSVGPoint();
4146 if (d3_mouse_bug44083 < 0) {
4147 var window = d3_window(container);
4148 if (window.scrollX || window.scrollY) {
4149 svg = d3.select("body").append("svg").style({
4150 position: "absolute",
4151 top: 0,
4152 left: 0,
4153 margin: 0,
4154 padding: 0,
4155 border: "none"
4156 }, "important");
4157 var ctm = svg[0][0].getScreenCTM();
4158 d3_mouse_bug44083 = !(ctm.f || ctm.e);
4159 svg.remove();
4160 }
4161 }
4162 if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
4163 point.y = e.clientY;
4164 point = point.matrixTransform(container.getScreenCTM().inverse());
4165 return [ point.x, point.y ];
4166 }
4167 var rect = container.getBoundingClientRect();
4168 return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
4169 }
4170 d3.touch = function(container, touches, identifier) {
4171 if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
4172 if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
4173 if ((touch = touches[i]).identifier === identifier) {
4174 return d3_mousePoint(container, touch);
4175 }
4176 }
4177 };
4178 d3.behavior.drag = function() {
4179 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");
4180 function drag() {
4181 this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
4182 }
4183 function dragstart(id, position, subject, move, end) {
4184 return function() {
4185 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);
4186 if (origin) {
4187 dragOffset = origin.apply(that, arguments);
4188 dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
4189 } else {
4190 dragOffset = [ 0, 0 ];
4191 }
4192 dispatch({
4193 type: "dragstart"
4194 });
4195 function moved() {
4196 var position1 = position(parent, dragId), dx, dy;
4197 if (!position1) return;
4198 dx = position1[0] - position0[0];
4199 dy = position1[1] - position0[1];
4200 dragged |= dx | dy;
4201 position0 = position1;
4202 dispatch({
4203 type: "drag",
4204 x: position1[0] + dragOffset[0],
4205 y: position1[1] + dragOffset[1],
4206 dx: dx,
4207 dy: dy
4208 });
4209 }
4210 function ended() {
4211 if (!position(parent, dragId)) return;
4212 dragSubject.on(move + dragName, null).on(end + dragName, null);
4213 dragRestore(dragged);
4214 dispatch({
4215 type: "dragend"
4216 });
4217 }
4218 };
4219 }
4220 drag.origin = function(x) {
4221 if (!arguments.length) return origin;
4222 origin = x;
4223 return drag;
4224 };
4225 return d3.rebind(drag, event, "on");
4226 };
4227 function d3_behavior_dragTouchId() {
4228 return d3.event.changedTouches[0].identifier;
4229 }
4230 d3.touches = function(container, touches) {
4231 if (arguments.length < 2) touches = d3_eventSource().touches;
4232 return touches ? d3_array(touches).map(function(touch) {
4233 var point = d3_mousePoint(container, touch);
4234 point.identifier = touch.identifier;
4235 return point;
4236 }) : [];
4237 };
4238 var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
4239 function d3_sgn(x) {
4240 return x > 0 ? 1 : x < 0 ? -1 : 0;
4241 }
4242 function d3_cross2d(a, b, c) {
4243 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
4244 }
4245 function d3_acos(x) {
4246 return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
4247 }
4248 function d3_asin(x) {
4249 return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
4250 }
4251 function d3_sinh(x) {
4252 return ((x = Math.exp(x)) - 1 / x) / 2;
4253 }
4254 function d3_cosh(x) {
4255 return ((x = Math.exp(x)) + 1 / x) / 2;
4256 }
4257 function d3_tanh(x) {
4258 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
4259 }
4260 function d3_haversin(x) {
4261 return (x = Math.sin(x / 2)) * x;
4262 }
4263 var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
4264 d3.interpolateZoom = function(p0, p1) {
4265 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;
4266 if (d2 < ε2) {
4267 S = Math.log(w1 / w0) / ρ;
4268 i = function(t) {
4269 return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ];
4270 };
4271 } else {
4272 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);
4273 S = (r1 - r0) / ρ;
4274 i = function(t) {
4275 var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
4276 return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
4277 };
4278 }
4279 i.duration = S * 1e3;
4280 return i;
4281 };
4282 d3.behavior.zoom = function() {
4283 var view = {
4284 x: 0,
4285 y: 0,
4286 k: 1
4287 }, 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;
4288 if (!d3_behavior_zoomWheel) {
4289 d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
4290 return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
4291 }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
4292 return d3.event.wheelDelta;
4293 }, "mousewheel") : (d3_behavior_zoomDelta = function() {
4294 return -d3.event.detail;
4295 }, "MozMousePixelScroll");
4296 }
4297 function zoom(g) {
4298 g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
4299 }
4300 zoom.event = function(g) {
4301 g.each(function() {
4302 var dispatch = event.of(this, arguments), view1 = view;
4303 if (d3_transitionInheritId) {
4304 d3.select(this).transition().each("start.zoom", function() {
4305 view = this.__chart__ || {
4306 x: 0,
4307 y: 0,
4308 k: 1
4309 };
4310 zoomstarted(dispatch);
4311 }).tween("zoom:zoom", function() {
4312 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 ]);
4313 return function(t) {
4314 var l = i(t), k = dx / l[2];
4315 this.__chart__ = view = {
4316 x: cx - l[0] * k,
4317 y: cy - l[1] * k,
4318 k: k
4319 };
4320 zoomed(dispatch);
4321 };
4322 }).each("interrupt.zoom", function() {
4323 zoomended(dispatch);
4324 }).each("end.zoom", function() {
4325 zoomended(dispatch);
4326 });
4327 } else {
4328 this.__chart__ = view;
4329 zoomstarted(dispatch);
4330 zoomed(dispatch);
4331 zoomended(dispatch);
4332 }
4333 });
4334 };
4335 zoom.translate = function(_) {
4336 if (!arguments.length) return [ view.x, view.y ];
4337 view = {
4338 x: +_[0],
4339 y: +_[1],
4340 k: view.k
4341 };
4342 rescale();
4343 return zoom;
4344 };
4345 zoom.scale = function(_) {
4346 if (!arguments.length) return view.k;
4347 view = {
4348 x: view.x,
4349 y: view.y,
4350 k: null
4351 };
4352 scaleTo(+_);
4353 rescale();
4354 return zoom;
4355 };
4356 zoom.scaleExtent = function(_) {
4357 if (!arguments.length) return scaleExtent;
4358 scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
4359 return zoom;
4360 };
4361 zoom.center = function(_) {
4362 if (!arguments.length) return center;
4363 center = _ && [ +_[0], +_[1] ];
4364 return zoom;
4365 };
4366 zoom.size = function(_) {
4367 if (!arguments.length) return size;
4368 size = _ && [ +_[0], +_[1] ];
4369 return zoom;
4370 };
4371 zoom.duration = function(_) {
4372 if (!arguments.length) return duration;
4373 duration = +_;
4374 return zoom;
4375 };
4376 zoom.x = function(z) {
4377 if (!arguments.length) return x1;
4378 x1 = z;
4379 x0 = z.copy();
4380 view = {
4381 x: 0,
4382 y: 0,
4383 k: 1
4384 };
4385 return zoom;
4386 };
4387 zoom.y = function(z) {
4388 if (!arguments.length) return y1;
4389 y1 = z;
4390 y0 = z.copy();
4391 view = {
4392 x: 0,
4393 y: 0,
4394 k: 1
4395 };
4396 return zoom;
4397 };
4398 function location(p) {
4399 return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
4400 }
4401 function point(l) {
4402 return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
4403 }
4404 function scaleTo(s) {
4405 view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
4406 }
4407 function translateTo(p, l) {
4408 l = point(l);
4409 view.x += p[0] - l[0];
4410 view.y += p[1] - l[1];
4411 }
4412 function zoomTo(that, p, l, k) {
4413 that.__chart__ = {
4414 x: view.x,
4415 y: view.y,
4416 k: view.k
4417 };
4418 scaleTo(Math.pow(2, k));
4419 translateTo(center0 = p, l);
4420 that = d3.select(that);
4421 if (duration > 0) that = that.transition().duration(duration);
4422 that.call(zoom.event);
4423 }
4424 function rescale() {
4425 if (x1) x1.domain(x0.range().map(function(x) {
4426 return (x - view.x) / view.k;
4427 }).map(x0.invert));
4428 if (y1) y1.domain(y0.range().map(function(y) {
4429 return (y - view.y) / view.k;
4430 }).map(y0.invert));
4431 }
4432 function zoomstarted(dispatch) {
4433 if (!zooming++) dispatch({
4434 type: "zoomstart"
4435 });
4436 }
4437 function zoomed(dispatch) {
4438 rescale();
4439 dispatch({
4440 type: "zoom",
4441 scale: view.k,
4442 translate: [ view.x, view.y ]
4443 });
4444 }
4445 function zoomended(dispatch) {
4446 if (!--zooming) dispatch({
4447 type: "zoomend"
4448 }), center0 = null;
4449 }
4450 function mousedowned() {
4451 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);
4452 d3_selection_interrupt.call(that);
4453 zoomstarted(dispatch);
4454 function moved() {
4455 dragged = 1;
4456 translateTo(d3.mouse(that), location0);
4457 zoomed(dispatch);
4458 }
4459 function ended() {
4460 subject.on(mousemove, null).on(mouseup, null);
4461 dragRestore(dragged);
4462 zoomended(dispatch);
4463 }
4464 }
4465 function touchstarted() {
4466 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);
4467 started();
4468 zoomstarted(dispatch);
4469 subject.on(mousedown, null).on(touchstart, started);
4470 function relocate() {
4471 var touches = d3.touches(that);
4472 scale0 = view.k;
4473 touches.forEach(function(t) {
4474 if (t.identifier in locations0) locations0[t.identifier] = location(t);
4475 });
4476 return touches;
4477 }
4478 function started() {
4479 var target = d3.event.target;
4480 d3.select(target).on(touchmove, moved).on(touchend, ended);
4481 targets.push(target);
4482 var changed = d3.event.changedTouches;
4483 for (var i = 0, n = changed.length; i < n; ++i) {
4484 locations0[changed[i].identifier] = null;
4485 }
4486 var touches = relocate(), now = Date.now();
4487 if (touches.length === 1) {
4488 if (now - touchtime < 500) {
4489 var p = touches[0];
4490 zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
4491 d3_eventPreventDefault();
4492 }
4493 touchtime = now;
4494 } else if (touches.length > 1) {
4495 var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
4496 distance0 = dx * dx + dy * dy;
4497 }
4498 }
4499 function moved() {
4500 var touches = d3.touches(that), p0, l0, p1, l1;
4501 d3_selection_interrupt.call(that);
4502 for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
4503 p1 = touches[i];
4504 if (l1 = locations0[p1.identifier]) {
4505 if (l0) break;
4506 p0 = p1, l0 = l1;
4507 }
4508 }
4509 if (l1) {
4510 var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
4511 p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
4512 l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
4513 scaleTo(scale1 * scale0);
4514 }
4515 touchtime = null;
4516 translateTo(p0, l0);
4517 zoomed(dispatch);
4518 }
4519 function ended() {
4520 if (d3.event.touches.length) {
4521 var changed = d3.event.changedTouches;
4522 for (var i = 0, n = changed.length; i < n; ++i) {
4523 delete locations0[changed[i].identifier];
4524 }
4525 for (var identifier in locations0) {
4526 return void relocate();
4527 }
4528 }
4529 d3.selectAll(targets).on(zoomName, null);
4530 subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
4531 dragRestore();
4532 zoomended(dispatch);
4533 }
4534 }
4535 function mousewheeled() {
4536 var dispatch = event.of(this, arguments);
4537 if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
4538 translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
4539 mousewheelTimer = setTimeout(function() {
4540 mousewheelTimer = null;
4541 zoomended(dispatch);
4542 }, 50);
4543 d3_eventPreventDefault();
4544 scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
4545 translateTo(center0, translate0);
4546 zoomed(dispatch);
4547 }
4548 function dblclicked() {
4549 var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
4550 zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
4551 }
4552 return d3.rebind(zoom, event, "on");
4553 };
4554 var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel;
4555 d3.color = d3_color;
4556 function d3_color() {}
4557 d3_color.prototype.toString = function() {
4558 return this.rgb() + "";
4559 };
4560 d3.hsl = d3_hsl;
4561 function d3_hsl(h, s, l) {
4562 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);
4563 }
4564 var d3_hslPrototype = d3_hsl.prototype = new d3_color();
4565 d3_hslPrototype.brighter = function(k) {
4566 k = Math.pow(.7, arguments.length ? k : 1);
4567 return new d3_hsl(this.h, this.s, this.l / k);
4568 };
4569 d3_hslPrototype.darker = function(k) {
4570 k = Math.pow(.7, arguments.length ? k : 1);
4571 return new d3_hsl(this.h, this.s, k * this.l);
4572 };
4573 d3_hslPrototype.rgb = function() {
4574 return d3_hsl_rgb(this.h, this.s, this.l);
4575 };
4576 function d3_hsl_rgb(h, s, l) {
4577 var m1, m2;
4578 h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
4579 s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
4580 l = l < 0 ? 0 : l > 1 ? 1 : l;
4581 m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
4582 m1 = 2 * l - m2;
4583 function v(h) {
4584 if (h > 360) h -= 360; else if (h < 0) h += 360;
4585 if (h < 60) return m1 + (m2 - m1) * h / 60;
4586 if (h < 180) return m2;
4587 if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
4588 return m1;
4589 }
4590 function vv(h) {
4591 return Math.round(v(h) * 255);
4592 }
4593 return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
4594 }
4595 d3.hcl = d3_hcl;
4596 function d3_hcl(h, c, l) {
4597 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);
4598 }
4599 var d3_hclPrototype = d3_hcl.prototype = new d3_color();
4600 d3_hclPrototype.brighter = function(k) {
4601 return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
4602 };
4603 d3_hclPrototype.darker = function(k) {
4604 return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
4605 };
4606 d3_hclPrototype.rgb = function() {
4607 return d3_hcl_lab(this.h, this.c, this.l).rgb();
4608 };
4609 function d3_hcl_lab(h, c, l) {
4610 if (isNaN(h)) h = 0;
4611 if (isNaN(c)) c = 0;
4612 return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
4613 }
4614 d3.lab = d3_lab;
4615 function d3_lab(l, a, b) {
4616 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);
4617 }
4618 var d3_lab_K = 18;
4619 var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
4620 var d3_labPrototype = d3_lab.prototype = new d3_color();
4621 d3_labPrototype.brighter = function(k) {
4622 return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
4623 };
4624 d3_labPrototype.darker = function(k) {
4625 return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
4626 };
4627 d3_labPrototype.rgb = function() {
4628 return d3_lab_rgb(this.l, this.a, this.b);
4629 };
4630 function d3_lab_rgb(l, a, b) {
4631 var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
4632 x = d3_lab_xyz(x) * d3_lab_X;
4633 y = d3_lab_xyz(y) * d3_lab_Y;
4634 z = d3_lab_xyz(z) * d3_lab_Z;
4635 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));
4636 }
4637 function d3_lab_hcl(l, a, b) {
4638 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);
4639 }
4640 function d3_lab_xyz(x) {
4641 return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
4642 }
4643 function d3_xyz_lab(x) {
4644 return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
4645 }
4646 function d3_xyz_rgb(r) {
4647 return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
4648 }
4649 d3.rgb = d3_rgb;
4650 function d3_rgb(r, g, b) {
4651 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);
4652 }
4653 function d3_rgbNumber(value) {
4654 return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
4655 }
4656 function d3_rgbString(value) {
4657 return d3_rgbNumber(value) + "";
4658 }
4659 var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
4660 d3_rgbPrototype.brighter = function(k) {
4661 k = Math.pow(.7, arguments.length ? k : 1);
4662 var r = this.r, g = this.g, b = this.b, i = 30;
4663 if (!r && !g && !b) return new d3_rgb(i, i, i);
4664 if (r && r < i) r = i;
4665 if (g && g < i) g = i;
4666 if (b && b < i) b = i;
4667 return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
4668 };
4669 d3_rgbPrototype.darker = function(k) {
4670 k = Math.pow(.7, arguments.length ? k : 1);
4671 return new d3_rgb(k * this.r, k * this.g, k * this.b);
4672 };
4673 d3_rgbPrototype.hsl = function() {
4674 return d3_rgb_hsl(this.r, this.g, this.b);
4675 };
4676 d3_rgbPrototype.toString = function() {
4677 return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
4678 };
4679 function d3_rgb_hex(v) {
4680 return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
4681 }
4682 function d3_rgb_parse(format, rgb, hsl) {
4683 var r = 0, g = 0, b = 0, m1, m2, color;
4684 m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase());
4685 if (m1) {
4686 m2 = m1[2].split(",");
4687 switch (m1[1]) {
4688 case "hsl":
4689 {
4690 return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
4691 }
4692
4693 case "rgb":
4694 {
4695 return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
4696 }
4697 }
4698 }
4699 if (color = d3_rgb_names.get(format)) {
4700 return rgb(color.r, color.g, color.b);
4701 }
4702 if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
4703 if (format.length === 4) {
4704 r = (color & 3840) >> 4;
4705 r = r >> 4 | r;
4706 g = color & 240;
4707 g = g >> 4 | g;
4708 b = color & 15;
4709 b = b << 4 | b;
4710 } else if (format.length === 7) {
4711 r = (color & 16711680) >> 16;
4712 g = (color & 65280) >> 8;
4713 b = color & 255;
4714 }
4715 }
4716 return rgb(r, g, b);
4717 }
4718 function d3_rgb_hsl(r, g, b) {
4719 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;
4720 if (d) {
4721 s = l < .5 ? d / (max + min) : d / (2 - max - min);
4722 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;
4723 h *= 60;
4724 } else {
4725 h = NaN;
4726 s = l > 0 && l < 1 ? 0 : h;
4727 }
4728 return new d3_hsl(h, s, l);
4729 }
4730 function d3_rgb_lab(r, g, b) {
4731 r = d3_rgb_xyz(r);
4732 g = d3_rgb_xyz(g);
4733 b = d3_rgb_xyz(b);
4734 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);
4735 return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
4736 }
4737 function d3_rgb_xyz(r) {
4738 return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
4739 }
4740 function d3_rgb_parseNumber(c) {
4741 var f = parseFloat(c);
4742 return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
4743 }
4744 var d3_rgb_names = d3.map({
4745 aliceblue: 15792383,
4746 antiquewhite: 16444375,
4747 aqua: 65535,
4748 aquamarine: 8388564,
4749 azure: 15794175,
4750 beige: 16119260,
4751 bisque: 16770244,
4752 black: 0,
4753 blanchedalmond: 16772045,
4754 blue: 255,
4755 blueviolet: 9055202,
4756 brown: 10824234,
4757 burlywood: 14596231,
4758 cadetblue: 6266528,
4759 chartreuse: 8388352,
4760 chocolate: 13789470,
4761 coral: 16744272,
4762 cornflowerblue: 6591981,
4763 cornsilk: 16775388,
4764 crimson: 14423100,
4765 cyan: 65535,
4766 darkblue: 139,
4767 darkcyan: 35723,
4768 darkgoldenrod: 12092939,
4769 darkgray: 11119017,
4770 darkgreen: 25600,
4771 darkgrey: 11119017,
4772 darkkhaki: 12433259,
4773 darkmagenta: 9109643,
4774 darkolivegreen: 5597999,
4775 darkorange: 16747520,
4776 darkorchid: 10040012,
4777 darkred: 9109504,
4778 darksalmon: 15308410,
4779 darkseagreen: 9419919,
4780 darkslateblue: 4734347,
4781 darkslategray: 3100495,
4782 darkslategrey: 3100495,
4783 darkturquoise: 52945,
4784 darkviolet: 9699539,
4785 deeppink: 16716947,
4786 deepskyblue: 49151,
4787 dimgray: 6908265,
4788 dimgrey: 6908265,
4789 dodgerblue: 2003199,
4790 firebrick: 11674146,
4791 floralwhite: 16775920,
4792 forestgreen: 2263842,
4793 fuchsia: 16711935,
4794 gainsboro: 14474460,
4795 ghostwhite: 16316671,
4796 gold: 16766720,
4797 goldenrod: 14329120,
4798 gray: 8421504,
4799 green: 32768,
4800 greenyellow: 11403055,
4801 grey: 8421504,
4802 honeydew: 15794160,
4803 hotpink: 16738740,
4804 indianred: 13458524,
4805 indigo: 4915330,
4806 ivory: 16777200,
4807 khaki: 15787660,
4808 lavender: 15132410,
4809 lavenderblush: 16773365,
4810 lawngreen: 8190976,
4811 lemonchiffon: 16775885,
4812 lightblue: 11393254,
4813 lightcoral: 15761536,
4814 lightcyan: 14745599,
4815 lightgoldenrodyellow: 16448210,
4816 lightgray: 13882323,
4817 lightgreen: 9498256,
4818 lightgrey: 13882323,
4819 lightpink: 16758465,
4820 lightsalmon: 16752762,
4821 lightseagreen: 2142890,
4822 lightskyblue: 8900346,
4823 lightslategray: 7833753,
4824 lightslategrey: 7833753,
4825 lightsteelblue: 11584734,
4826 lightyellow: 16777184,
4827 lime: 65280,
4828 limegreen: 3329330,
4829 linen: 16445670,
4830 magenta: 16711935,
4831 maroon: 8388608,
4832 mediumaquamarine: 6737322,
4833 mediumblue: 205,
4834 mediumorchid: 12211667,
4835 mediumpurple: 9662683,
4836 mediumseagreen: 3978097,
4837 mediumslateblue: 8087790,
4838 mediumspringgreen: 64154,
4839 mediumturquoise: 4772300,
4840 mediumvioletred: 13047173,
4841 midnightblue: 1644912,
4842 mintcream: 16121850,
4843 mistyrose: 16770273,
4844 moccasin: 16770229,
4845 navajowhite: 16768685,
4846 navy: 128,
4847 oldlace: 16643558,
4848 olive: 8421376,
4849 olivedrab: 7048739,
4850 orange: 16753920,
4851 orangered: 16729344,
4852 orchid: 14315734,
4853 palegoldenrod: 15657130,
4854 palegreen: 10025880,
4855 paleturquoise: 11529966,
4856 palevioletred: 14381203,
4857 papayawhip: 16773077,
4858 peachpuff: 16767673,
4859 peru: 13468991,
4860 pink: 16761035,
4861 plum: 14524637,
4862 powderblue: 11591910,
4863 purple: 8388736,
4864 rebeccapurple: 6697881,
4865 red: 16711680,
4866 rosybrown: 12357519,
4867 royalblue: 4286945,
4868 saddlebrown: 9127187,
4869 salmon: 16416882,
4870 sandybrown: 16032864,
4871 seagreen: 3050327,
4872 seashell: 16774638,
4873 sienna: 10506797,
4874 silver: 12632256,
4875 skyblue: 8900331,
4876 slateblue: 6970061,
4877 slategray: 7372944,
4878 slategrey: 7372944,
4879 snow: 16775930,
4880 springgreen: 65407,
4881 steelblue: 4620980,
4882 tan: 13808780,
4883 teal: 32896,
4884 thistle: 14204888,
4885 tomato: 16737095,
4886 turquoise: 4251856,
4887 violet: 15631086,
4888 wheat: 16113331,
4889 white: 16777215,
4890 whitesmoke: 16119285,
4891 yellow: 16776960,
4892 yellowgreen: 10145074
4893 });
4894 d3_rgb_names.forEach(function(key, value) {
4895 d3_rgb_names.set(key, d3_rgbNumber(value));
4896 });
4897 function d3_functor(v) {
4898 return typeof v === "function" ? v : function() {
4899 return v;
4900 };
4901 }
4902 d3.functor = d3_functor;
4903 d3.xhr = d3_xhrType(d3_identity);
4904 function d3_xhrType(response) {
4905 return function(url, mimeType, callback) {
4906 if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
4907 mimeType = null;
4908 return d3_xhr(url, mimeType, response, callback);
4909 };
4910 }
4911 function d3_xhr(url, mimeType, response, callback) {
4912 var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
4913 if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
4914 "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
4915 request.readyState > 3 && respond();
4916 };
4917 function respond() {
4918 var status = request.status, result;
4919 if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
4920 try {
4921 result = response.call(xhr, request);
4922 } catch (e) {
4923 dispatch.error.call(xhr, e);
4924 return;
4925 }
4926 dispatch.load.call(xhr, result);
4927 } else {
4928 dispatch.error.call(xhr, request);
4929 }
4930 }
4931 request.onprogress = function(event) {
4932 var o = d3.event;
4933 d3.event = event;
4934 try {
4935 dispatch.progress.call(xhr, request);
4936 } finally {
4937 d3.event = o;
4938 }
4939 };
4940 xhr.header = function(name, value) {
4941 name = (name + "").toLowerCase();
4942 if (arguments.length < 2) return headers[name];
4943 if (value == null) delete headers[name]; else headers[name] = value + "";
4944 return xhr;
4945 };
4946 xhr.mimeType = function(value) {
4947 if (!arguments.length) return mimeType;
4948 mimeType = value == null ? null : value + "";
4949 return xhr;
4950 };
4951 xhr.responseType = function(value) {
4952 if (!arguments.length) return responseType;
4953 responseType = value;
4954 return xhr;
4955 };
4956 xhr.response = function(value) {
4957 response = value;
4958 return xhr;
4959 };
4960 [ "get", "post" ].forEach(function(method) {
4961 xhr[method] = function() {
4962 return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
4963 };
4964 });
4965 xhr.send = function(method, data, callback) {
4966 if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
4967 request.open(method, url, true);
4968 if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
4969 if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
4970 if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
4971 if (responseType != null) request.responseType = responseType;
4972 if (callback != null) xhr.on("error", callback).on("load", function(request) {
4973 callback(null, request);
4974 });
4975 dispatch.beforesend.call(xhr, request);
4976 request.send(data == null ? null : data);
4977 return xhr;
4978 };
4979 xhr.abort = function() {
4980 request.abort();
4981 return xhr;
4982 };
4983 d3.rebind(xhr, dispatch, "on");
4984 return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
4985 }
4986 function d3_xhr_fixCallback(callback) {
4987 return callback.length === 1 ? function(error, request) {
4988 callback(error == null ? request : null);
4989 } : callback;
4990 }
4991 function d3_xhrHasResponse(request) {
4992 var type = request.responseType;
4993 return type && type !== "text" ? request.response : request.responseText;
4994 }
4995 d3.dsv = function(delimiter, mimeType) {
4996 var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
4997 function dsv(url, row, callback) {
4998 if (arguments.length < 3) callback = row, row = null;
4999 var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
5000 xhr.row = function(_) {
5001 return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
5002 };
5003 return xhr;
5004 }
5005 function response(request) {
5006 return dsv.parse(request.responseText);
5007 }
5008 function typedResponse(f) {
5009 return function(request) {
5010 return dsv.parse(request.responseText, f);
5011 };
5012 }
5013 dsv.parse = function(text, f) {
5014 var o;
5015 return dsv.parseRows(text, function(row, i) {
5016 if (o) return o(row, i - 1);
5017 var a = new Function("d", "return {" + row.map(function(name, i) {
5018 return JSON.stringify(name) + ": d[" + i + "]";
5019 }).join(",") + "}");
5020 o = f ? function(row, i) {
5021 return f(a(row), i);
5022 } : a;
5023 });
5024 };
5025 dsv.parseRows = function(text, f) {
5026 var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
5027 function token() {
5028 if (I >= N) return EOF;
5029 if (eol) return eol = false, EOL;
5030 var j = I;
5031 if (text.charCodeAt(j) === 34) {
5032 var i = j;
5033 while (i++ < N) {
5034 if (text.charCodeAt(i) === 34) {
5035 if (text.charCodeAt(i + 1) !== 34) break;
5036 ++i;
5037 }
5038 }
5039 I = i + 2;
5040 var c = text.charCodeAt(i + 1);
5041 if (c === 13) {
5042 eol = true;
5043 if (text.charCodeAt(i + 2) === 10) ++I;
5044 } else if (c === 10) {
5045 eol = true;
5046 }
5047 return text.slice(j + 1, i).replace(/""/g, '"');
5048 }
5049 while (I < N) {
5050 var c = text.charCodeAt(I++), k = 1;
5051 if (c === 10) eol = true; else if (c === 13) {
5052 eol = true;
5053 if (text.charCodeAt(I) === 10) ++I, ++k;
5054 } else if (c !== delimiterCode) continue;
5055 return text.slice(j, I - k);
5056 }
5057 return text.slice(j);
5058 }
5059 while ((t = token()) !== EOF) {
5060 var a = [];
5061 while (t !== EOL && t !== EOF) {
5062 a.push(t);
5063 t = token();
5064 }
5065 if (f && (a = f(a, n++)) == null) continue;
5066 rows.push(a);
5067 }
5068 return rows;
5069 };
5070 dsv.format = function(rows) {
5071 if (Array.isArray(rows[0])) return dsv.formatRows(rows);
5072 var fieldSet = new d3_Set(), fields = [];
5073 rows.forEach(function(row) {
5074 for (var field in row) {
5075 if (!fieldSet.has(field)) {
5076 fields.push(fieldSet.add(field));
5077 }
5078 }
5079 });
5080 return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
5081 return fields.map(function(field) {
5082 return formatValue(row[field]);
5083 }).join(delimiter);
5084 })).join("\n");
5085 };
5086 dsv.formatRows = function(rows) {
5087 return rows.map(formatRow).join("\n");
5088 };
5089 function formatRow(row) {
5090 return row.map(formatValue).join(delimiter);
5091 }
5092 function formatValue(text) {
5093 return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
5094 }
5095 return dsv;
5096 };
5097 d3.csv = d3.dsv(",", "text/csv");
5098 d3.tsv = d3.dsv(" ", "text/tab-separated-values");
5099 var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) {
5100 setTimeout(callback, 17);
5101 };
5102 d3.timer = function() {
5103 d3_timer.apply(this, arguments);
5104 };
5105 function d3_timer(callback, delay, then) {
5106 var n = arguments.length;
5107 if (n < 2) delay = 0;
5108 if (n < 3) then = Date.now();
5109 var time = then + delay, timer = {
5110 c: callback,
5111 t: time,
5112 n: null
5113 };
5114 if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
5115 d3_timer_queueTail = timer;
5116 if (!d3_timer_interval) {
5117 d3_timer_timeout = clearTimeout(d3_timer_timeout);
5118 d3_timer_interval = 1;
5119 d3_timer_frame(d3_timer_step);
5120 }
5121 return timer;
5122 }
5123 function d3_timer_step() {
5124 var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
5125 if (delay > 24) {
5126 if (isFinite(delay)) {
5127 clearTimeout(d3_timer_timeout);
5128 d3_timer_timeout = setTimeout(d3_timer_step, delay);
5129 }
5130 d3_timer_interval = 0;
5131 } else {
5132 d3_timer_interval = 1;
5133 d3_timer_frame(d3_timer_step);
5134 }
5135 }
5136 d3.timer.flush = function() {
5137 d3_timer_mark();
5138 d3_timer_sweep();
5139 };
5140 function d3_timer_mark() {
5141 var now = Date.now(), timer = d3_timer_queueHead;
5142 while (timer) {
5143 if (now >= timer.t && timer.c(now - timer.t)) timer.c = null;
5144 timer = timer.n;
5145 }
5146 return now;
5147 }
5148 function d3_timer_sweep() {
5149 var t0, t1 = d3_timer_queueHead, time = Infinity;
5150 while (t1) {
5151 if (t1.c) {
5152 if (t1.t < time) time = t1.t;
5153 t1 = (t0 = t1).n;
5154 } else {
5155 t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
5156 }
5157 }
5158 d3_timer_queueTail = t0;
5159 return time;
5160 }
5161 function d3_format_precision(x, p) {
5162 return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
5163 }
5164 d3.round = function(x, n) {
5165 return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
5166 };
5167 var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
5168 d3.formatPrefix = function(value, precision) {
5169 var i = 0;
5170 if (value = +value) {
5171 if (value < 0) value *= -1;
5172 if (precision) value = d3.round(value, d3_format_precision(value, precision));
5173 i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
5174 i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
5175 }
5176 return d3_formatPrefixes[8 + i / 3];
5177 };
5178 function d3_formatPrefix(d, i) {
5179 var k = Math.pow(10, abs(8 - i) * 3);
5180 return {
5181 scale: i > 8 ? function(d) {
5182 return d / k;
5183 } : function(d) {
5184 return d * k;
5185 },
5186 symbol: d
5187 };
5188 }
5189 function d3_locale_numberFormat(locale) {
5190 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) {
5191 var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0;
5192 while (i > 0 && g > 0) {
5193 if (length + g + 1 > width) g = Math.max(1, width - length);
5194 t.push(value.substring(i -= g, i + g));
5195 if ((length += g + 1) > width) break;
5196 g = locale_grouping[j = (j + 1) % locale_grouping.length];
5197 }
5198 return t.reverse().join(locale_thousands);
5199 } : d3_identity;
5200 return function(specifier) {
5201 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;
5202 if (precision) precision = +precision.substring(1);
5203 if (zfill || fill === "0" && align === "=") {
5204 zfill = fill = "0";
5205 align = "=";
5206 }
5207 switch (type) {
5208 case "n":
5209 comma = true;
5210 type = "g";
5211 break;
5212
5213 case "%":
5214 scale = 100;
5215 suffix = "%";
5216 type = "f";
5217 break;
5218
5219 case "p":
5220 scale = 100;
5221 suffix = "%";
5222 type = "r";
5223 break;
5224
5225 case "b":
5226 case "o":
5227 case "x":
5228 case "X":
5229 if (symbol === "#") prefix = "0" + type.toLowerCase();
5230
5231 case "c":
5232 exponent = false;
5233
5234 case "d":
5235 integer = true;
5236 precision = 0;
5237 break;
5238
5239 case "s":
5240 scale = -1;
5241 type = "r";
5242 break;
5243 }
5244 if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
5245 if (type == "r" && !precision) type = "g";
5246 if (precision != null) {
5247 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));
5248 }
5249 type = d3_format_types.get(type) || d3_format_typeDefault;
5250 var zcomma = zfill && comma;
5251 return function(value) {
5252 var fullSuffix = suffix;
5253 if (integer && value % 1) return "";
5254 var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign;
5255 if (scale < 0) {
5256 var unit = d3.formatPrefix(value, precision);
5257 value = unit.scale(value);
5258 fullSuffix = unit.symbol + suffix;
5259 } else {
5260 value *= scale;
5261 }
5262 value = type(value, precision);
5263 var i = value.lastIndexOf("."), before, after;
5264 if (i < 0) {
5265 var j = exponent ? value.lastIndexOf("e") : -1;
5266 if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j);
5267 } else {
5268 before = value.substring(0, i);
5269 after = locale_decimal + value.substring(i + 1);
5270 }
5271 if (!zfill && comma) before = formatGroup(before, Infinity);
5272 var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
5273 if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity);
5274 negative += prefix;
5275 value = before + after;
5276 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;
5277 };
5278 };
5279 }
5280 var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
5281 var d3_format_types = d3.map({
5282 b: function(x) {
5283 return x.toString(2);
5284 },
5285 c: function(x) {
5286 return String.fromCharCode(x);
5287 },
5288 o: function(x) {
5289 return x.toString(8);
5290 },
5291 x: function(x) {
5292 return x.toString(16);
5293 },
5294 X: function(x) {
5295 return x.toString(16).toUpperCase();
5296 },
5297 g: function(x, p) {
5298 return x.toPrecision(p);
5299 },
5300 e: function(x, p) {
5301 return x.toExponential(p);
5302 },
5303 f: function(x, p) {
5304 return x.toFixed(p);
5305 },
5306 r: function(x, p) {
5307 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))));
5308 }
5309 });
5310 function d3_format_typeDefault(x) {
5311 return x + "";
5312 }
5313 var d3_time = d3.time = {}, d3_date = Date;
5314 function d3_date_utc() {
5315 this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
5316 }
5317 d3_date_utc.prototype = {
5318 getDate: function() {
5319 return this._.getUTCDate();
5320 },
5321 getDay: function() {
5322 return this._.getUTCDay();
5323 },
5324 getFullYear: function() {
5325 return this._.getUTCFullYear();
5326 },
5327 getHours: function() {
5328 return this._.getUTCHours();
5329 },
5330 getMilliseconds: function() {
5331 return this._.getUTCMilliseconds();
5332 },
5333 getMinutes: function() {
5334 return this._.getUTCMinutes();
5335 },
5336 getMonth: function() {
5337 return this._.getUTCMonth();
5338 },
5339 getSeconds: function() {
5340 return this._.getUTCSeconds();
5341 },
5342 getTime: function() {
5343 return this._.getTime();
5344 },
5345 getTimezoneOffset: function() {
5346 return 0;
5347 },
5348 valueOf: function() {
5349 return this._.valueOf();
5350 },
5351 setDate: function() {
5352 d3_time_prototype.setUTCDate.apply(this._, arguments);
5353 },
5354 setDay: function() {
5355 d3_time_prototype.setUTCDay.apply(this._, arguments);
5356 },
5357 setFullYear: function() {
5358 d3_time_prototype.setUTCFullYear.apply(this._, arguments);
5359 },
5360 setHours: function() {
5361 d3_time_prototype.setUTCHours.apply(this._, arguments);
5362 },
5363 setMilliseconds: function() {
5364 d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
5365 },
5366 setMinutes: function() {
5367 d3_time_prototype.setUTCMinutes.apply(this._, arguments);
5368 },
5369 setMonth: function() {
5370 d3_time_prototype.setUTCMonth.apply(this._, arguments);
5371 },
5372 setSeconds: function() {
5373 d3_time_prototype.setUTCSeconds.apply(this._, arguments);
5374 },
5375 setTime: function() {
5376 d3_time_prototype.setTime.apply(this._, arguments);
5377 }
5378 };
5379 var d3_time_prototype = Date.prototype;
5380 function d3_time_interval(local, step, number) {
5381 function round(date) {
5382 var d0 = local(date), d1 = offset(d0, 1);
5383 return date - d0 < d1 - date ? d0 : d1;
5384 }
5385 function ceil(date) {
5386 step(date = local(new d3_date(date - 1)), 1);
5387 return date;
5388 }
5389 function offset(date, k) {
5390 step(date = new d3_date(+date), k);
5391 return date;
5392 }
5393 function range(t0, t1, dt) {
5394 var time = ceil(t0), times = [];
5395 if (dt > 1) {
5396 while (time < t1) {
5397 if (!(number(time) % dt)) times.push(new Date(+time));
5398 step(time, 1);
5399 }
5400 } else {
5401 while (time < t1) times.push(new Date(+time)), step(time, 1);
5402 }
5403 return times;
5404 }
5405 function range_utc(t0, t1, dt) {
5406 try {
5407 d3_date = d3_date_utc;
5408 var utc = new d3_date_utc();
5409 utc._ = t0;
5410 return range(utc, t1, dt);
5411 } finally {
5412 d3_date = Date;
5413 }
5414 }
5415 local.floor = local;
5416 local.round = round;
5417 local.ceil = ceil;
5418 local.offset = offset;
5419 local.range = range;
5420 var utc = local.utc = d3_time_interval_utc(local);
5421 utc.floor = utc;
5422 utc.round = d3_time_interval_utc(round);
5423 utc.ceil = d3_time_interval_utc(ceil);
5424 utc.offset = d3_time_interval_utc(offset);
5425 utc.range = range_utc;
5426 return local;
5427 }
5428 function d3_time_interval_utc(method) {
5429 return function(date, k) {
5430 try {
5431 d3_date = d3_date_utc;
5432 var utc = new d3_date_utc();
5433 utc._ = date;
5434 return method(utc, k)._;
5435 } finally {
5436 d3_date = Date;
5437 }
5438 };
5439 }
5440 d3_time.year = d3_time_interval(function(date) {
5441 date = d3_time.day(date);
5442 date.setMonth(0, 1);
5443 return date;
5444 }, function(date, offset) {
5445 date.setFullYear(date.getFullYear() + offset);
5446 }, function(date) {
5447 return date.getFullYear();
5448 });
5449 d3_time.years = d3_time.year.range;
5450 d3_time.years.utc = d3_time.year.utc.range;
5451 d3_time.day = d3_time_interval(function(date) {
5452 var day = new d3_date(2e3, 0);
5453 day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
5454 return day;
5455 }, function(date, offset) {
5456 date.setDate(date.getDate() + offset);
5457 }, function(date) {
5458 return date.getDate() - 1;
5459 });
5460 d3_time.days = d3_time.day.range;
5461 d3_time.days.utc = d3_time.day.utc.range;
5462 d3_time.dayOfYear = function(date) {
5463 var year = d3_time.year(date);
5464 return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
5465 };
5466 [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
5467 i = 7 - i;
5468 var interval = d3_time[day] = d3_time_interval(function(date) {
5469 (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
5470 return date;
5471 }, function(date, offset) {
5472 date.setDate(date.getDate() + Math.floor(offset) * 7);
5473 }, function(date) {
5474 var day = d3_time.year(date).getDay();
5475 return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
5476 });
5477 d3_time[day + "s"] = interval.range;
5478 d3_time[day + "s"].utc = interval.utc.range;
5479 d3_time[day + "OfYear"] = function(date) {
5480 var day = d3_time.year(date).getDay();
5481 return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
5482 };
5483 });
5484 d3_time.week = d3_time.sunday;
5485 d3_time.weeks = d3_time.sunday.range;
5486 d3_time.weeks.utc = d3_time.sunday.utc.range;
5487 d3_time.weekOfYear = d3_time.sundayOfYear;
5488 function d3_locale_timeFormat(locale) {
5489 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;
5490 function d3_time_format(template) {
5491 var n = template.length;
5492 function format(date) {
5493 var string = [], i = -1, j = 0, c, p, f;
5494 while (++i < n) {
5495 if (template.charCodeAt(i) === 37) {
5496 string.push(template.slice(j, i));
5497 if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
5498 if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
5499 string.push(c);
5500 j = i + 1;
5501 }
5502 }
5503 string.push(template.slice(j, i));
5504 return string.join("");
5505 }
5506 format.parse = function(string) {
5507 var d = {
5508 y: 1900,
5509 m: 0,
5510 d: 1,
5511 H: 0,
5512 M: 0,
5513 S: 0,
5514 L: 0,
5515 Z: null
5516 }, i = d3_time_parse(d, template, string, 0);
5517 if (i != string.length) return null;
5518 if ("p" in d) d.H = d.H % 12 + d.p * 12;
5519 var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
5520 if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) {
5521 if (!("w" in d)) d.w = "W" in d ? 1 : 0;
5522 date.setFullYear(d.y, 0, 1);
5523 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);
5524 } else date.setFullYear(d.y, d.m, d.d);
5525 date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
5526 return localZ ? date._ : date;
5527 };
5528 format.toString = function() {
5529 return template;
5530 };
5531 return format;
5532 }
5533 function d3_time_parse(date, template, string, j) {
5534 var c, p, t, i = 0, n = template.length, m = string.length;
5535 while (i < n) {
5536 if (j >= m) return -1;
5537 c = template.charCodeAt(i++);
5538 if (c === 37) {
5539 t = template.charAt(i++);
5540 p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
5541 if (!p || (j = p(date, string, j)) < 0) return -1;
5542 } else if (c != string.charCodeAt(j++)) {
5543 return -1;
5544 }
5545 }
5546 return j;
5547 }
5548 d3_time_format.utc = function(template) {
5549 var local = d3_time_format(template);
5550 function format(date) {
5551 try {
5552 d3_date = d3_date_utc;
5553 var utc = new d3_date();
5554 utc._ = date;
5555 return local(utc);
5556 } finally {
5557 d3_date = Date;
5558 }
5559 }
5560 format.parse = function(string) {
5561 try {
5562 d3_date = d3_date_utc;
5563 var date = local.parse(string);
5564 return date && date._;
5565 } finally {
5566 d3_date = Date;
5567 }
5568 };
5569 format.toString = local.toString;
5570 return format;
5571 };
5572 d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
5573 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);
5574 locale_periods.forEach(function(p, i) {
5575 d3_time_periodLookup.set(p.toLowerCase(), i);
5576 });
5577 var d3_time_formats = {
5578 a: function(d) {
5579 return locale_shortDays[d.getDay()];
5580 },
5581 A: function(d) {
5582 return locale_days[d.getDay()];
5583 },
5584 b: function(d) {
5585 return locale_shortMonths[d.getMonth()];
5586 },
5587 B: function(d) {
5588 return locale_months[d.getMonth()];
5589 },
5590 c: d3_time_format(locale_dateTime),
5591 d: function(d, p) {
5592 return d3_time_formatPad(d.getDate(), p, 2);
5593 },
5594 e: function(d, p) {
5595 return d3_time_formatPad(d.getDate(), p, 2);
5596 },
5597 H: function(d, p) {
5598 return d3_time_formatPad(d.getHours(), p, 2);
5599 },
5600 I: function(d, p) {
5601 return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
5602 },
5603 j: function(d, p) {
5604 return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
5605 },
5606 L: function(d, p) {
5607 return d3_time_formatPad(d.getMilliseconds(), p, 3);
5608 },
5609 m: function(d, p) {
5610 return d3_time_formatPad(d.getMonth() + 1, p, 2);
5611 },
5612 M: function(d, p) {
5613 return d3_time_formatPad(d.getMinutes(), p, 2);
5614 },
5615 p: function(d) {
5616 return locale_periods[+(d.getHours() >= 12)];
5617 },
5618 S: function(d, p) {
5619 return d3_time_formatPad(d.getSeconds(), p, 2);
5620 },
5621 U: function(d, p) {
5622 return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
5623 },
5624 w: function(d) {
5625 return d.getDay();
5626 },
5627 W: function(d, p) {
5628 return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
5629 },
5630 x: d3_time_format(locale_date),
5631 X: d3_time_format(locale_time),
5632 y: function(d, p) {
5633 return d3_time_formatPad(d.getFullYear() % 100, p, 2);
5634 },
5635 Y: function(d, p) {
5636 return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
5637 },
5638 Z: d3_time_zone,
5639 "%": function() {
5640 return "%";
5641 }
5642 };
5643 var d3_time_parsers = {
5644 a: d3_time_parseWeekdayAbbrev,
5645 A: d3_time_parseWeekday,
5646 b: d3_time_parseMonthAbbrev,
5647 B: d3_time_parseMonth,
5648 c: d3_time_parseLocaleFull,
5649 d: d3_time_parseDay,
5650 e: d3_time_parseDay,
5651 H: d3_time_parseHour24,
5652 I: d3_time_parseHour24,
5653 j: d3_time_parseDayOfYear,
5654 L: d3_time_parseMilliseconds,
5655 m: d3_time_parseMonthNumber,
5656 M: d3_time_parseMinutes,
5657 p: d3_time_parseAmPm,
5658 S: d3_time_parseSeconds,
5659 U: d3_time_parseWeekNumberSunday,
5660 w: d3_time_parseWeekdayNumber,
5661 W: d3_time_parseWeekNumberMonday,
5662 x: d3_time_parseLocaleDate,
5663 X: d3_time_parseLocaleTime,
5664 y: d3_time_parseYear,
5665 Y: d3_time_parseFullYear,
5666 Z: d3_time_parseZone,
5667 "%": d3_time_parseLiteralPercent
5668 };
5669 function d3_time_parseWeekdayAbbrev(date, string, i) {
5670 d3_time_dayAbbrevRe.lastIndex = 0;
5671 var n = d3_time_dayAbbrevRe.exec(string.slice(i));
5672 return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
5673 }
5674 function d3_time_parseWeekday(date, string, i) {
5675 d3_time_dayRe.lastIndex = 0;
5676 var n = d3_time_dayRe.exec(string.slice(i));
5677 return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
5678 }
5679 function d3_time_parseMonthAbbrev(date, string, i) {
5680 d3_time_monthAbbrevRe.lastIndex = 0;
5681 var n = d3_time_monthAbbrevRe.exec(string.slice(i));
5682 return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
5683 }
5684 function d3_time_parseMonth(date, string, i) {
5685 d3_time_monthRe.lastIndex = 0;
5686 var n = d3_time_monthRe.exec(string.slice(i));
5687 return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
5688 }
5689 function d3_time_parseLocaleFull(date, string, i) {
5690 return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
5691 }
5692 function d3_time_parseLocaleDate(date, string, i) {
5693 return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
5694 }
5695 function d3_time_parseLocaleTime(date, string, i) {
5696 return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
5697 }
5698 function d3_time_parseAmPm(date, string, i) {
5699 var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
5700 return n == null ? -1 : (date.p = n, i);
5701 }
5702 return d3_time_format;
5703 }
5704 var d3_time_formatPads = {
5705 "-": "",
5706 _: " ",
5707 "0": "0"
5708 }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
5709 function d3_time_formatPad(value, fill, width) {
5710 var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
5711 return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
5712 }
5713 function d3_time_formatRe(names) {
5714 return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
5715 }
5716 function d3_time_formatLookup(names) {
5717 var map = new d3_Map(), i = -1, n = names.length;
5718 while (++i < n) map.set(names[i].toLowerCase(), i);
5719 return map;
5720 }
5721 function d3_time_parseWeekdayNumber(date, string, i) {
5722 d3_time_numberRe.lastIndex = 0;
5723 var n = d3_time_numberRe.exec(string.slice(i, i + 1));
5724 return n ? (date.w = +n[0], i + n[0].length) : -1;
5725 }
5726 function d3_time_parseWeekNumberSunday(date, string, i) {
5727 d3_time_numberRe.lastIndex = 0;
5728 var n = d3_time_numberRe.exec(string.slice(i));
5729 return n ? (date.U = +n[0], i + n[0].length) : -1;
5730 }
5731 function d3_time_parseWeekNumberMonday(date, string, i) {
5732 d3_time_numberRe.lastIndex = 0;
5733 var n = d3_time_numberRe.exec(string.slice(i));
5734 return n ? (date.W = +n[0], i + n[0].length) : -1;
5735 }
5736 function d3_time_parseFullYear(date, string, i) {
5737 d3_time_numberRe.lastIndex = 0;
5738 var n = d3_time_numberRe.exec(string.slice(i, i + 4));
5739 return n ? (date.y = +n[0], i + n[0].length) : -1;
5740 }
5741 function d3_time_parseYear(date, string, i) {
5742 d3_time_numberRe.lastIndex = 0;
5743 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5744 return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
5745 }
5746 function d3_time_parseZone(date, string, i) {
5747 return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string,
5748 i + 5) : -1;
5749 }
5750 function d3_time_expandYear(d) {
5751 return d + (d > 68 ? 1900 : 2e3);
5752 }
5753 function d3_time_parseMonthNumber(date, string, i) {
5754 d3_time_numberRe.lastIndex = 0;
5755 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5756 return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
5757 }
5758 function d3_time_parseDay(date, string, i) {
5759 d3_time_numberRe.lastIndex = 0;
5760 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5761 return n ? (date.d = +n[0], i + n[0].length) : -1;
5762 }
5763 function d3_time_parseDayOfYear(date, string, i) {
5764 d3_time_numberRe.lastIndex = 0;
5765 var n = d3_time_numberRe.exec(string.slice(i, i + 3));
5766 return n ? (date.j = +n[0], i + n[0].length) : -1;
5767 }
5768 function d3_time_parseHour24(date, string, i) {
5769 d3_time_numberRe.lastIndex = 0;
5770 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5771 return n ? (date.H = +n[0], i + n[0].length) : -1;
5772 }
5773 function d3_time_parseMinutes(date, string, i) {
5774 d3_time_numberRe.lastIndex = 0;
5775 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5776 return n ? (date.M = +n[0], i + n[0].length) : -1;
5777 }
5778 function d3_time_parseSeconds(date, string, i) {
5779 d3_time_numberRe.lastIndex = 0;
5780 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
5781 return n ? (date.S = +n[0], i + n[0].length) : -1;
5782 }
5783 function d3_time_parseMilliseconds(date, string, i) {
5784 d3_time_numberRe.lastIndex = 0;
5785 var n = d3_time_numberRe.exec(string.slice(i, i + 3));
5786 return n ? (date.L = +n[0], i + n[0].length) : -1;
5787 }
5788 function d3_time_zone(d) {
5789 var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
5790 return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
5791 }
5792 function d3_time_parseLiteralPercent(date, string, i) {
5793 d3_time_percentRe.lastIndex = 0;
5794 var n = d3_time_percentRe.exec(string.slice(i, i + 1));
5795 return n ? i + n[0].length : -1;
5796 }
5797 function d3_time_formatMulti(formats) {
5798 var n = formats.length, i = -1;
5799 while (++i < n) formats[i][0] = this(formats[i][0]);
5800 return function(date) {
5801 var i = 0, f = formats[i];
5802 while (!f[1](date)) f = formats[++i];
5803 return f[0](date);
5804 };
5805 }
5806 d3.locale = function(locale) {
5807 return {
5808 numberFormat: d3_locale_numberFormat(locale),
5809 timeFormat: d3_locale_timeFormat(locale)
5810 };
5811 };
5812 var d3_locale_enUS = d3.locale({
5813 decimal: ".",
5814 thousands: ",",
5815 grouping: [ 3 ],
5816 currency: [ "$", "" ],
5817 dateTime: "%a %b %e %X %Y",
5818 date: "%m/%d/%Y",
5819 time: "%H:%M:%S",
5820 periods: [ "AM", "PM" ],
5821 days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
5822 shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
5823 months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
5824 shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
5825 });
5826 d3.format = d3_locale_enUS.numberFormat;
5827 d3.geo = {};
5828 function d3_adder() {}
5829 d3_adder.prototype = {
5830 s: 0,
5831 t: 0,
5832 add: function(y) {
5833 d3_adderSum(y, this.t, d3_adderTemp);
5834 d3_adderSum(d3_adderTemp.s, this.s, this);
5835 if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
5836 },
5837 reset: function() {
5838 this.s = this.t = 0;
5839 },
5840 valueOf: function() {
5841 return this.s;
5842 }
5843 };
5844 var d3_adderTemp = new d3_adder();
5845 function d3_adderSum(a, b, o) {
5846 var x = o.s = a + b, bv = x - a, av = x - bv;
5847 o.t = a - av + (b - bv);
5848 }
5849 d3.geo.stream = function(object, listener) {
5850 if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
5851 d3_geo_streamObjectType[object.type](object, listener);
5852 } else {
5853 d3_geo_streamGeometry(object, listener);
5854 }
5855 };
5856 function d3_geo_streamGeometry(geometry, listener) {
5857 if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
5858 d3_geo_streamGeometryType[geometry.type](geometry, listener);
5859 }
5860 }
5861 var d3_geo_streamObjectType = {
5862 Feature: function(feature, listener) {
5863 d3_geo_streamGeometry(feature.geometry, listener);
5864 },
5865 FeatureCollection: function(object, listener) {
5866 var features = object.features, i = -1, n = features.length;
5867 while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
5868 }
5869 };
5870 var d3_geo_streamGeometryType = {
5871 Sphere: function(object, listener) {
5872 listener.sphere();
5873 },
5874 Point: function(object, listener) {
5875 object = object.coordinates;
5876 listener.point(object[0], object[1], object[2]);
5877 },
5878 MultiPoint: function(object, listener) {
5879 var coordinates = object.coordinates, i = -1, n = coordinates.length;
5880 while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
5881 },
5882 LineString: function(object, listener) {
5883 d3_geo_streamLine(object.coordinates, listener, 0);
5884 },
5885 MultiLineString: function(object, listener) {
5886 var coordinates = object.coordinates, i = -1, n = coordinates.length;
5887 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
5888 },
5889 Polygon: function(object, listener) {
5890 d3_geo_streamPolygon(object.coordinates, listener);
5891 },
5892 MultiPolygon: function(object, listener) {
5893 var coordinates = object.coordinates, i = -1, n = coordinates.length;
5894 while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
5895 },
5896 GeometryCollection: function(object, listener) {
5897 var geometries = object.geometries, i = -1, n = geometries.length;
5898 while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
5899 }
5900 };
5901 function d3_geo_streamLine(coordinates, listener, closed) {
5902 var i = -1, n = coordinates.length - closed, coordinate;
5903 listener.lineStart();
5904 while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
5905 listener.lineEnd();
5906 }
5907 function d3_geo_streamPolygon(coordinates, listener) {
5908 var i = -1, n = coordinates.length;
5909 listener.polygonStart();
5910 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
5911 listener.polygonEnd();
5912 }
5913 d3.geo.area = function(object) {
5914 d3_geo_areaSum = 0;
5915 d3.geo.stream(object, d3_geo_area);
5916 return d3_geo_areaSum;
5917 };
5918 var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
5919 var d3_geo_area = {
5920 sphere: function() {
5921 d3_geo_areaSum += 4 * π;
5922 },
5923 point: d3_noop,
5924 lineStart: d3_noop,
5925 lineEnd: d3_noop,
5926 polygonStart: function() {
5927 d3_geo_areaRingSum.reset();
5928 d3_geo_area.lineStart = d3_geo_areaRingStart;
5929 },
5930 polygonEnd: function() {
5931 var area = 2 * d3_geo_areaRingSum;
5932 d3_geo_areaSum += area < 0 ? 4 * π + area : area;
5933 d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
5934 }
5935 };
5936 function d3_geo_areaRingStart() {
5937 var λ00, φ00, λ0, cosφ0, sinφ0;
5938 d3_geo_area.point = function(λ, φ) {
5939 d3_geo_area.point = nextPoint;
5940 λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4),
5941 sinφ0 = Math.sin(φ);
5942 };
5943 function nextPoint(λ, φ) {
5944 λ *= d3_radians;
5945 φ = φ * d3_radians / 2 + π / 4;
5946 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λ);
5947 d3_geo_areaRingSum.add(Math.atan2(v, u));
5948 λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
5949 }
5950 d3_geo_area.lineEnd = function() {
5951 nextPoint(λ00, φ00);
5952 };
5953 }
5954 function d3_geo_cartesian(spherical) {
5955 var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
5956 return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
5957 }
5958 function d3_geo_cartesianDot(a, b) {
5959 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
5960 }
5961 function d3_geo_cartesianCross(a, b) {
5962 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] ];
5963 }
5964 function d3_geo_cartesianAdd(a, b) {
5965 a[0] += b[0];
5966 a[1] += b[1];
5967 a[2] += b[2];
5968 }
5969 function d3_geo_cartesianScale(vector, k) {
5970 return [ vector[0] * k, vector[1] * k, vector[2] * k ];
5971 }
5972 function d3_geo_cartesianNormalize(d) {
5973 var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
5974 d[0] /= l;
5975 d[1] /= l;
5976 d[2] /= l;
5977 }
5978 function d3_geo_spherical(cartesian) {
5979 return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
5980 }
5981 function d3_geo_sphericalEqual(a, b) {
5982 return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
5983 }
5984 d3.geo.bounds = function() {
5985 var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
5986 var bound = {
5987 point: point,
5988 lineStart: lineStart,
5989 lineEnd: lineEnd,
5990 polygonStart: function() {
5991 bound.point = ringPoint;
5992 bound.lineStart = ringStart;
5993 bound.lineEnd = ringEnd;
5994 dλSum = 0;
5995 d3_geo_area.polygonStart();
5996 },
5997 polygonEnd: function() {
5998 d3_geo_area.polygonEnd();
5999 bound.point = point;
6000 bound.lineStart = lineStart;
6001 bound.lineEnd = lineEnd;
6002 if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
6003 range[0] = λ0, range[1] = λ1;
6004 }
6005 };
6006 function point(λ, φ) {
6007 ranges.push(range = [ λ0 = λ, λ1 = λ ]);
6008 if (φ < φ0) φ0 = φ;
6009 if (φ > φ1) φ1 = φ;
6010 }
6011 function linePoint(λ, φ) {
6012 var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
6013 if (p0) {
6014 var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
6015 d3_geo_cartesianNormalize(inflection);
6016 inflection = d3_geo_spherical(inflection);
6017 var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
6018 if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
6019 var φi = inflection[1] * d3_degrees;
6020 if (φi > φ1) φ1 = φi;
6021 } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
6022 var φi = -inflection[1] * d3_degrees;
6023 if (φi < φ0) φ0 = φi;
6024 } else {
6025 if (φ < φ0) φ0 = φ;
6026 if (φ > φ1) φ1 = φ;
6027 }
6028 if (antimeridian) {
6029 if (λ < λ_) {
6030 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
6031 } else {
6032 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
6033 }
6034 } else {
6035 if (λ1 >= λ0) {
6036 if (λ < λ0) λ0 = λ;
6037 if (λ > λ1) λ1 = λ;
6038 } else {
6039 if (λ > λ_) {
6040 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
6041 } else {
6042 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
6043 }
6044 }
6045 }
6046 } else {
6047 point(λ, φ);
6048 }
6049 p0 = p, λ_ = λ;
6050 }
6051 function lineStart() {
6052 bound.point = linePoint;
6053 }
6054 function lineEnd() {
6055 range[0] = λ0, range[1] = λ1;
6056 bound.point = point;
6057 p0 = null;
6058 }
6059 function ringPoint(λ, φ) {
6060 if (p0) {
6061 var dλ = λ - λ_;
6062 dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
6063 } else λ__ = λ, φ__ = φ;
6064 d3_geo_area.point(λ, φ);
6065 linePoint(λ, φ);
6066 }
6067 function ringStart() {
6068 d3_geo_area.lineStart();
6069 }
6070 function ringEnd() {
6071 ringPoint(λ__, φ__);
6072 d3_geo_area.lineEnd();
6073 if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
6074 range[0] = λ0, range[1] = λ1;
6075 p0 = null;
6076 }
6077 function angle(λ0, λ1) {
6078 return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
6079 }
6080 function compareRanges(a, b) {
6081 return a[0] - b[0];
6082 }
6083 function withinRange(x, range) {
6084 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
6085 }
6086 return function(feature) {
6087 φ1 = λ1 = -(λ0 = φ0 = Infinity);
6088 ranges = [];
6089 d3.geo.stream(feature, bound);
6090 var n = ranges.length;
6091 if (n) {
6092 ranges.sort(compareRanges);
6093 for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
6094 b = ranges[i];
6095 if (withinRange(b[0], a) || withinRange(b[1], a)) {
6096 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
6097 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
6098 } else {
6099 merged.push(a = b);
6100 }
6101 }
6102 var best = -Infinity, dλ;
6103 for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
6104 b = merged[i];
6105 if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
6106 }
6107 }
6108 ranges = range = null;
6109 return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
6110 };
6111 }();
6112 d3.geo.centroid = function(object) {
6113 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;
6114 d3.geo.stream(object, d3_geo_centroid);
6115 var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
6116 if (m < ε2) {
6117 x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
6118 if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
6119 m = x * x + y * y + z * z;
6120 if (m < ε2) return [ NaN, NaN ];
6121 }
6122 return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
6123 };
6124 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;
6125 var d3_geo_centroid = {
6126 sphere: d3_noop,
6127 point: d3_geo_centroidPoint,
6128 lineStart: d3_geo_centroidLineStart,
6129 lineEnd: d3_geo_centroidLineEnd,
6130 polygonStart: function() {
6131 d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
6132 },
6133 polygonEnd: function() {
6134 d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
6135 }
6136 };
6137 function d3_geo_centroidPoint(λ, φ) {
6138 λ *= d3_radians;
6139 var cosφ = Math.cos(φ *= d3_radians);
6140 d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
6141 }
6142 function d3_geo_centroidPointXYZ(x, y, z) {
6143 ++d3_geo_centroidW0;
6144 d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
6145 d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
6146 d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
6147 }
6148 function d3_geo_centroidLineStart() {
6149 var x0, y0, z0;
6150 d3_geo_centroid.point = function(λ, φ) {
6151 λ *= d3_radians;
6152 var cosφ = Math.cos(φ *= d3_radians);
6153 x0 = cosφ * Math.cos(λ);
6154 y0 = cosφ * Math.sin(λ);
6155 z0 = Math.sin(φ);
6156 d3_geo_centroid.point = nextPoint;
6157 d3_geo_centroidPointXYZ(x0, y0, z0);
6158 };
6159 function nextPoint(λ, φ) {
6160 λ *= d3_radians;
6161 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);
6162 d3_geo_centroidW1 += w;
6163 d3_geo_centroidX1 += w * (x0 + (x0 = x));
6164 d3_geo_centroidY1 += w * (y0 + (y0 = y));
6165 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
6166 d3_geo_centroidPointXYZ(x0, y0, z0);
6167 }
6168 }
6169 function d3_geo_centroidLineEnd() {
6170 d3_geo_centroid.point = d3_geo_centroidPoint;
6171 }
6172 function d3_geo_centroidRingStart() {
6173 var λ00, φ00, x0, y0, z0;
6174 d3_geo_centroid.point = function(λ, φ) {
6175 λ00 = λ, φ00 = φ;
6176 d3_geo_centroid.point = nextPoint;
6177 λ *= d3_radians;
6178 var cosφ = Math.cos(φ *= d3_radians);
6179 x0 = cosφ * Math.cos(λ);
6180 y0 = cosφ * Math.sin(λ);
6181 z0 = Math.sin(φ);
6182 d3_geo_centroidPointXYZ(x0, y0, z0);
6183 };
6184 d3_geo_centroid.lineEnd = function() {
6185 nextPoint(λ00, φ00);
6186 d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
6187 d3_geo_centroid.point = d3_geo_centroidPoint;
6188 };
6189 function nextPoint(λ, φ) {
6190 λ *= d3_radians;
6191 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);
6192 d3_geo_centroidX2 += v * cx;
6193 d3_geo_centroidY2 += v * cy;
6194 d3_geo_centroidZ2 += v * cz;
6195 d3_geo_centroidW1 += w;
6196 d3_geo_centroidX1 += w * (x0 + (x0 = x));
6197 d3_geo_centroidY1 += w * (y0 + (y0 = y));
6198 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
6199 d3_geo_centroidPointXYZ(x0, y0, z0);
6200 }
6201 }
6202 function d3_geo_compose(a, b) {
6203 function compose(x, y) {
6204 return x = a(x, y), b(x[0], x[1]);
6205 }
6206 if (a.invert && b.invert) compose.invert = function(x, y) {
6207 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
6208 };
6209 return compose;
6210 }
6211 function d3_true() {
6212 return true;
6213 }
6214 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
6215 var subject = [], clip = [];
6216 segments.forEach(function(segment) {
6217 if ((n = segment.length - 1) <= 0) return;
6218 var n, p0 = segment[0], p1 = segment[n];
6219 if (d3_geo_sphericalEqual(p0, p1)) {
6220 listener.lineStart();
6221 for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
6222 listener.lineEnd();
6223 return;
6224 }
6225 var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
6226 a.o = b;
6227 subject.push(a);
6228 clip.push(b);
6229 a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
6230 b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
6231 a.o = b;
6232 subject.push(a);
6233 clip.push(b);
6234 });
6235 clip.sort(compare);
6236 d3_geo_clipPolygonLinkCircular(subject);
6237 d3_geo_clipPolygonLinkCircular(clip);
6238 if (!subject.length) return;
6239 for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
6240 clip[i].e = entry = !entry;
6241 }
6242 var start = subject[0], points, point;
6243 while (1) {
6244 var current = start, isSubject = true;
6245 while (current.v) if ((current = current.n) === start) return;
6246 points = current.z;
6247 listener.lineStart();
6248 do {
6249 current.v = current.o.v = true;
6250 if (current.e) {
6251 if (isSubject) {
6252 for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
6253 } else {
6254 interpolate(current.x, current.n.x, 1, listener);
6255 }
6256 current = current.n;
6257 } else {
6258 if (isSubject) {
6259 points = current.p.z;
6260 for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
6261 } else {
6262 interpolate(current.x, current.p.x, -1, listener);
6263 }
6264 current = current.p;
6265 }
6266 current = current.o;
6267 points = current.z;
6268 isSubject = !isSubject;
6269 } while (!current.v);
6270 listener.lineEnd();
6271 }
6272 }
6273 function d3_geo_clipPolygonLinkCircular(array) {
6274 if (!(n = array.length)) return;
6275 var n, i = 0, a = array[0], b;
6276 while (++i < n) {
6277 a.n = b = array[i];
6278 b.p = a;
6279 a = b;
6280 }
6281 a.n = b = array[0];
6282 b.p = a;
6283 }
6284 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
6285 this.x = point;
6286 this.z = points;
6287 this.o = other;
6288 this.e = entry;
6289 this.v = false;
6290 this.n = this.p = null;
6291 }
6292 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
6293 return function(rotate, listener) {
6294 var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
6295 var clip = {
6296 point: point,
6297 lineStart: lineStart,
6298 lineEnd: lineEnd,
6299 polygonStart: function() {
6300 clip.point = pointRing;
6301 clip.lineStart = ringStart;
6302 clip.lineEnd = ringEnd;
6303 segments = [];
6304 polygon = [];
6305 },
6306 polygonEnd: function() {
6307 clip.point = point;
6308 clip.lineStart = lineStart;
6309 clip.lineEnd = lineEnd;
6310 segments = d3.merge(segments);
6311 var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
6312 if (segments.length) {
6313 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
6314 d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
6315 } else if (clipStartInside) {
6316 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
6317 listener.lineStart();
6318 interpolate(null, null, 1, listener);
6319 listener.lineEnd();
6320 }
6321 if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
6322 segments = polygon = null;
6323 },
6324 sphere: function() {
6325 listener.polygonStart();
6326 listener.lineStart();
6327 interpolate(null, null, 1, listener);
6328 listener.lineEnd();
6329 listener.polygonEnd();
6330 }
6331 };
6332 function point(λ, φ) {
6333 var point = rotate(λ, φ);
6334 if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
6335 }
6336 function pointLine(λ, φ) {
6337 var point = rotate(λ, φ);
6338 line.point(point[0], point[1]);
6339 }
6340 function lineStart() {
6341 clip.point = pointLine;
6342 line.lineStart();
6343 }
6344 function lineEnd() {
6345 clip.point = point;
6346 line.lineEnd();
6347 }
6348 var segments;
6349 var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring;
6350 function pointRing(λ, φ) {
6351 ring.push([ λ, φ ]);
6352 var point = rotate(λ, φ);
6353 ringListener.point(point[0], point[1]);
6354 }
6355 function ringStart() {
6356 ringListener.lineStart();
6357 ring = [];
6358 }
6359 function ringEnd() {
6360 pointRing(ring[0][0], ring[0][1]);
6361 ringListener.lineEnd();
6362 var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
6363 ring.pop();
6364 polygon.push(ring);
6365 ring = null;
6366 if (!n) return;
6367 if (clean & 1) {
6368 segment = ringSegments[0];
6369 var n = segment.length - 1, i = -1, point;
6370 if (n > 0) {
6371 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
6372 listener.lineStart();
6373 while (++i < n) listener.point((point = segment[i])[0], point[1]);
6374 listener.lineEnd();
6375 }
6376 return;
6377 }
6378 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
6379 segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
6380 }
6381 return clip;
6382 };
6383 }
6384 function d3_geo_clipSegmentLength1(segment) {
6385 return segment.length > 1;
6386 }
6387 function d3_geo_clipBufferListener() {
6388 var lines = [], line;
6389 return {
6390 lineStart: function() {
6391 lines.push(line = []);
6392 },
6393 point: function(λ, φ) {
6394 line.push([ λ, φ ]);
6395 },
6396 lineEnd: d3_noop,
6397 buffer: function() {
6398 var buffer = lines;
6399 lines = [];
6400 line = null;
6401 return buffer;
6402 },
6403 rejoin: function() {
6404 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
6405 }
6406 };
6407 }
6408 function d3_geo_clipSort(a, b) {
6409 return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
6410 }
6411 var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
6412 function d3_geo_clipAntimeridianLine(listener) {
6413 var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
6414 return {
6415 lineStart: function() {
6416 listener.lineStart();
6417 clean = 1;
6418 },
6419 point: function(λ1, φ1) {
6420 var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
6421 if (abs(dλ - π) < ε) {
6422 listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
6423 listener.point(sλ0, φ0);
6424 listener.lineEnd();
6425 listener.lineStart();
6426 listener.point(sλ1, φ0);
6427 listener.point(λ1, φ0);
6428 clean = 0;
6429 } else if (sλ0 !== sλ1 && dλ >= π) {
6430 if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
6431 if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
6432 φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
6433 listener.point(sλ0, φ0);
6434 listener.lineEnd();
6435 listener.lineStart();
6436 listener.point(sλ1, φ0);
6437 clean = 0;
6438 }
6439 listener.point(λ0 = λ1, φ0 = φ1);
6440 sλ0 = sλ1;
6441 },
6442 lineEnd: function() {
6443 listener.lineEnd();
6444 λ0 = φ0 = NaN;
6445 },
6446 clean: function() {
6447 return 2 - clean;
6448 }
6449 };
6450 }
6451 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
6452 var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
6453 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;
6454 }
6455 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
6456 var φ;
6457 if (from == null) {
6458 φ = direction * halfπ;
6459 listener.point(-π, φ);
6460 listener.point(0, φ);
6461 listener.point(π, φ);
6462 listener.point(π, 0);
6463 listener.point(π, -φ);
6464 listener.point(0, -φ);
6465 listener.point(-π, -φ);
6466 listener.point(-π, 0);
6467 listener.point(-π, φ);
6468 } else if (abs(from[0] - to[0]) > ε) {
6469 var s = from[0] < to[0] ? π : -π;
6470 φ = direction * s / 2;
6471 listener.point(-s, φ);
6472 listener.point(0, φ);
6473 listener.point(s, φ);
6474 } else {
6475 listener.point(to[0], to[1]);
6476 }
6477 }
6478 function d3_geo_pointInPolygon(point, polygon) {
6479 var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
6480 d3_geo_areaRingSum.reset();
6481 for (var i = 0, n = polygon.length; i < n; ++i) {
6482 var ring = polygon[i], m = ring.length;
6483 if (!m) continue;
6484 var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
6485 while (true) {
6486 if (j === m) j = 0;
6487 point = ring[j];
6488 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φ;
6489 d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
6490 polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
6491 if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
6492 var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
6493 d3_geo_cartesianNormalize(arc);
6494 var intersection = d3_geo_cartesianCross(meridianNormal, arc);
6495 d3_geo_cartesianNormalize(intersection);
6496 var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
6497 if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
6498 winding += antimeridian ^ dλ >= 0 ? 1 : -1;
6499 }
6500 }
6501 if (!j++) break;
6502 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
6503 }
6504 }
6505 return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1;
6506 }
6507 function d3_geo_clipCircle(radius) {
6508 var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
6509 return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
6510 function visible(λ, φ) {
6511 return Math.cos(λ) * Math.cos(φ) > cr;
6512 }
6513 function clipLine(listener) {
6514 var point0, c0, v0, v00, clean;
6515 return {
6516 lineStart: function() {
6517 v00 = v0 = false;
6518 clean = 1;
6519 },
6520 point: function(λ, φ) {
6521 var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
6522 if (!point0 && (v00 = v0 = v)) listener.lineStart();
6523 if (v !== v0) {
6524 point2 = intersect(point0, point1);
6525 if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
6526 point1[0] += ε;
6527 point1[1] += ε;
6528 v = visible(point1[0], point1[1]);
6529 }
6530 }
6531 if (v !== v0) {
6532 clean = 0;
6533 if (v) {
6534 listener.lineStart();
6535 point2 = intersect(point1, point0);
6536 listener.point(point2[0], point2[1]);
6537 } else {
6538 point2 = intersect(point0, point1);
6539 listener.point(point2[0], point2[1]);
6540 listener.lineEnd();
6541 }
6542 point0 = point2;
6543 } else if (notHemisphere && point0 && smallRadius ^ v) {
6544 var t;
6545 if (!(c & c0) && (t = intersect(point1, point0, true))) {
6546 clean = 0;
6547 if (smallRadius) {
6548 listener.lineStart();
6549 listener.point(t[0][0], t[0][1]);
6550 listener.point(t[1][0], t[1][1]);
6551 listener.lineEnd();
6552 } else {
6553 listener.point(t[1][0], t[1][1]);
6554 listener.lineEnd();
6555 listener.lineStart();
6556 listener.point(t[0][0], t[0][1]);
6557 }
6558 }
6559 }
6560 if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
6561 listener.point(point1[0], point1[1]);
6562 }
6563 point0 = point1, v0 = v, c0 = c;
6564 },
6565 lineEnd: function() {
6566 if (v0) listener.lineEnd();
6567 point0 = null;
6568 },
6569 clean: function() {
6570 return clean | (v00 && v0) << 1;
6571 }
6572 };
6573 }
6574 function intersect(a, b, two) {
6575 var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
6576 var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
6577 if (!determinant) return !two && a;
6578 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);
6579 d3_geo_cartesianAdd(A, B);
6580 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);
6581 if (t2 < 0) return;
6582 var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
6583 d3_geo_cartesianAdd(q, A);
6584 q = d3_geo_spherical(q);
6585 if (!two) return q;
6586 var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
6587 if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
6588 var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
6589 if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
6590 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)) {
6591 var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
6592 d3_geo_cartesianAdd(q1, A);
6593 return [ q, d3_geo_spherical(q1) ];
6594 }
6595 }
6596 function code(λ, φ) {
6597 var r = smallRadius ? radius : π - radius, code = 0;
6598 if (λ < -r) code |= 1; else if (λ > r) code |= 2;
6599 if (φ < -r) code |= 4; else if (φ > r) code |= 8;
6600 return code;
6601 }
6602 }
6603 function d3_geom_clipLine(x0, y0, x1, y1) {
6604 return function(line) {
6605 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;
6606 r = x0 - ax;
6607 if (!dx && r > 0) return;
6608 r /= dx;
6609 if (dx < 0) {
6610 if (r < t0) return;
6611 if (r < t1) t1 = r;
6612 } else if (dx > 0) {
6613 if (r > t1) return;
6614 if (r > t0) t0 = r;
6615 }
6616 r = x1 - ax;
6617 if (!dx && r < 0) return;
6618 r /= dx;
6619 if (dx < 0) {
6620 if (r > t1) return;
6621 if (r > t0) t0 = r;
6622 } else if (dx > 0) {
6623 if (r < t0) return;
6624 if (r < t1) t1 = r;
6625 }
6626 r = y0 - ay;
6627 if (!dy && r > 0) return;
6628 r /= dy;
6629 if (dy < 0) {
6630 if (r < t0) return;
6631 if (r < t1) t1 = r;
6632 } else if (dy > 0) {
6633 if (r > t1) return;
6634 if (r > t0) t0 = r;
6635 }
6636 r = y1 - ay;
6637 if (!dy && r < 0) return;
6638 r /= dy;
6639 if (dy < 0) {
6640 if (r > t1) return;
6641 if (r > t0) t0 = r;
6642 } else if (dy > 0) {
6643 if (r < t0) return;
6644 if (r < t1) t1 = r;
6645 }
6646 if (t0 > 0) line.a = {
6647 x: ax + t0 * dx,
6648 y: ay + t0 * dy
6649 };
6650 if (t1 < 1) line.b = {
6651 x: ax + t1 * dx,
6652 y: ay + t1 * dy
6653 };
6654 return line;
6655 };
6656 }
6657 var d3_geo_clipExtentMAX = 1e9;
6658 d3.geo.clipExtent = function() {
6659 var x0, y0, x1, y1, stream, clip, clipExtent = {
6660 stream: function(output) {
6661 if (stream) stream.valid = false;
6662 stream = clip(output);
6663 stream.valid = true;
6664 return stream;
6665 },
6666 extent: function(_) {
6667 if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
6668 clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
6669 if (stream) stream.valid = false, stream = null;
6670 return clipExtent;
6671 }
6672 };
6673 return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
6674 };
6675 function d3_geo_clipExtent(x0, y0, x1, y1) {
6676 return function(listener) {
6677 var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
6678 var clip = {
6679 point: point,
6680 lineStart: lineStart,
6681 lineEnd: lineEnd,
6682 polygonStart: function() {
6683 listener = bufferListener;
6684 segments = [];
6685 polygon = [];
6686 clean = true;
6687 },
6688 polygonEnd: function() {
6689 listener = listener_;
6690 segments = d3.merge(segments);
6691 var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
6692 if (inside || visible) {
6693 listener.polygonStart();
6694 if (inside) {
6695 listener.lineStart();
6696 interpolate(null, null, 1, listener);
6697 listener.lineEnd();
6698 }
6699 if (visible) {
6700 d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
6701 }
6702 listener.polygonEnd();
6703 }
6704 segments = polygon = ring = null;
6705 }
6706 };
6707 function insidePolygon(p) {
6708 var wn = 0, n = polygon.length, y = p[1];
6709 for (var i = 0; i < n; ++i) {
6710 for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
6711 b = v[j];
6712 if (a[1] <= y) {
6713 if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
6714 } else {
6715 if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
6716 }
6717 a = b;
6718 }
6719 }
6720 return wn !== 0;
6721 }
6722 function interpolate(from, to, direction, listener) {
6723 var a = 0, a1 = 0;
6724 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
6725 do {
6726 listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
6727 } while ((a = (a + direction + 4) % 4) !== a1);
6728 } else {
6729 listener.point(to[0], to[1]);
6730 }
6731 }
6732 function pointVisible(x, y) {
6733 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
6734 }
6735 function point(x, y) {
6736 if (pointVisible(x, y)) listener.point(x, y);
6737 }
6738 var x__, y__, v__, x_, y_, v_, first, clean;
6739 function lineStart() {
6740 clip.point = linePoint;
6741 if (polygon) polygon.push(ring = []);
6742 first = true;
6743 v_ = false;
6744 x_ = y_ = NaN;
6745 }
6746 function lineEnd() {
6747 if (segments) {
6748 linePoint(x__, y__);
6749 if (v__ && v_) bufferListener.rejoin();
6750 segments.push(bufferListener.buffer());
6751 }
6752 clip.point = point;
6753 if (v_) listener.lineEnd();
6754 }
6755 function linePoint(x, y) {
6756 x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
6757 y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
6758 var v = pointVisible(x, y);
6759 if (polygon) ring.push([ x, y ]);
6760 if (first) {
6761 x__ = x, y__ = y, v__ = v;
6762 first = false;
6763 if (v) {
6764 listener.lineStart();
6765 listener.point(x, y);
6766 }
6767 } else {
6768 if (v && v_) listener.point(x, y); else {
6769 var l = {
6770 a: {
6771 x: x_,
6772 y: y_
6773 },
6774 b: {
6775 x: x,
6776 y: y
6777 }
6778 };
6779 if (clipLine(l)) {
6780 if (!v_) {
6781 listener.lineStart();
6782 listener.point(l.a.x, l.a.y);
6783 }
6784 listener.point(l.b.x, l.b.y);
6785 if (!v) listener.lineEnd();
6786 clean = false;
6787 } else if (v) {
6788 listener.lineStart();
6789 listener.point(x, y);
6790 clean = false;
6791 }
6792 }
6793 }
6794 x_ = x, y_ = y, v_ = v;
6795 }
6796 return clip;
6797 };
6798 function corner(p, direction) {
6799 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;
6800 }
6801 function compare(a, b) {
6802 return comparePoints(a.x, b.x);
6803 }
6804 function comparePoints(a, b) {
6805 var ca = corner(a, 1), cb = corner(b, 1);
6806 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];
6807 }
6808 }
6809 function d3_geo_conic(projectAt) {
6810 var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
6811 p.parallels = function(_) {
6812 if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
6813 return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
6814 };
6815 return p;
6816 }
6817 function d3_geo_conicEqualArea(φ0, φ1) {
6818 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;
6819 function forward(λ, φ) {
6820 var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
6821 return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
6822 }
6823 forward.invert = function(x, y) {
6824 var ρ0_y = ρ0 - y;
6825 return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
6826 };
6827 return forward;
6828 }
6829 (d3.geo.conicEqualArea = function() {
6830 return d3_geo_conic(d3_geo_conicEqualArea);
6831 }).raw = d3_geo_conicEqualArea;
6832 d3.geo.albers = function() {
6833 return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
6834 };
6835 d3.geo.albersUsa = function() {
6836 var lower48 = d3.geo.albers();
6837 var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
6838 var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
6839 var point, pointStream = {
6840 point: function(x, y) {
6841 point = [ x, y ];
6842 }
6843 }, lower48Point, alaskaPoint, hawaiiPoint;
6844 function albersUsa(coordinates) {
6845 var x = coordinates[0], y = coordinates[1];
6846 point = null;
6847 (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
6848 return point;
6849 }
6850 albersUsa.invert = function(coordinates) {
6851 var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
6852 return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
6853 };
6854 albersUsa.stream = function(stream) {
6855 var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
6856 return {
6857 point: function(x, y) {
6858 lower48Stream.point(x, y);
6859 alaskaStream.point(x, y);
6860 hawaiiStream.point(x, y);
6861 },
6862 sphere: function() {
6863 lower48Stream.sphere();
6864 alaskaStream.sphere();
6865 hawaiiStream.sphere();
6866 },
6867 lineStart: function() {
6868 lower48Stream.lineStart();
6869 alaskaStream.lineStart();
6870 hawaiiStream.lineStart();
6871 },
6872 lineEnd: function() {
6873 lower48Stream.lineEnd();
6874 alaskaStream.lineEnd();
6875 hawaiiStream.lineEnd();
6876 },
6877 polygonStart: function() {
6878 lower48Stream.polygonStart();
6879 alaskaStream.polygonStart();
6880 hawaiiStream.polygonStart();
6881 },
6882 polygonEnd: function() {
6883 lower48Stream.polygonEnd();
6884 alaskaStream.polygonEnd();
6885 hawaiiStream.polygonEnd();
6886 }
6887 };
6888 };
6889 albersUsa.precision = function(_) {
6890 if (!arguments.length) return lower48.precision();
6891 lower48.precision(_);
6892 alaska.precision(_);
6893 hawaii.precision(_);
6894 return albersUsa;
6895 };
6896 albersUsa.scale = function(_) {
6897 if (!arguments.length) return lower48.scale();
6898 lower48.scale(_);
6899 alaska.scale(_ * .35);
6900 hawaii.scale(_);
6901 return albersUsa.translate(lower48.translate());
6902 };
6903 albersUsa.translate = function(_) {
6904 if (!arguments.length) return lower48.translate();
6905 var k = lower48.scale(), x = +_[0], y = +_[1];
6906 lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
6907 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;
6908 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;
6909 return albersUsa;
6910 };
6911 return albersUsa.scale(1070);
6912 };
6913 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
6914 point: d3_noop,
6915 lineStart: d3_noop,
6916 lineEnd: d3_noop,
6917 polygonStart: function() {
6918 d3_geo_pathAreaPolygon = 0;
6919 d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
6920 },
6921 polygonEnd: function() {
6922 d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
6923 d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
6924 }
6925 };
6926 function d3_geo_pathAreaRingStart() {
6927 var x00, y00, x0, y0;
6928 d3_geo_pathArea.point = function(x, y) {
6929 d3_geo_pathArea.point = nextPoint;
6930 x00 = x0 = x, y00 = y0 = y;
6931 };
6932 function nextPoint(x, y) {
6933 d3_geo_pathAreaPolygon += y0 * x - x0 * y;
6934 x0 = x, y0 = y;
6935 }
6936 d3_geo_pathArea.lineEnd = function() {
6937 nextPoint(x00, y00);
6938 };
6939 }
6940 var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
6941 var d3_geo_pathBounds = {
6942 point: d3_geo_pathBoundsPoint,
6943 lineStart: d3_noop,
6944 lineEnd: d3_noop,
6945 polygonStart: d3_noop,
6946 polygonEnd: d3_noop
6947 };
6948 function d3_geo_pathBoundsPoint(x, y) {
6949 if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
6950 if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
6951 if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
6952 if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
6953 }
6954 function d3_geo_pathBuffer() {
6955 var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
6956 var stream = {
6957 point: point,
6958 lineStart: function() {
6959 stream.point = pointLineStart;
6960 },
6961 lineEnd: lineEnd,
6962 polygonStart: function() {
6963 stream.lineEnd = lineEndPolygon;
6964 },
6965 polygonEnd: function() {
6966 stream.lineEnd = lineEnd;
6967 stream.point = point;
6968 },
6969 pointRadius: function(_) {
6970 pointCircle = d3_geo_pathBufferCircle(_);
6971 return stream;
6972 },
6973 result: function() {
6974 if (buffer.length) {
6975 var result = buffer.join("");
6976 buffer = [];
6977 return result;
6978 }
6979 }
6980 };
6981 function point(x, y) {
6982 buffer.push("M", x, ",", y, pointCircle);
6983 }
6984 function pointLineStart(x, y) {
6985 buffer.push("M", x, ",", y);
6986 stream.point = pointLine;
6987 }
6988 function pointLine(x, y) {
6989 buffer.push("L", x, ",", y);
6990 }
6991 function lineEnd() {
6992 stream.point = point;
6993 }
6994 function lineEndPolygon() {
6995 buffer.push("Z");
6996 }
6997 return stream;
6998 }
6999 function d3_geo_pathBufferCircle(radius) {
7000 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
7001 }
7002 var d3_geo_pathCentroid = {
7003 point: d3_geo_pathCentroidPoint,
7004 lineStart: d3_geo_pathCentroidLineStart,
7005 lineEnd: d3_geo_pathCentroidLineEnd,
7006 polygonStart: function() {
7007 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
7008 },
7009 polygonEnd: function() {
7010 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
7011 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
7012 d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
7013 }
7014 };
7015 function d3_geo_pathCentroidPoint(x, y) {
7016 d3_geo_centroidX0 += x;
7017 d3_geo_centroidY0 += y;
7018 ++d3_geo_centroidZ0;
7019 }
7020 function d3_geo_pathCentroidLineStart() {
7021 var x0, y0;
7022 d3_geo_pathCentroid.point = function(x, y) {
7023 d3_geo_pathCentroid.point = nextPoint;
7024 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
7025 };
7026 function nextPoint(x, y) {
7027 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
7028 d3_geo_centroidX1 += z * (x0 + x) / 2;
7029 d3_geo_centroidY1 += z * (y0 + y) / 2;
7030 d3_geo_centroidZ1 += z;
7031 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
7032 }
7033 }
7034 function d3_geo_pathCentroidLineEnd() {
7035 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
7036 }
7037 function d3_geo_pathCentroidRingStart() {
7038 var x00, y00, x0, y0;
7039 d3_geo_pathCentroid.point = function(x, y) {
7040 d3_geo_pathCentroid.point = nextPoint;
7041 d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
7042 };
7043 function nextPoint(x, y) {
7044 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
7045 d3_geo_centroidX1 += z * (x0 + x) / 2;
7046 d3_geo_centroidY1 += z * (y0 + y) / 2;
7047 d3_geo_centroidZ1 += z;
7048 z = y0 * x - x0 * y;
7049 d3_geo_centroidX2 += z * (x0 + x);
7050 d3_geo_centroidY2 += z * (y0 + y);
7051 d3_geo_centroidZ2 += z * 3;
7052 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
7053 }
7054 d3_geo_pathCentroid.lineEnd = function() {
7055 nextPoint(x00, y00);
7056 };
7057 }
7058 function d3_geo_pathContext(context) {
7059 var pointRadius = 4.5;
7060 var stream = {
7061 point: point,
7062 lineStart: function() {
7063 stream.point = pointLineStart;
7064 },
7065 lineEnd: lineEnd,
7066 polygonStart: function() {
7067 stream.lineEnd = lineEndPolygon;
7068 },
7069 polygonEnd: function() {
7070 stream.lineEnd = lineEnd;
7071 stream.point = point;
7072 },
7073 pointRadius: function(_) {
7074 pointRadius = _;
7075 return stream;
7076 },
7077 result: d3_noop
7078 };
7079 function point(x, y) {
7080 context.moveTo(x + pointRadius, y);
7081 context.arc(x, y, pointRadius, 0, τ);
7082 }
7083 function pointLineStart(x, y) {
7084 context.moveTo(x, y);
7085 stream.point = pointLine;
7086 }
7087 function pointLine(x, y) {
7088 context.lineTo(x, y);
7089 }
7090 function lineEnd() {
7091 stream.point = point;
7092 }
7093 function lineEndPolygon() {
7094 context.closePath();
7095 }
7096 return stream;
7097 }
7098 function d3_geo_resample(project) {
7099 var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
7100 function resample(stream) {
7101 return (maxDepth ? resampleRecursive : resampleNone)(stream);
7102 }
7103 function resampleNone(stream) {
7104 return d3_geo_transformPoint(stream, function(x, y) {
7105 x = project(x, y);
7106 stream.point(x[0], x[1]);
7107 });
7108 }
7109 function resampleRecursive(stream) {
7110 var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
7111 var resample = {
7112 point: point,
7113 lineStart: lineStart,
7114 lineEnd: lineEnd,
7115 polygonStart: function() {
7116 stream.polygonStart();
7117 resample.lineStart = ringStart;
7118 },
7119 polygonEnd: function() {
7120 stream.polygonEnd();
7121 resample.lineStart = lineStart;
7122 }
7123 };
7124 function point(x, y) {
7125 x = project(x, y);
7126 stream.point(x[0], x[1]);
7127 }
7128 function lineStart() {
7129 x0 = NaN;
7130 resample.point = linePoint;
7131 stream.lineStart();
7132 }
7133 function linePoint(λ, φ) {
7134 var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
7135 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);
7136 stream.point(x0, y0);
7137 }
7138 function lineEnd() {
7139 resample.point = point;
7140 stream.lineEnd();
7141 }
7142 function ringStart() {
7143 lineStart();
7144 resample.point = ringPoint;
7145 resample.lineEnd = ringEnd;
7146 }
7147 function ringPoint(λ, φ) {
7148 linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
7149 resample.point = linePoint;
7150 }
7151 function ringEnd() {
7152 resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
7153 resample.lineEnd = lineEnd;
7154 lineEnd();
7155 }
7156 return resample;
7157 }
7158 function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
7159 var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
7160 if (d2 > 4 * δ2 && depth--) {
7161 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) < ε || abs0 - λ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;
7162 if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
7163 resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
7164 stream.point(x2, y2);
7165 resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
7166 }
7167 }
7168 }
7169 resample.precision = function(_) {
7170 if (!arguments.length) return Math.sqrt(δ2);
7171 maxDepth = (δ2 = _ * _) > 0 && 16;
7172 return resample;
7173 };
7174 return resample;
7175 }
7176 d3.geo.path = function() {
7177 var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
7178 function path(object) {
7179 if (object) {
7180 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
7181 if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
7182 d3.geo.stream(object, cacheStream);
7183 }
7184 return contextStream.result();
7185 }
7186 path.area = function(object) {
7187 d3_geo_pathAreaSum = 0;
7188 d3.geo.stream(object, projectStream(d3_geo_pathArea));
7189 return d3_geo_pathAreaSum;
7190 };
7191 path.centroid = function(object) {
7192 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;
7193 d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
7194 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 ];
7195 };
7196 path.bounds = function(object) {
7197 d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
7198 d3.geo.stream(object, projectStream(d3_geo_pathBounds));
7199 return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
7200 };
7201 path.projection = function(_) {
7202 if (!arguments.length) return projection;
7203 projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
7204 return reset();
7205 };
7206 path.context = function(_) {
7207 if (!arguments.length) return context;
7208 contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
7209 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
7210 return reset();
7211 };
7212 path.pointRadius = function(_) {
7213 if (!arguments.length) return pointRadius;
7214 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
7215 return path;
7216 };
7217 function reset() {
7218 cacheStream = null;
7219 return path;
7220 }
7221 return path.projection(d3.geo.albersUsa()).context(null);
7222 };
7223 function d3_geo_pathProjectStream(project) {
7224 var resample = d3_geo_resample(function(x, y) {
7225 return project([ x * d3_degrees, y * d3_degrees ]);
7226 });
7227 return function(stream) {
7228 return d3_geo_projectionRadians(resample(stream));
7229 };
7230 }
7231 d3.geo.transform = function(methods) {
7232 return {
7233 stream: function(stream) {
7234 var transform = new d3_geo_transform(stream);
7235 for (var k in methods) transform[k] = methods[k];
7236 return transform;
7237 }
7238 };
7239 };
7240 function d3_geo_transform(stream) {
7241 this.stream = stream;
7242 }
7243 d3_geo_transform.prototype = {
7244 point: function(x, y) {
7245 this.stream.point(x, y);
7246 },
7247 sphere: function() {
7248 this.stream.sphere();
7249 },
7250 lineStart: function() {
7251 this.stream.lineStart();
7252 },
7253 lineEnd: function() {
7254 this.stream.lineEnd();
7255 },
7256 polygonStart: function() {
7257 this.stream.polygonStart();
7258 },
7259 polygonEnd: function() {
7260 this.stream.polygonEnd();
7261 }
7262 };
7263 function d3_geo_transformPoint(stream, point) {
7264 return {
7265 point: point,
7266 sphere: function() {
7267 stream.sphere();
7268 },
7269 lineStart: function() {
7270 stream.lineStart();
7271 },
7272 lineEnd: function() {
7273 stream.lineEnd();
7274 },
7275 polygonStart: function() {
7276 stream.polygonStart();
7277 },
7278 polygonEnd: function() {
7279 stream.polygonEnd();
7280 }
7281 };
7282 }
7283 d3.geo.projection = d3_geo_projection;
7284 d3.geo.projectionMutator = d3_geo_projectionMutator;
7285 function d3_geo_projection(project) {
7286 return d3_geo_projectionMutator(function() {
7287 return project;
7288 })();
7289 }
7290 function d3_geo_projectionMutator(projectAt) {
7291 var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
7292 x = project(x, y);
7293 return [ x[0] * k + δx, δy - x[1] * k ];
7294 }), 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;
7295 function projection(point) {
7296 point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
7297 return [ point[0] * k + δx, δy - point[1] * k ];
7298 }
7299 function invert(point) {
7300 point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
7301 return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
7302 }
7303 projection.stream = function(output) {
7304 if (stream) stream.valid = false;
7305 stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
7306 stream.valid = true;
7307 return stream;
7308 };
7309 projection.clipAngle = function(_) {
7310 if (!arguments.length) return clipAngle;
7311 preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
7312 return invalidate();
7313 };
7314 projection.clipExtent = function(_) {
7315 if (!arguments.length) return clipExtent;
7316 clipExtent = _;
7317 postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
7318 return invalidate();
7319 };
7320 projection.scale = function(_) {
7321 if (!arguments.length) return k;
7322 k = +_;
7323 return reset();
7324 };
7325 projection.translate = function(_) {
7326 if (!arguments.length) return [ x, y ];
7327 x = +_[0];
7328 y = +_[1];
7329 return reset();
7330 };
7331 projection.center = function(_) {
7332 if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
7333 λ = _[0] % 360 * d3_radians;
7334 φ = _[1] % 360 * d3_radians;
7335 return reset();
7336 };
7337 projection.rotate = function(_) {
7338 if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
7339 δλ = _[0] % 360 * d3_radians;
7340 δφ = _[1] % 360 * d3_radians;
7341 δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
7342 return reset();
7343 };
7344 d3.rebind(projection, projectResample, "precision");
7345 function reset() {
7346 projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
7347 var center = project(λ, φ);
7348 δx = x - center[0] * k;
7349 δy = y + center[1] * k;
7350 return invalidate();
7351 }
7352 function invalidate() {
7353 if (stream) stream.valid = false, stream = null;
7354 return projection;
7355 }
7356 return function() {
7357 project = projectAt.apply(this, arguments);
7358 projection.invert = project.invert && invert;
7359 return reset();
7360 };
7361 }
7362 function d3_geo_projectionRadians(stream) {
7363 return d3_geo_transformPoint(stream, function(x, y) {
7364 stream.point(x * d3_radians, y * d3_radians);
7365 });
7366 }
7367 function d3_geo_equirectangular(λ, φ) {
7368 return [ λ, φ ];
7369 }
7370 (d3.geo.equirectangular = function() {
7371 return d3_geo_projection(d3_geo_equirectangular);
7372 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
7373 d3.geo.rotation = function(rotate) {
7374 rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
7375 function forward(coordinates) {
7376 coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
7377 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
7378 }
7379 forward.invert = function(coordinates) {
7380 coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
7381 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
7382 };
7383 return forward;
7384 };
7385 function d3_geo_identityRotation(λ, φ) {
7386 return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
7387 }
7388 d3_geo_identityRotation.invert = d3_geo_equirectangular;
7389 function d3_geo_rotation(δλ, δφ, δγ) {
7390 return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
7391 }
7392 function d3_geo_forwardRotationλ(δλ) {
7393 return function(λ, φ) {
7394 return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
7395 };
7396 }
7397 function d3_geo_rotationλ(δλ) {
7398 var rotation = d3_geo_forwardRotationλ(δλ);
7399 rotation.invert = d3_geo_forwardRotationλ(-δλ);
7400 return rotation;
7401 }
7402 function d3_geo_rotationφγ(δφ, δγ) {
7403 var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
7404 function rotation(λ, φ) {
7405 var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
7406 return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
7407 }
7408 rotation.invert = function(λ, φ) {
7409 var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
7410 return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
7411 };
7412 return rotation;
7413 }
7414 d3.geo.circle = function() {
7415 var origin = [ 0, 0 ], angle, precision = 6, interpolate;
7416 function circle() {
7417 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 = [];
7418 interpolate(null, null, 1, {
7419 point: function(x, y) {
7420 ring.push(x = rotate(x, y));
7421 x[0] *= d3_degrees, x[1] *= d3_degrees;
7422 }
7423 });
7424 return {
7425 type: "Polygon",
7426 coordinates: [ ring ]
7427 };
7428 }
7429 circle.origin = function(x) {
7430 if (!arguments.length) return origin;
7431 origin = x;
7432 return circle;
7433 };
7434 circle.angle = function(x) {
7435 if (!arguments.length) return angle;
7436 interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
7437 return circle;
7438 };
7439 circle.precision = function(_) {
7440 if (!arguments.length) return precision;
7441 interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
7442 return circle;
7443 };
7444 return circle.angle(90);
7445 };
7446 function d3_geo_circleInterpolate(radius, precision) {
7447 var cr = Math.cos(radius), sr = Math.sin(radius);
7448 return function(from, to, direction, listener) {
7449 var step = direction * precision;
7450 if (from != null) {
7451 from = d3_geo_circleAngle(cr, from);
7452 to = d3_geo_circleAngle(cr, to);
7453 if (direction > 0 ? from < to : from > to) from += direction * τ;
7454 } else {
7455 from = radius + direction * τ;
7456 to = radius - .5 * step;
7457 }
7458 for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
7459 listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
7460 }
7461 };
7462 }
7463 function d3_geo_circleAngle(cr, point) {
7464 var a = d3_geo_cartesian(point);
7465 a[0] -= cr;
7466 d3_geo_cartesianNormalize(a);
7467 var angle = d3_acos(-a[1]);
7468 return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
7469 }
7470 d3.geo.distance = function(a, b) {
7471 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;
7472 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Δλ);
7473 };
7474 d3.geo.graticule = function() {
7475 var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
7476 function graticule() {
7477 return {
7478 type: "MultiLineString",
7479 coordinates: lines()
7480 };
7481 }
7482 function lines() {
7483 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) {
7484 return abs(x % DX) > ε;
7485 }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
7486 return abs(y % DY) > ε;
7487 }).map(y));
7488 }
7489 graticule.lines = function() {
7490 return lines().map(function(coordinates) {
7491 return {
7492 type: "LineString",
7493 coordinates: coordinates
7494 };
7495 });
7496 };
7497 graticule.outline = function() {
7498 return {
7499 type: "Polygon",
7500 coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
7501 };
7502 };
7503 graticule.extent = function(_) {
7504 if (!arguments.length) return graticule.minorExtent();
7505 return graticule.majorExtent(_).minorExtent(_);
7506 };
7507 graticule.majorExtent = function(_) {
7508 if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
7509 X0 = +_[0][0], X1 = +_[1][0];
7510 Y0 = +_[0][1], Y1 = +_[1][1];
7511 if (X0 > X1) _ = X0, X0 = X1, X1 = _;
7512 if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
7513 return graticule.precision(precision);
7514 };
7515 graticule.minorExtent = function(_) {
7516 if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
7517 x0 = +_[0][0], x1 = +_[1][0];
7518 y0 = +_[0][1], y1 = +_[1][1];
7519 if (x0 > x1) _ = x0, x0 = x1, x1 = _;
7520 if (y0 > y1) _ = y0, y0 = y1, y1 = _;
7521 return graticule.precision(precision);
7522 };
7523 graticule.step = function(_) {
7524 if (!arguments.length) return graticule.minorStep();
7525 return graticule.majorStep(_).minorStep(_);
7526 };
7527 graticule.majorStep = function(_) {
7528 if (!arguments.length) return [ DX, DY ];
7529 DX = +_[0], DY = +_[1];
7530 return graticule;
7531 };
7532 graticule.minorStep = function(_) {
7533 if (!arguments.length) return [ dx, dy ];
7534 dx = +_[0], dy = +_[1];
7535 return graticule;
7536 };
7537 graticule.precision = function(_) {
7538 if (!arguments.length) return precision;
7539 precision = +_;
7540 x = d3_geo_graticuleX(y0, y1, 90);
7541 y = d3_geo_graticuleY(x0, x1, precision);
7542 X = d3_geo_graticuleX(Y0, Y1, 90);
7543 Y = d3_geo_graticuleY(X0, X1, precision);
7544 return graticule;
7545 };
7546 return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
7547 };
7548 function d3_geo_graticuleX(y0, y1, dy) {
7549 var y = d3.range(y0, y1 - ε, dy).concat(y1);
7550 return function(x) {
7551 return y.map(function(y) {
7552 return [ x, y ];
7553 });
7554 };
7555 }
7556 function d3_geo_graticuleY(x0, x1, dx) {
7557 var x = d3.range(x0, x1 - ε, dx).concat(x1);
7558 return function(y) {
7559 return x.map(function(x) {
7560 return [ x, y ];
7561 });
7562 };
7563 }
7564 function d3_source(d) {
7565 return d.source;
7566 }
7567 function d3_target(d) {
7568 return d.target;
7569 }
7570 d3.geo.greatArc = function() {
7571 var source = d3_source, source_, target = d3_target, target_;
7572 function greatArc() {
7573 return {
7574 type: "LineString",
7575 coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
7576 };
7577 }
7578 greatArc.distance = function() {
7579 return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
7580 };
7581 greatArc.source = function(_) {
7582 if (!arguments.length) return source;
7583 source = _, source_ = typeof _ === "function" ? null : _;
7584 return greatArc;
7585 };
7586 greatArc.target = function(_) {
7587 if (!arguments.length) return target;
7588 target = _, target_ = typeof _ === "function" ? null : _;
7589 return greatArc;
7590 };
7591 greatArc.precision = function() {
7592 return arguments.length ? greatArc : 0;
7593 };
7594 return greatArc;
7595 };
7596 d3.geo.interpolate = function(source, target) {
7597 return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
7598 };
7599 function d3_geo_interpolate(x0, y0, x1, y1) {
7600 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);
7601 var interpolate = d ? function(t) {
7602 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;
7603 return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
7604 } : function() {
7605 return [ x0 * d3_degrees, y0 * d3_degrees ];
7606 };
7607 interpolate.distance = d;
7608 return interpolate;
7609 }
7610 d3.geo.length = function(object) {
7611 d3_geo_lengthSum = 0;
7612 d3.geo.stream(object, d3_geo_length);
7613 return d3_geo_lengthSum;
7614 };
7615 var d3_geo_lengthSum;
7616 var d3_geo_length = {
7617 sphere: d3_noop,
7618 point: d3_noop,
7619 lineStart: d3_geo_lengthLineStart,
7620 lineEnd: d3_noop,
7621 polygonStart: d3_noop,
7622 polygonEnd: d3_noop
7623 };
7624 function d3_geo_lengthLineStart() {
7625 var λ0, sinφ0, cosφ0;
7626 d3_geo_length.point = function(λ, φ) {
7627 λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
7628 d3_geo_length.point = nextPoint;
7629 };
7630 d3_geo_length.lineEnd = function() {
7631 d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
7632 };
7633 function nextPoint(λ, φ) {
7634 var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
7635 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Δλ);
7636 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
7637 }
7638 }
7639 function d3_geo_azimuthal(scale, angle) {
7640 function azimuthal(λ, φ) {
7641 var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
7642 return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
7643 }
7644 azimuthal.invert = function(x, y) {
7645 var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
7646 return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
7647 };
7648 return azimuthal;
7649 }
7650 var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
7651 return Math.sqrt(2 / (1 + cosλcosφ));
7652 }, function(ρ) {
7653 return 2 * Math.asin(ρ / 2);
7654 });
7655 (d3.geo.azimuthalEqualArea = function() {
7656 return d3_geo_projection(d3_geo_azimuthalEqualArea);
7657 }).raw = d3_geo_azimuthalEqualArea;
7658 var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
7659 var c = Math.acos(cosλcosφ);
7660 return c && c / Math.sin(c);
7661 }, d3_identity);
7662 (d3.geo.azimuthalEquidistant = function() {
7663 return d3_geo_projection(d3_geo_azimuthalEquidistant);
7664 }).raw = d3_geo_azimuthalEquidistant;
7665 function d3_geo_conicConformal(φ0, φ1) {
7666 var cosφ0 = Math.cos(φ0), t = function(φ) {
7667 return Math.tan(π / 4 + φ / 2);
7668 }, 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;
7669 if (!n) return d3_geo_mercator;
7670 function forward(λ, φ) {
7671 if (F > 0) {
7672 if (φ < -halfπ + ε) φ = -halfπ + ε;
7673 } else {
7674 if (φ > halfπ - ε) φ = halfπ - ε;
7675 }
7676 var ρ = F / Math.pow(t(φ), n);
7677 return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
7678 }
7679 forward.invert = function(x, y) {
7680 var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
7681 return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
7682 };
7683 return forward;
7684 }
7685 (d3.geo.conicConformal = function() {
7686 return d3_geo_conic(d3_geo_conicConformal);
7687 }).raw = d3_geo_conicConformal;
7688 function d3_geo_conicEquidistant(φ0, φ1) {
7689 var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
7690 if (abs(n) < ε) return d3_geo_equirectangular;
7691 function forward(λ, φ) {
7692 var ρ = G - φ;
7693 return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
7694 }
7695 forward.invert = function(x, y) {
7696 var ρ0_y = G - y;
7697 return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
7698 };
7699 return forward;
7700 }
7701 (d3.geo.conicEquidistant = function() {
7702 return d3_geo_conic(d3_geo_conicEquidistant);
7703 }).raw = d3_geo_conicEquidistant;
7704 var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
7705 return 1 / cosλcosφ;
7706 }, Math.atan);
7707 (d3.geo.gnomonic = function() {
7708 return d3_geo_projection(d3_geo_gnomonic);
7709 }).raw = d3_geo_gnomonic;
7710 function d3_geo_mercator(λ, φ) {
7711 return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
7712 }
7713 d3_geo_mercator.invert = function(x, y) {
7714 return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
7715 };
7716 function d3_geo_mercatorProjection(project) {
7717 var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
7718 m.scale = function() {
7719 var v = scale.apply(m, arguments);
7720 return v === m ? clipAuto ? m.clipExtent(null) : m : v;
7721 };
7722 m.translate = function() {
7723 var v = translate.apply(m, arguments);
7724 return v === m ? clipAuto ? m.clipExtent(null) : m : v;
7725 };
7726 m.clipExtent = function(_) {
7727 var v = clipExtent.apply(m, arguments);
7728 if (v === m) {
7729 if (clipAuto = _ == null) {
7730 var k = π * scale(), t = translate();
7731 clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
7732 }
7733 } else if (clipAuto) {
7734 v = null;
7735 }
7736 return v;
7737 };
7738 return m.clipExtent(null);
7739 }
7740 (d3.geo.mercator = function() {
7741 return d3_geo_mercatorProjection(d3_geo_mercator);
7742 }).raw = d3_geo_mercator;
7743 var d3_geo_orthographic = d3_geo_azimuthal(function() {
7744 return 1;
7745 }, Math.asin);
7746 (d3.geo.orthographic = function() {
7747 return d3_geo_projection(d3_geo_orthographic);
7748 }).raw = d3_geo_orthographic;
7749 var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
7750 return 1 / (1 + cosλcosφ);
7751 }, function(ρ) {
7752 return 2 * Math.atan(ρ);
7753 });
7754 (d3.geo.stereographic = function() {
7755 return d3_geo_projection(d3_geo_stereographic);
7756 }).raw = d3_geo_stereographic;
7757 function d3_geo_transverseMercator(λ, φ) {
7758 return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
7759 }
7760 d3_geo_transverseMercator.invert = function(x, y) {
7761 return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
7762 };
7763 (d3.geo.transverseMercator = function() {
7764 var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
7765 projection.center = function(_) {
7766 return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]);
7767 };
7768 projection.rotate = function(_) {
7769 return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
7770 [ _[0], _[1], _[2] - 90 ]);
7771 };
7772 return rotate([ 0, 0, 90 ]);
7773 }).raw = d3_geo_transverseMercator;
7774 d3.geom = {};
7775 function d3_geom_pointX(d) {
7776 return d[0];
7777 }
7778 function d3_geom_pointY(d) {
7779 return d[1];
7780 }
7781 d3.geom.hull = function(vertices) {
7782 var x = d3_geom_pointX, y = d3_geom_pointY;
7783 if (arguments.length) return hull(vertices);
7784 function hull(data) {
7785 if (data.length < 3) return [];
7786 var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
7787 for (i = 0; i < n; i++) {
7788 points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
7789 }
7790 points.sort(d3_geom_hullOrder);
7791 for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
7792 var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
7793 var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
7794 for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
7795 for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
7796 return polygon;
7797 }
7798 hull.x = function(_) {
7799 return arguments.length ? (x = _, hull) : x;
7800 };
7801 hull.y = function(_) {
7802 return arguments.length ? (y = _, hull) : y;
7803 };
7804 return hull;
7805 };
7806 function d3_geom_hullUpper(points) {
7807 var n = points.length, hull = [ 0, 1 ], hs = 2;
7808 for (var i = 2; i < n; i++) {
7809 while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
7810 hull[hs++] = i;
7811 }
7812 return hull.slice(0, hs);
7813 }
7814 function d3_geom_hullOrder(a, b) {
7815 return a[0] - b[0] || a[1] - b[1];
7816 }
7817 d3.geom.polygon = function(coordinates) {
7818 d3_subclass(coordinates, d3_geom_polygonPrototype);
7819 return coordinates;
7820 };
7821 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
7822 d3_geom_polygonPrototype.area = function() {
7823 var i = -1, n = this.length, a, b = this[n - 1], area = 0;
7824 while (++i < n) {
7825 a = b;
7826 b = this[i];
7827 area += a[1] * b[0] - a[0] * b[1];
7828 }
7829 return area * .5;
7830 };
7831 d3_geom_polygonPrototype.centroid = function(k) {
7832 var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
7833 if (!arguments.length) k = -1 / (6 * this.area());
7834 while (++i < n) {
7835 a = b;
7836 b = this[i];
7837 c = a[0] * b[1] - b[0] * a[1];
7838 x += (a[0] + b[0]) * c;
7839 y += (a[1] + b[1]) * c;
7840 }
7841 return [ x * k, y * k ];
7842 };
7843 d3_geom_polygonPrototype.clip = function(subject) {
7844 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;
7845 while (++i < n) {
7846 input = subject.slice();
7847 subject.length = 0;
7848 b = this[i];
7849 c = input[(m = input.length - closed) - 1];
7850 j = -1;
7851 while (++j < m) {
7852 d = input[j];
7853 if (d3_geom_polygonInside(d, a, b)) {
7854 if (!d3_geom_polygonInside(c, a, b)) {
7855 subject.push(d3_geom_polygonIntersect(c, d, a, b));
7856 }
7857 subject.push(d);
7858 } else if (d3_geom_polygonInside(c, a, b)) {
7859 subject.push(d3_geom_polygonIntersect(c, d, a, b));
7860 }
7861 c = d;
7862 }
7863 if (closed) subject.push(subject[0]);
7864 a = b;
7865 }
7866 return subject;
7867 };
7868 function d3_geom_polygonInside(p, a, b) {
7869 return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
7870 }
7871 function d3_geom_polygonIntersect(c, d, a, b) {
7872 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);
7873 return [ x1 + ua * x21, y1 + ua * y21 ];
7874 }
7875 function d3_geom_polygonClosed(coordinates) {
7876 var a = coordinates[0], b = coordinates[coordinates.length - 1];
7877 return !(a[0] - b[0] || a[1] - b[1]);
7878 }
7879 var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
7880 function d3_geom_voronoiBeach() {
7881 d3_geom_voronoiRedBlackNode(this);
7882 this.edge = this.site = this.circle = null;
7883 }
7884 function d3_geom_voronoiCreateBeach(site) {
7885 var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
7886 beach.site = site;
7887 return beach;
7888 }
7889 function d3_geom_voronoiDetachBeach(beach) {
7890 d3_geom_voronoiDetachCircle(beach);
7891 d3_geom_voronoiBeaches.remove(beach);
7892 d3_geom_voronoiBeachPool.push(beach);
7893 d3_geom_voronoiRedBlackNode(beach);
7894 }
7895 function d3_geom_voronoiRemoveBeach(beach) {
7896 var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
7897 x: x,
7898 y: y
7899 }, previous = beach.P, next = beach.N, disappearing = [ beach ];
7900 d3_geom_voronoiDetachBeach(beach);
7901 var lArc = previous;
7902 while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
7903 previous = lArc.P;
7904 disappearing.unshift(lArc);
7905 d3_geom_voronoiDetachBeach(lArc);
7906 lArc = previous;
7907 }
7908 disappearing.unshift(lArc);
7909 d3_geom_voronoiDetachCircle(lArc);
7910 var rArc = next;
7911 while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
7912 next = rArc.N;
7913 disappearing.push(rArc);
7914 d3_geom_voronoiDetachBeach(rArc);
7915 rArc = next;
7916 }
7917 disappearing.push(rArc);
7918 d3_geom_voronoiDetachCircle(rArc);
7919 var nArcs = disappearing.length, iArc;
7920 for (iArc = 1; iArc < nArcs; ++iArc) {
7921 rArc = disappearing[iArc];
7922 lArc = disappearing[iArc - 1];
7923 d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
7924 }
7925 lArc = disappearing[0];
7926 rArc = disappearing[nArcs - 1];
7927 rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
7928 d3_geom_voronoiAttachCircle(lArc);
7929 d3_geom_voronoiAttachCircle(rArc);
7930 }
7931 function d3_geom_voronoiAddBeach(site) {
7932 var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
7933 while (node) {
7934 dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
7935 if (dxl > ε) node = node.L; else {
7936 dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
7937 if (dxr > ε) {
7938 if (!node.R) {
7939 lArc = node;
7940 break;
7941 }
7942 node = node.R;
7943 } else {
7944 if (dxl > -ε) {
7945 lArc = node.P;
7946 rArc = node;
7947 } else if (dxr > -ε) {
7948 lArc = node;
7949 rArc = node.N;
7950 } else {
7951 lArc = rArc = node;
7952 }
7953 break;
7954 }
7955 }
7956 }
7957 var newArc = d3_geom_voronoiCreateBeach(site);
7958 d3_geom_voronoiBeaches.insert(lArc, newArc);
7959 if (!lArc && !rArc) return;
7960 if (lArc === rArc) {
7961 d3_geom_voronoiDetachCircle(lArc);
7962 rArc = d3_geom_voronoiCreateBeach(lArc.site);
7963 d3_geom_voronoiBeaches.insert(newArc, rArc);
7964 newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
7965 d3_geom_voronoiAttachCircle(lArc);
7966 d3_geom_voronoiAttachCircle(rArc);
7967 return;
7968 }
7969 if (!rArc) {
7970 newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
7971 return;
7972 }
7973 d3_geom_voronoiDetachCircle(lArc);
7974 d3_geom_voronoiDetachCircle(rArc);
7975 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 = {
7976 x: (cy * hb - by * hc) / d + ax,
7977 y: (bx * hc - cx * hb) / d + ay
7978 };
7979 d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
7980 newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
7981 rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
7982 d3_geom_voronoiAttachCircle(lArc);
7983 d3_geom_voronoiAttachCircle(rArc);
7984 }
7985 function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
7986 var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
7987 if (!pby2) return rfocx;
7988 var lArc = arc.P;
7989 if (!lArc) return -Infinity;
7990 site = lArc.site;
7991 var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
7992 if (!plby2) return lfocx;
7993 var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
7994 if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
7995 return (rfocx + lfocx) / 2;
7996 }
7997 function d3_geom_voronoiRightBreakPoint(arc, directrix) {
7998 var rArc = arc.N;
7999 if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
8000 var site = arc.site;
8001 return site.y === directrix ? site.x : Infinity;
8002 }
8003 function d3_geom_voronoiCell(site) {
8004 this.site = site;
8005 this.edges = [];
8006 }
8007 d3_geom_voronoiCell.prototype.prepare = function() {
8008 var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
8009 while (iHalfEdge--) {
8010 edge = halfEdges[iHalfEdge].edge;
8011 if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
8012 }
8013 halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
8014 return halfEdges.length;
8015 };
8016 function d3_geom_voronoiCloseCells(extent) {
8017 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;
8018 while (iCell--) {
8019 cell = cells[iCell];
8020 if (!cell || !cell.prepare()) continue;
8021 halfEdges = cell.edges;
8022 nHalfEdges = halfEdges.length;
8023 iHalfEdge = 0;
8024 while (iHalfEdge < nHalfEdges) {
8025 end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
8026 start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
8027 if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
8028 halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
8029 x: x0,
8030 y: abs(x2 - x0) < ε ? y2 : y1
8031 } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
8032 x: abs(y2 - y1) < ε ? x2 : x1,
8033 y: y1
8034 } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
8035 x: x1,
8036 y: abs(x2 - x1) < ε ? y2 : y0
8037 } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
8038 x: abs(y2 - y0) < ε ? x2 : x0,
8039 y: y0
8040 } : null), cell.site, null));
8041 ++nHalfEdges;
8042 }
8043 }
8044 }
8045 }
8046 function d3_geom_voronoiHalfEdgeOrder(a, b) {
8047 return b.angle - a.angle;
8048 }
8049 function d3_geom_voronoiCircle() {
8050 d3_geom_voronoiRedBlackNode(this);
8051 this.x = this.y = this.arc = this.site = this.cy = null;
8052 }
8053 function d3_geom_voronoiAttachCircle(arc) {
8054 var lArc = arc.P, rArc = arc.N;
8055 if (!lArc || !rArc) return;
8056 var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
8057 if (lSite === rSite) return;
8058 var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
8059 var d = 2 * (ax * cy - ay * cx);
8060 if (d >= -ε2) return;
8061 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;
8062 var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
8063 circle.arc = arc;
8064 circle.site = cSite;
8065 circle.x = x + bx;
8066 circle.y = cy + Math.sqrt(x * x + y * y);
8067 circle.cy = cy;
8068 arc.circle = circle;
8069 var before = null, node = d3_geom_voronoiCircles._;
8070 while (node) {
8071 if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
8072 if (node.L) node = node.L; else {
8073 before = node.P;
8074 break;
8075 }
8076 } else {
8077 if (node.R) node = node.R; else {
8078 before = node;
8079 break;
8080 }
8081 }
8082 }
8083 d3_geom_voronoiCircles.insert(before, circle);
8084 if (!before) d3_geom_voronoiFirstCircle = circle;
8085 }
8086 function d3_geom_voronoiDetachCircle(arc) {
8087 var circle = arc.circle;
8088 if (circle) {
8089 if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
8090 d3_geom_voronoiCircles.remove(circle);
8091 d3_geom_voronoiCirclePool.push(circle);
8092 d3_geom_voronoiRedBlackNode(circle);
8093 arc.circle = null;
8094 }
8095 }
8096 function d3_geom_voronoiClipEdges(extent) {
8097 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;
8098 while (i--) {
8099 e = edges[i];
8100 if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
8101 e.a = e.b = null;
8102 edges.splice(i, 1);
8103 }
8104 }
8105 }
8106 function d3_geom_voronoiConnectEdge(edge, extent) {
8107 var vb = edge.b;
8108 if (vb) return true;
8109 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;
8110 if (ry === ly) {
8111 if (fx < x0 || fx >= x1) return;
8112 if (lx > rx) {
8113 if (!va) va = {
8114 x: fx,
8115 y: y0
8116 }; else if (va.y >= y1) return;
8117 vb = {
8118 x: fx,
8119 y: y1
8120 };
8121 } else {
8122 if (!va) va = {
8123 x: fx,
8124 y: y1
8125 }; else if (va.y < y0) return;
8126 vb = {
8127 x: fx,
8128 y: y0
8129 };
8130 }
8131 } else {
8132 fm = (lx - rx) / (ry - ly);
8133 fb = fy - fm * fx;
8134 if (fm < -1 || fm > 1) {
8135 if (lx > rx) {
8136 if (!va) va = {
8137 x: (y0 - fb) / fm,
8138 y: y0
8139 }; else if (va.y >= y1) return;
8140 vb = {
8141 x: (y1 - fb) / fm,
8142 y: y1
8143 };
8144 } else {
8145 if (!va) va = {
8146 x: (y1 - fb) / fm,
8147 y: y1
8148 }; else if (va.y < y0) return;
8149 vb = {
8150 x: (y0 - fb) / fm,
8151 y: y0
8152 };
8153 }
8154 } else {
8155 if (ly < ry) {
8156 if (!va) va = {
8157 x: x0,
8158 y: fm * x0 + fb
8159 }; else if (va.x >= x1) return;
8160 vb = {
8161 x: x1,
8162 y: fm * x1 + fb
8163 };
8164 } else {
8165 if (!va) va = {
8166 x: x1,
8167 y: fm * x1 + fb
8168 }; else if (va.x < x0) return;
8169 vb = {
8170 x: x0,
8171 y: fm * x0 + fb
8172 };
8173 }
8174 }
8175 }
8176 edge.a = va;
8177 edge.b = vb;
8178 return true;
8179 }
8180 function d3_geom_voronoiEdge(lSite, rSite) {
8181 this.l = lSite;
8182 this.r = rSite;
8183 this.a = this.b = null;
8184 }
8185 function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
8186 var edge = new d3_geom_voronoiEdge(lSite, rSite);
8187 d3_geom_voronoiEdges.push(edge);
8188 if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
8189 if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
8190 d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
8191 d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
8192 return edge;
8193 }
8194 function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
8195 var edge = new d3_geom_voronoiEdge(lSite, null);
8196 edge.a = va;
8197 edge.b = vb;
8198 d3_geom_voronoiEdges.push(edge);
8199 return edge;
8200 }
8201 function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
8202 if (!edge.a && !edge.b) {
8203 edge.a = vertex;
8204 edge.l = lSite;
8205 edge.r = rSite;
8206 } else if (edge.l === rSite) {
8207 edge.b = vertex;
8208 } else {
8209 edge.a = vertex;
8210 }
8211 }
8212 function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
8213 var va = edge.a, vb = edge.b;
8214 this.edge = edge;
8215 this.site = lSite;
8216 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);
8217 }
8218 d3_geom_voronoiHalfEdge.prototype = {
8219 start: function() {
8220 return this.edge.l === this.site ? this.edge.a : this.edge.b;
8221 },
8222 end: function() {
8223 return this.edge.l === this.site ? this.edge.b : this.edge.a;
8224 }
8225 };
8226 function d3_geom_voronoiRedBlackTree() {
8227 this._ = null;
8228 }
8229 function d3_geom_voronoiRedBlackNode(node) {
8230 node.U = node.C = node.L = node.R = node.P = node.N = null;
8231 }
8232 d3_geom_voronoiRedBlackTree.prototype = {
8233 insert: function(after, node) {
8234 var parent, grandpa, uncle;
8235 if (after) {
8236 node.P = after;
8237 node.N = after.N;
8238 if (after.N) after.N.P = node;
8239 after.N = node;
8240 if (after.R) {
8241 after = after.R;
8242 while (after.L) after = after.L;
8243 after.L = node;
8244 } else {
8245 after.R = node;
8246 }
8247 parent = after;
8248 } else if (this._) {
8249 after = d3_geom_voronoiRedBlackFirst(this._);
8250 node.P = null;
8251 node.N = after;
8252 after.P = after.L = node;
8253 parent = after;
8254 } else {
8255 node.P = node.N = null;
8256 this._ = node;
8257 parent = null;
8258 }
8259 node.L = node.R = null;
8260 node.U = parent;
8261 node.C = true;
8262 after = node;
8263 while (parent && parent.C) {
8264 grandpa = parent.U;
8265 if (parent === grandpa.L) {
8266 uncle = grandpa.R;
8267 if (uncle && uncle.C) {
8268 parent.C = uncle.C = false;
8269 grandpa.C = true;
8270 after = grandpa;
8271 } else {
8272 if (after === parent.R) {
8273 d3_geom_voronoiRedBlackRotateLeft(this, parent);
8274 after = parent;
8275 parent = after.U;
8276 }
8277 parent.C = false;
8278 grandpa.C = true;
8279 d3_geom_voronoiRedBlackRotateRight(this, grandpa);
8280 }
8281 } else {
8282 uncle = grandpa.L;
8283 if (uncle && uncle.C) {
8284 parent.C = uncle.C = false;
8285 grandpa.C = true;
8286 after = grandpa;
8287 } else {
8288 if (after === parent.L) {
8289 d3_geom_voronoiRedBlackRotateRight(this, parent);
8290 after = parent;
8291 parent = after.U;
8292 }
8293 parent.C = false;
8294 grandpa.C = true;
8295 d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
8296 }
8297 }
8298 parent = after.U;
8299 }
8300 this._.C = false;
8301 },
8302 remove: function(node) {
8303 if (node.N) node.N.P = node.P;
8304 if (node.P) node.P.N = node.N;
8305 node.N = node.P = null;
8306 var parent = node.U, sibling, left = node.L, right = node.R, next, red;
8307 if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
8308 if (parent) {
8309 if (parent.L === node) parent.L = next; else parent.R = next;
8310 } else {
8311 this._ = next;
8312 }
8313 if (left && right) {
8314 red = next.C;
8315 next.C = node.C;
8316 next.L = left;
8317 left.U = next;
8318 if (next !== right) {
8319 parent = next.U;
8320 next.U = node.U;
8321 node = next.R;
8322 parent.L = node;
8323 next.R = right;
8324 right.U = next;
8325 } else {
8326 next.U = parent;
8327 parent = next;
8328 node = next.R;
8329 }
8330 } else {
8331 red = node.C;
8332 node = next;
8333 }
8334 if (node) node.U = parent;
8335 if (red) return;
8336 if (node && node.C) {
8337 node.C = false;
8338 return;
8339 }
8340 do {
8341 if (node === this._) break;
8342 if (node === parent.L) {
8343 sibling = parent.R;
8344 if (sibling.C) {
8345 sibling.C = false;
8346 parent.C = true;
8347 d3_geom_voronoiRedBlackRotateLeft(this, parent);
8348 sibling = parent.R;
8349 }
8350 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
8351 if (!sibling.R || !sibling.R.C) {
8352 sibling.L.C = false;
8353 sibling.C = true;
8354 d3_geom_voronoiRedBlackRotateRight(this, sibling);
8355 sibling = parent.R;
8356 }
8357 sibling.C = parent.C;
8358 parent.C = sibling.R.C = false;
8359 d3_geom_voronoiRedBlackRotateLeft(this, parent);
8360 node = this._;
8361 break;
8362 }
8363 } else {
8364 sibling = parent.L;
8365 if (sibling.C) {
8366 sibling.C = false;
8367 parent.C = true;
8368 d3_geom_voronoiRedBlackRotateRight(this, parent);
8369 sibling = parent.L;
8370 }
8371 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
8372 if (!sibling.L || !sibling.L.C) {
8373 sibling.R.C = false;
8374 sibling.C = true;
8375 d3_geom_voronoiRedBlackRotateLeft(this, sibling);
8376 sibling = parent.L;
8377 }
8378 sibling.C = parent.C;
8379 parent.C = sibling.L.C = false;
8380 d3_geom_voronoiRedBlackRotateRight(this, parent);
8381 node = this._;
8382 break;
8383 }
8384 }
8385 sibling.C = true;
8386 node = parent;
8387 parent = parent.U;
8388 } while (!node.C);
8389 if (node) node.C = false;
8390 }
8391 };
8392 function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
8393 var p = node, q = node.R, parent = p.U;
8394 if (parent) {
8395 if (parent.L === p) parent.L = q; else parent.R = q;
8396 } else {
8397 tree._ = q;
8398 }
8399 q.U = parent;
8400 p.U = q;
8401 p.R = q.L;
8402 if (p.R) p.R.U = p;
8403 q.L = p;
8404 }
8405 function d3_geom_voronoiRedBlackRotateRight(tree, node) {
8406 var p = node, q = node.L, parent = p.U;
8407 if (parent) {
8408 if (parent.L === p) parent.L = q; else parent.R = q;
8409 } else {
8410 tree._ = q;
8411 }
8412 q.U = parent;
8413 p.U = q;
8414 p.L = q.R;
8415 if (p.L) p.L.U = p;
8416 q.R = p;
8417 }
8418 function d3_geom_voronoiRedBlackFirst(node) {
8419 while (node.L) node = node.L;
8420 return node;
8421 }
8422 function d3_geom_voronoi(sites, bbox) {
8423 var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
8424 d3_geom_voronoiEdges = [];
8425 d3_geom_voronoiCells = new Array(sites.length);
8426 d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
8427 d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
8428 while (true) {
8429 circle = d3_geom_voronoiFirstCircle;
8430 if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
8431 if (site.x !== x0 || site.y !== y0) {
8432 d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
8433 d3_geom_voronoiAddBeach(site);
8434 x0 = site.x, y0 = site.y;
8435 }
8436 site = sites.pop();
8437 } else if (circle) {
8438 d3_geom_voronoiRemoveBeach(circle.arc);
8439 } else {
8440 break;
8441 }
8442 }
8443 if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
8444 var diagram = {
8445 cells: d3_geom_voronoiCells,
8446 edges: d3_geom_voronoiEdges
8447 };
8448 d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
8449 return diagram;
8450 }
8451 function d3_geom_voronoiVertexOrder(a, b) {
8452 return b.y - a.y || b.x - a.x;
8453 }
8454 d3.geom.voronoi = function(points) {
8455 var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
8456 if (points) return voronoi(points);
8457 function voronoi(data) {
8458 var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
8459 d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
8460 var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
8461 var s = e.start();
8462 return [ s.x, s.y ];
8463 }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
8464 polygon.point = data[i];
8465 });
8466 return polygons;
8467 }
8468 function sites(data) {
8469 return data.map(function(d, i) {
8470 return {
8471 x: Math.round(fx(d, i) / ε) * ε,
8472 y: Math.round(fy(d, i) / ε) * ε,
8473 i: i
8474 };
8475 });
8476 }
8477 voronoi.links = function(data) {
8478 return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
8479 return edge.l && edge.r;
8480 }).map(function(edge) {
8481 return {
8482 source: data[edge.l.i],
8483 target: data[edge.r.i]
8484 };
8485 });
8486 };
8487 voronoi.triangles = function(data) {
8488 var triangles = [];
8489 d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
8490 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;
8491 while (++j < m) {
8492 e0 = e1;
8493 s0 = s1;
8494 e1 = edges[j].edge;
8495 s1 = e1.l === site ? e1.r : e1.l;
8496 if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
8497 triangles.push([ data[i], data[s0.i], data[s1.i] ]);
8498 }
8499 }
8500 });
8501 return triangles;
8502 };
8503 voronoi.x = function(_) {
8504 return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
8505 };
8506 voronoi.y = function(_) {
8507 return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
8508 };
8509 voronoi.clipExtent = function(_) {
8510 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
8511 clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
8512 return voronoi;
8513 };
8514 voronoi.size = function(_) {
8515 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
8516 return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
8517 };
8518 return voronoi;
8519 };
8520 var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
8521 function d3_geom_voronoiTriangleArea(a, b, c) {
8522 return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
8523 }
8524 d3.geom.delaunay = function(vertices) {
8525 return d3.geom.voronoi().triangles(vertices);
8526 };
8527 d3.geom.quadtree = function(points, x1, y1, x2, y2) {
8528 var x = d3_geom_pointX, y = d3_geom_pointY, compat;
8529 if (compat = arguments.length) {
8530 x = d3_geom_quadtreeCompatX;
8531 y = d3_geom_quadtreeCompatY;
8532 if (compat === 3) {
8533 y2 = y1;
8534 x2 = x1;
8535 y1 = x1 = 0;
8536 }
8537 return quadtree(points);
8538 }
8539 function quadtree(data) {
8540 var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
8541 if (x1 != null) {
8542 x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
8543 } else {
8544 x2_ = y2_ = -(x1_ = y1_ = Infinity);
8545 xs = [], ys = [];
8546 n = data.length;
8547 if (compat) for (i = 0; i < n; ++i) {
8548 d = data[i];
8549 if (d.x < x1_) x1_ = d.x;
8550 if (d.y < y1_) y1_ = d.y;
8551 if (d.x > x2_) x2_ = d.x;
8552 if (d.y > y2_) y2_ = d.y;
8553 xs.push(d.x);
8554 ys.push(d.y);
8555 } else for (i = 0; i < n; ++i) {
8556 var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
8557 if (x_ < x1_) x1_ = x_;
8558 if (y_ < y1_) y1_ = y_;
8559 if (x_ > x2_) x2_ = x_;
8560 if (y_ > y2_) y2_ = y_;
8561 xs.push(x_);
8562 ys.push(y_);
8563 }
8564 }
8565 var dx = x2_ - x1_, dy = y2_ - y1_;
8566 if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
8567 function insert(n, d, x, y, x1, y1, x2, y2) {
8568 if (isNaN(x) || isNaN(y)) return;
8569 if (n.leaf) {
8570 var nx = n.x, ny = n.y;
8571 if (nx != null) {
8572 if (abs(nx - x) + abs(ny - y) < .01) {
8573 insertChild(n, d, x, y, x1, y1, x2, y2);
8574 } else {
8575 var nPoint = n.point;
8576 n.x = n.y = n.point = null;
8577 insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
8578 insertChild(n, d, x, y, x1, y1, x2, y2);
8579 }
8580 } else {
8581 n.x = x, n.y = y, n.point = d;
8582 }
8583 } else {
8584 insertChild(n, d, x, y, x1, y1, x2, y2);
8585 }
8586 }
8587 function insertChild(n, d, x, y, x1, y1, x2, y2) {
8588 var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
8589 n.leaf = false;
8590 n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
8591 if (right) x1 = xm; else x2 = xm;
8592 if (below) y1 = ym; else y2 = ym;
8593 insert(n, d, x, y, x1, y1, x2, y2);
8594 }
8595 var root = d3_geom_quadtreeNode();
8596 root.add = function(d) {
8597 insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
8598 };
8599 root.visit = function(f) {
8600 d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
8601 };
8602 root.find = function(point) {
8603 return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
8604 };
8605 i = -1;
8606 if (x1 == null) {
8607 while (++i < n) {
8608 insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
8609 }
8610 --i;
8611 } else data.forEach(root.add);
8612 xs = ys = data = d = null;
8613 return root;
8614 }
8615 quadtree.x = function(_) {
8616 return arguments.length ? (x = _, quadtree) : x;
8617 };
8618 quadtree.y = function(_) {
8619 return arguments.length ? (y = _, quadtree) : y;
8620 };
8621 quadtree.extent = function(_) {
8622 if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
8623 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
8624 y2 = +_[1][1];
8625 return quadtree;
8626 };
8627 quadtree.size = function(_) {
8628 if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
8629 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
8630 return quadtree;
8631 };
8632 return quadtree;
8633 };
8634 function d3_geom_quadtreeCompatX(d) {
8635 return d.x;
8636 }
8637 function d3_geom_quadtreeCompatY(d) {
8638 return d.y;
8639 }
8640 function d3_geom_quadtreeNode() {
8641 return {
8642 leaf: true,
8643 nodes: [],
8644 point: null,
8645 x: null,
8646 y: null
8647 };
8648 }
8649 function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
8650 if (!f(node, x1, y1, x2, y2)) {
8651 var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
8652 if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
8653 if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
8654 if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
8655 if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
8656 }
8657 }
8658 function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
8659 var minDistance2 = Infinity, closestPoint;
8660 (function find(node, x1, y1, x2, y2) {
8661 if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
8662 if (point = node.point) {
8663 var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy;
8664 if (distance2 < minDistance2) {
8665 var distance = Math.sqrt(minDistance2 = distance2);
8666 x0 = x - distance, y0 = y - distance;
8667 x3 = x + distance, y3 = y + distance;
8668 closestPoint = point;
8669 }
8670 }
8671 var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
8672 for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
8673 if (node = children[i & 3]) switch (i & 3) {
8674 case 0:
8675 find(node, x1, y1, xm, ym);
8676 break;
8677
8678 case 1:
8679 find(node, xm, y1, x2, ym);
8680 break;
8681
8682 case 2:
8683 find(node, x1, ym, xm, y2);
8684 break;
8685
8686 case 3:
8687 find(node, xm, ym, x2, y2);
8688 break;
8689 }
8690 }
8691 })(root, x0, y0, x3, y3);
8692 return closestPoint;
8693 }
8694 d3.interpolateRgb = d3_interpolateRgb;
8695 function d3_interpolateRgb(a, b) {
8696 a = d3.rgb(a);
8697 b = d3.rgb(b);
8698 var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
8699 return function(t) {
8700 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));
8701 };
8702 }
8703 d3.interpolateObject = d3_interpolateObject;
8704 function d3_interpolateObject(a, b) {
8705 var i = {}, c = {}, k;
8706 for (k in a) {
8707 if (k in b) {
8708 i[k] = d3_interpolate(a[k], b[k]);
8709 } else {
8710 c[k] = a[k];
8711 }
8712 }
8713 for (k in b) {
8714 if (!(k in a)) {
8715 c[k] = b[k];
8716 }
8717 }
8718 return function(t) {
8719 for (k in i) c[k] = i[k](t);
8720 return c;
8721 };
8722 }
8723 d3.interpolateNumber = d3_interpolateNumber;
8724 function d3_interpolateNumber(a, b) {
8725 a = +a, b = +b;
8726 return function(t) {
8727 return a * (1 - t) + b * t;
8728 };
8729 }
8730 d3.interpolateString = d3_interpolateString;
8731 function d3_interpolateString(a, b) {
8732 var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
8733 a = a + "", b = b + "";
8734 while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
8735 if ((bs = bm.index) > bi) {
8736 bs = b.slice(bi, bs);
8737 if (s[i]) s[i] += bs; else s[++i] = bs;
8738 }
8739 if ((am = am[0]) === (bm = bm[0])) {
8740 if (s[i]) s[i] += bm; else s[++i] = bm;
8741 } else {
8742 s[++i] = null;
8743 q.push({
8744 i: i,
8745 x: d3_interpolateNumber(am, bm)
8746 });
8747 }
8748 bi = d3_interpolate_numberB.lastIndex;
8749 }
8750 if (bi < b.length) {
8751 bs = b.slice(bi);
8752 if (s[i]) s[i] += bs; else s[++i] = bs;
8753 }
8754 return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
8755 return b(t) + "";
8756 }) : function() {
8757 return b;
8758 } : (b = q.length, function(t) {
8759 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
8760 return s.join("");
8761 });
8762 }
8763 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
8764 d3.interpolate = d3_interpolate;
8765 function d3_interpolate(a, b) {
8766 var i = d3.interpolators.length, f;
8767 while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
8768 return f;
8769 }
8770 d3.interpolators = [ function(a, b) {
8771 var t = typeof b;
8772 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);
8773 } ];
8774 d3.interpolateArray = d3_interpolateArray;
8775 function d3_interpolateArray(a, b) {
8776 var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
8777 for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
8778 for (;i < na; ++i) c[i] = a[i];
8779 for (;i < nb; ++i) c[i] = b[i];
8780 return function(t) {
8781 for (i = 0; i < n0; ++i) c[i] = x[i](t);
8782 return c;
8783 };
8784 }
8785 var d3_ease_default = function() {
8786 return d3_identity;
8787 };
8788 var d3_ease = d3.map({
8789 linear: d3_ease_default,
8790 poly: d3_ease_poly,
8791 quad: function() {
8792 return d3_ease_quad;
8793 },
8794 cubic: function() {
8795 return d3_ease_cubic;
8796 },
8797 sin: function() {
8798 return d3_ease_sin;
8799 },
8800 exp: function() {
8801 return d3_ease_exp;
8802 },
8803 circle: function() {
8804 return d3_ease_circle;
8805 },
8806 elastic: d3_ease_elastic,
8807 back: d3_ease_back,
8808 bounce: function() {
8809 return d3_ease_bounce;
8810 }
8811 });
8812 var d3_ease_mode = d3.map({
8813 "in": d3_identity,
8814 out: d3_ease_reverse,
8815 "in-out": d3_ease_reflect,
8816 "out-in": function(f) {
8817 return d3_ease_reflect(d3_ease_reverse(f));
8818 }
8819 });
8820 d3.ease = function(name) {
8821 var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
8822 t = d3_ease.get(t) || d3_ease_default;
8823 m = d3_ease_mode.get(m) || d3_identity;
8824 return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
8825 };
8826 function d3_ease_clamp(f) {
8827 return function(t) {
8828 return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
8829 };
8830 }
8831 function d3_ease_reverse(f) {
8832 return function(t) {
8833 return 1 - f(1 - t);
8834 };
8835 }
8836 function d3_ease_reflect(f) {
8837 return function(t) {
8838 return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
8839 };
8840 }
8841 function d3_ease_quad(t) {
8842 return t * t;
8843 }
8844 function d3_ease_cubic(t) {
8845 return t * t * t;
8846 }
8847 function d3_ease_cubicInOut(t) {
8848 if (t <= 0) return 0;
8849 if (t >= 1) return 1;
8850 var t2 = t * t, t3 = t2 * t;
8851 return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
8852 }
8853 function d3_ease_poly(e) {
8854 return function(t) {
8855 return Math.pow(t, e);
8856 };
8857 }
8858 function d3_ease_sin(t) {
8859 return 1 - Math.cos(t * halfπ);
8860 }
8861 function d3_ease_exp(t) {
8862 return Math.pow(2, 10 * (t - 1));
8863 }
8864 function d3_ease_circle(t) {
8865 return 1 - Math.sqrt(1 - t * t);
8866 }
8867 function d3_ease_elastic(a, p) {
8868 var s;
8869 if (arguments.length < 2) p = .45;
8870 if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
8871 return function(t) {
8872 return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
8873 };
8874 }
8875 function d3_ease_back(s) {
8876 if (!s) s = 1.70158;
8877 return function(t) {
8878 return t * t * ((s + 1) * t - s);
8879 };
8880 }
8881 function d3_ease_bounce(t) {
8882 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;
8883 }
8884 d3.interpolateHcl = d3_interpolateHcl;
8885 function d3_interpolateHcl(a, b) {
8886 a = d3.hcl(a);
8887 b = d3.hcl(b);
8888 var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
8889 if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
8890 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
8891 return function(t) {
8892 return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
8893 };
8894 }
8895 d3.interpolateHsl = d3_interpolateHsl;
8896 function d3_interpolateHsl(a, b) {
8897 a = d3.hsl(a);
8898 b = d3.hsl(b);
8899 var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
8900 if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
8901 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
8902 return function(t) {
8903 return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
8904 };
8905 }
8906 d3.interpolateLab = d3_interpolateLab;
8907 function d3_interpolateLab(a, b) {
8908 a = d3.lab(a);
8909 b = d3.lab(b);
8910 var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
8911 return function(t) {
8912 return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
8913 };
8914 }
8915 d3.interpolateRound = d3_interpolateRound;
8916 function d3_interpolateRound(a, b) {
8917 b -= a;
8918 return function(t) {
8919 return Math.round(a + b * t);
8920 };
8921 }
8922 d3.transform = function(string) {
8923 var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
8924 return (d3.transform = function(string) {
8925 if (string != null) {
8926 g.setAttribute("transform", string);
8927 var t = g.transform.baseVal.consolidate();
8928 }
8929 return new d3_transform(t ? t.matrix : d3_transformIdentity);
8930 })(string);
8931 };
8932 function d3_transform(m) {
8933 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;
8934 if (r0[0] * r1[1] < r1[0] * r0[1]) {
8935 r0[0] *= -1;
8936 r0[1] *= -1;
8937 kx *= -1;
8938 kz *= -1;
8939 }
8940 this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
8941 this.translate = [ m.e, m.f ];
8942 this.scale = [ kx, ky ];
8943 this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
8944 }
8945 d3_transform.prototype.toString = function() {
8946 return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
8947 };
8948 function d3_transformDot(a, b) {
8949 return a[0] * b[0] + a[1] * b[1];
8950 }
8951 function d3_transformNormalize(a) {
8952 var k = Math.sqrt(d3_transformDot(a, a));
8953 if (k) {
8954 a[0] /= k;
8955 a[1] /= k;
8956 }
8957 return k;
8958 }
8959 function d3_transformCombine(a, b, k) {
8960 a[0] += k * b[0];
8961 a[1] += k * b[1];
8962 return a;
8963 }
8964 var d3_transformIdentity = {
8965 a: 1,
8966 b: 0,
8967 c: 0,
8968 d: 1,
8969 e: 0,
8970 f: 0
8971 };
8972 d3.interpolateTransform = d3_interpolateTransform;
8973 function d3_interpolateTransformPop(s) {
8974 return s.length ? s.pop() + "," : "";
8975 }
8976 function d3_interpolateTranslate(ta, tb, s, q) {
8977 if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
8978 var i = s.push("translate(", null, ",", null, ")");
8979 q.push({
8980 i: i - 4,
8981 x: d3_interpolateNumber(ta[0], tb[0])
8982 }, {
8983 i: i - 2,
8984 x: d3_interpolateNumber(ta[1], tb[1])
8985 });
8986 } else if (tb[0] || tb[1]) {
8987 s.push("translate(" + tb + ")");
8988 }
8989 }
8990 function d3_interpolateRotate(ra, rb, s, q) {
8991 if (ra !== rb) {
8992 if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
8993 q.push({
8994 i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2,
8995 x: d3_interpolateNumber(ra, rb)
8996 });
8997 } else if (rb) {
8998 s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")");
8999 }
9000 }
9001 function d3_interpolateSkew(wa, wb, s, q) {
9002 if (wa !== wb) {
9003 q.push({
9004 i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2,
9005 x: d3_interpolateNumber(wa, wb)
9006 });
9007 } else if (wb) {
9008 s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")");
9009 }
9010 }
9011 function d3_interpolateScale(ka, kb, s, q) {
9012 if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
9013 var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")");
9014 q.push({
9015 i: i - 4,
9016 x: d3_interpolateNumber(ka[0], kb[0])
9017 }, {
9018 i: i - 2,
9019 x: d3_interpolateNumber(ka[1], kb[1])
9020 });
9021 } else if (kb[0] !== 1 || kb[1] !== 1) {
9022 s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")");
9023 }
9024 }
9025 function d3_interpolateTransform(a, b) {
9026 var s = [], q = [];
9027 a = d3.transform(a), b = d3.transform(b);
9028 d3_interpolateTranslate(a.translate, b.translate, s, q);
9029 d3_interpolateRotate(a.rotate, b.rotate, s, q);
9030 d3_interpolateSkew(a.skew, b.skew, s, q);
9031 d3_interpolateScale(a.scale, b.scale, s, q);
9032 a = b = null;
9033 return function(t) {
9034 var i = -1, n = q.length, o;
9035 while (++i < n) s[(o = q[i]).i] = o.x(t);
9036 return s.join("");
9037 };
9038 }
9039 function d3_uninterpolateNumber(a, b) {
9040 b = (b -= a = +a) || 1 / b;
9041 return function(x) {
9042 return (x - a) / b;
9043 };
9044 }
9045 function d3_uninterpolateClamp(a, b) {
9046 b = (b -= a = +a) || 1 / b;
9047 return function(x) {
9048 return Math.max(0, Math.min(1, (x - a) / b));
9049 };
9050 }
9051 d3.layout = {};
9052 d3.layout.bundle = function() {
9053 return function(links) {
9054 var paths = [], i = -1, n = links.length;
9055 while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
9056 return paths;
9057 };
9058 };
9059 function d3_layout_bundlePath(link) {
9060 var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
9061 while (start !== lca) {
9062 start = start.parent;
9063 points.push(start);
9064 }
9065 var k = points.length;
9066 while (end !== lca) {
9067 points.splice(k, 0, end);
9068 end = end.parent;
9069 }
9070 return points;
9071 }
9072 function d3_layout_bundleAncestors(node) {
9073 var ancestors = [], parent = node.parent;
9074 while (parent != null) {
9075 ancestors.push(node);
9076 node = parent;
9077 parent = parent.parent;
9078 }
9079 ancestors.push(node);
9080 return ancestors;
9081 }
9082 function d3_layout_bundleLeastCommonAncestor(a, b) {
9083 if (a === b) return a;
9084 var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
9085 while (aNode === bNode) {
9086 sharedNode = aNode;
9087 aNode = aNodes.pop();
9088 bNode = bNodes.pop();
9089 }
9090 return sharedNode;
9091 }
9092 d3.layout.chord = function() {
9093 var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
9094 function relayout() {
9095 var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
9096 chords = [];
9097 groups = [];
9098 k = 0, i = -1;
9099 while (++i < n) {
9100 x = 0, j = -1;
9101 while (++j < n) {
9102 x += matrix[i][j];
9103 }
9104 groupSums.push(x);
9105 subgroupIndex.push(d3.range(n));
9106 k += x;
9107 }
9108 if (sortGroups) {
9109 groupIndex.sort(function(a, b) {
9110 return sortGroups(groupSums[a], groupSums[b]);
9111 });
9112 }
9113 if (sortSubgroups) {
9114 subgroupIndex.forEach(function(d, i) {
9115 d.sort(function(a, b) {
9116 return sortSubgroups(matrix[i][a], matrix[i][b]);
9117 });
9118 });
9119 }
9120 k = (τ - padding * n) / k;
9121 x = 0, i = -1;
9122 while (++i < n) {
9123 x0 = x, j = -1;
9124 while (++j < n) {
9125 var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
9126 subgroups[di + "-" + dj] = {
9127 index: di,
9128 subindex: dj,
9129 startAngle: a0,
9130 endAngle: a1,
9131 value: v
9132 };
9133 }
9134 groups[di] = {
9135 index: di,
9136 startAngle: x0,
9137 endAngle: x,
9138 value: groupSums[di]
9139 };
9140 x += padding;
9141 }
9142 i = -1;
9143 while (++i < n) {
9144 j = i - 1;
9145 while (++j < n) {
9146 var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
9147 if (source.value || target.value) {
9148 chords.push(source.value < target.value ? {
9149 source: target,
9150 target: source
9151 } : {
9152 source: source,
9153 target: target
9154 });
9155 }
9156 }
9157 }
9158 if (sortChords) resort();
9159 }
9160 function resort() {
9161 chords.sort(function(a, b) {
9162 return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
9163 });
9164 }
9165 chord.matrix = function(x) {
9166 if (!arguments.length) return matrix;
9167 n = (matrix = x) && matrix.length;
9168 chords = groups = null;
9169 return chord;
9170 };
9171 chord.padding = function(x) {
9172 if (!arguments.length) return padding;
9173 padding = x;
9174 chords = groups = null;
9175 return chord;
9176 };
9177 chord.sortGroups = function(x) {
9178 if (!arguments.length) return sortGroups;
9179 sortGroups = x;
9180 chords = groups = null;
9181 return chord;
9182 };
9183 chord.sortSubgroups = function(x) {
9184 if (!arguments.length) return sortSubgroups;
9185 sortSubgroups = x;
9186 chords = null;
9187 return chord;
9188 };
9189 chord.sortChords = function(x) {
9190 if (!arguments.length) return sortChords;
9191 sortChords = x;
9192 if (chords) resort();
9193 return chord;
9194 };
9195 chord.chords = function() {
9196 if (!chords) relayout();
9197 return chords;
9198 };
9199 chord.groups = function() {
9200 if (!groups) relayout();
9201 return groups;
9202 };
9203 return chord;
9204 };
9205 d3.layout.force = function() {
9206 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;
9207 function repulse(node) {
9208 return function(quad, x1, _, x2) {
9209 if (quad.point !== node) {
9210 var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
9211 if (dw * dw / theta2 < dn) {
9212 if (dn < chargeDistance2) {
9213 var k = quad.charge / dn;
9214 node.px -= dx * k;
9215 node.py -= dy * k;
9216 }
9217 return true;
9218 }
9219 if (quad.point && dn && dn < chargeDistance2) {
9220 var k = quad.pointCharge / dn;
9221 node.px -= dx * k;
9222 node.py -= dy * k;
9223 }
9224 }
9225 return !quad.charge;
9226 };
9227 }
9228 force.tick = function() {
9229 if ((alpha *= .99) < .005) {
9230 timer = null;
9231 event.end({
9232 type: "end",
9233 alpha: alpha = 0
9234 });
9235 return true;
9236 }
9237 var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
9238 for (i = 0; i < m; ++i) {
9239 o = links[i];
9240 s = o.source;
9241 t = o.target;
9242 x = t.x - s.x;
9243 y = t.y - s.y;
9244 if (l = x * x + y * y) {
9245 l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
9246 x *= l;
9247 y *= l;
9248 t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5);
9249 t.y -= y * k;
9250 s.x += x * (k = 1 - k);
9251 s.y += y * k;
9252 }
9253 }
9254 if (k = alpha * gravity) {
9255 x = size[0] / 2;
9256 y = size[1] / 2;
9257 i = -1;
9258 if (k) while (++i < n) {
9259 o = nodes[i];
9260 o.x += (x - o.x) * k;
9261 o.y += (y - o.y) * k;
9262 }
9263 }
9264 if (charge) {
9265 d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
9266 i = -1;
9267 while (++i < n) {
9268 if (!(o = nodes[i]).fixed) {
9269 q.visit(repulse(o));
9270 }
9271 }
9272 }
9273 i = -1;
9274 while (++i < n) {
9275 o = nodes[i];
9276 if (o.fixed) {
9277 o.x = o.px;
9278 o.y = o.py;
9279 } else {
9280 o.x -= (o.px - (o.px = o.x)) * friction;
9281 o.y -= (o.py - (o.py = o.y)) * friction;
9282 }
9283 }
9284 event.tick({
9285 type: "tick",
9286 alpha: alpha
9287 });
9288 };
9289 force.nodes = function(x) {
9290 if (!arguments.length) return nodes;
9291 nodes = x;
9292 return force;
9293 };
9294 force.links = function(x) {
9295 if (!arguments.length) return links;
9296 links = x;
9297 return force;
9298 };
9299 force.size = function(x) {
9300 if (!arguments.length) return size;
9301 size = x;
9302 return force;
9303 };
9304 force.linkDistance = function(x) {
9305 if (!arguments.length) return linkDistance;
9306 linkDistance = typeof x === "function" ? x : +x;
9307 return force;
9308 };
9309 force.distance = force.linkDistance;
9310 force.linkStrength = function(x) {
9311 if (!arguments.length) return linkStrength;
9312 linkStrength = typeof x === "function" ? x : +x;
9313 return force;
9314 };
9315 force.friction = function(x) {
9316 if (!arguments.length) return friction;
9317 friction = +x;
9318 return force;
9319 };
9320 force.charge = function(x) {
9321 if (!arguments.length) return charge;
9322 charge = typeof x === "function" ? x : +x;
9323 return force;
9324 };
9325 force.chargeDistance = function(x) {
9326 if (!arguments.length) return Math.sqrt(chargeDistance2);
9327 chargeDistance2 = x * x;
9328 return force;
9329 };
9330 force.gravity = function(x) {
9331 if (!arguments.length) return gravity;
9332 gravity = +x;
9333 return force;
9334 };
9335 force.theta = function(x) {
9336 if (!arguments.length) return Math.sqrt(theta2);
9337 theta2 = x * x;
9338 return force;
9339 };
9340 force.alpha = function(x) {
9341 if (!arguments.length) return alpha;
9342 x = +x;
9343 if (alpha) {
9344 if (x > 0) {
9345 alpha = x;
9346 } else {
9347 timer.c = null, timer.t = NaN, timer = null;
9348 event.end({
9349 type: "end",
9350 alpha: alpha = 0
9351 });
9352 }
9353 } else if (x > 0) {
9354 event.start({
9355 type: "start",
9356 alpha: alpha = x
9357 });
9358 timer = d3_timer(force.tick);
9359 }
9360 return force;
9361 };
9362 force.start = function() {
9363 var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
9364 for (i = 0; i < n; ++i) {
9365 (o = nodes[i]).index = i;
9366 o.weight = 0;
9367 }
9368 for (i = 0; i < m; ++i) {
9369 o = links[i];
9370 if (typeof o.source == "number") o.source = nodes[o.source];
9371 if (typeof o.target == "number") o.target = nodes[o.target];
9372 ++o.source.weight;
9373 ++o.target.weight;
9374 }
9375 for (i = 0; i < n; ++i) {
9376 o = nodes[i];
9377 if (isNaN(o.x)) o.x = position("x", w);
9378 if (isNaN(o.y)) o.y = position("y", h);
9379 if (isNaN(o.px)) o.px = o.x;
9380 if (isNaN(o.py)) o.py = o.y;
9381 }
9382 distances = [];
9383 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;
9384 strengths = [];
9385 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;
9386 charges = [];
9387 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;
9388 function position(dimension, size) {
9389 if (!neighbors) {
9390 neighbors = new Array(n);
9391 for (j = 0; j < n; ++j) {
9392 neighbors[j] = [];
9393 }
9394 for (j = 0; j < m; ++j) {
9395 var o = links[j];
9396 neighbors[o.source.index].push(o.target);
9397 neighbors[o.target.index].push(o.source);
9398 }
9399 }
9400 var candidates = neighbors[i], j = -1, l = candidates.length, x;
9401 while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x;
9402 return Math.random() * size;
9403 }
9404 return force.resume();
9405 };
9406 force.resume = function() {
9407 return force.alpha(.1);
9408 };
9409 force.stop = function() {
9410 return force.alpha(0);
9411 };
9412 force.drag = function() {
9413 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);
9414 if (!arguments.length) return drag;
9415 this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
9416 };
9417 function dragmove(d) {
9418 d.px = d3.event.x, d.py = d3.event.y;
9419 force.resume();
9420 }
9421 return d3.rebind(force, event, "on");
9422 };
9423 function d3_layout_forceDragstart(d) {
9424 d.fixed |= 2;
9425 }
9426 function d3_layout_forceDragend(d) {
9427 d.fixed &= ~6;
9428 }
9429 function d3_layout_forceMouseover(d) {
9430 d.fixed |= 4;
9431 d.px = d.x, d.py = d.y;
9432 }
9433 function d3_layout_forceMouseout(d) {
9434 d.fixed &= ~4;
9435 }
9436 function d3_layout_forceAccumulate(quad, alpha, charges) {
9437 var cx = 0, cy = 0;
9438 quad.charge = 0;
9439 if (!quad.leaf) {
9440 var nodes = quad.nodes, n = nodes.length, i = -1, c;
9441 while (++i < n) {
9442 c = nodes[i];
9443 if (c == null) continue;
9444 d3_layout_forceAccumulate(c, alpha, charges);
9445 quad.charge += c.charge;
9446 cx += c.charge * c.cx;
9447 cy += c.charge * c.cy;
9448 }
9449 }
9450 if (quad.point) {
9451 if (!quad.leaf) {
9452 quad.point.x += Math.random() - .5;
9453 quad.point.y += Math.random() - .5;
9454 }
9455 var k = alpha * charges[quad.point.index];
9456 quad.charge += quad.pointCharge = k;
9457 cx += k * quad.point.x;
9458 cy += k * quad.point.y;
9459 }
9460 quad.cx = cx / quad.charge;
9461 quad.cy = cy / quad.charge;
9462 }
9463 var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
9464 d3.layout.hierarchy = function() {
9465 var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
9466 function hierarchy(root) {
9467 var stack = [ root ], nodes = [], node;
9468 root.depth = 0;
9469 while ((node = stack.pop()) != null) {
9470 nodes.push(node);
9471 if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
9472 var n, childs, child;
9473 while (--n >= 0) {
9474 stack.push(child = childs[n]);
9475 child.parent = node;
9476 child.depth = node.depth + 1;
9477 }
9478 if (value) node.value = 0;
9479 node.children = childs;
9480 } else {
9481 if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
9482 delete node.children;
9483 }
9484 }
9485 d3_layout_hierarchyVisitAfter(root, function(node) {
9486 var childs, parent;
9487 if (sort && (childs = node.children)) childs.sort(sort);
9488 if (value && (parent = node.parent)) parent.value += node.value;
9489 });
9490 return nodes;
9491 }
9492 hierarchy.sort = function(x) {
9493 if (!arguments.length) return sort;
9494 sort = x;
9495 return hierarchy;
9496 };
9497 hierarchy.children = function(x) {
9498 if (!arguments.length) return children;
9499 children = x;
9500 return hierarchy;
9501 };
9502 hierarchy.value = function(x) {
9503 if (!arguments.length) return value;
9504 value = x;
9505 return hierarchy;
9506 };
9507 hierarchy.revalue = function(root) {
9508 if (value) {
9509 d3_layout_hierarchyVisitBefore(root, function(node) {
9510 if (node.children) node.value = 0;
9511 });
9512 d3_layout_hierarchyVisitAfter(root, function(node) {
9513 var parent;
9514 if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
9515 if (parent = node.parent) parent.value += node.value;
9516 });
9517 }
9518 return root;
9519 };
9520 return hierarchy;
9521 };
9522 function d3_layout_hierarchyRebind(object, hierarchy) {
9523 d3.rebind(object, hierarchy, "sort", "children", "value");
9524 object.nodes = object;
9525 object.links = d3_layout_hierarchyLinks;
9526 return object;
9527 }
9528 function d3_layout_hierarchyVisitBefore(node, callback) {
9529 var nodes = [ node ];
9530 while ((node = nodes.pop()) != null) {
9531 callback(node);
9532 if ((children = node.children) && (n = children.length)) {
9533 var n, children;
9534 while (--n >= 0) nodes.push(children[n]);
9535 }
9536 }
9537 }
9538 function d3_layout_hierarchyVisitAfter(node, callback) {
9539 var nodes = [ node ], nodes2 = [];
9540 while ((node = nodes.pop()) != null) {
9541 nodes2.push(node);
9542 if ((children = node.children) && (n = children.length)) {
9543 var i = -1, n, children;
9544 while (++i < n) nodes.push(children[i]);
9545 }
9546 }
9547 while ((node = nodes2.pop()) != null) {
9548 callback(node);
9549 }
9550 }
9551 function d3_layout_hierarchyChildren(d) {
9552 return d.children;
9553 }
9554 function d3_layout_hierarchyValue(d) {
9555 return d.value;
9556 }
9557 function d3_layout_hierarchySort(a, b) {
9558 return b.value - a.value;
9559 }
9560 function d3_layout_hierarchyLinks(nodes) {
9561 return d3.merge(nodes.map(function(parent) {
9562 return (parent.children || []).map(function(child) {
9563 return {
9564 source: parent,
9565 target: child
9566 };
9567 });
9568 }));
9569 }
9570 d3.layout.partition = function() {
9571 var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
9572 function position(node, x, dx, dy) {
9573 var children = node.children;
9574 node.x = x;
9575 node.y = node.depth * dy;
9576 node.dx = dx;
9577 node.dy = dy;
9578 if (children && (n = children.length)) {
9579 var i = -1, n, c, d;
9580 dx = node.value ? dx / node.value : 0;
9581 while (++i < n) {
9582 position(c = children[i], x, d = c.value * dx, dy);
9583 x += d;
9584 }
9585 }
9586 }
9587 function depth(node) {
9588 var children = node.children, d = 0;
9589 if (children && (n = children.length)) {
9590 var i = -1, n;
9591 while (++i < n) d = Math.max(d, depth(children[i]));
9592 }
9593 return 1 + d;
9594 }
9595 function partition(d, i) {
9596 var nodes = hierarchy.call(this, d, i);
9597 position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
9598 return nodes;
9599 }
9600 partition.size = function(x) {
9601 if (!arguments.length) return size;
9602 size = x;
9603 return partition;
9604 };
9605 return d3_layout_hierarchyRebind(partition, hierarchy);
9606 };
9607 d3.layout.pie = function() {
9608 var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
9609 function pie(data) {
9610 var n = data.length, values = data.map(function(d, i) {
9611 return +value.call(pie, d, i);
9612 }), 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;
9613 if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
9614 return values[j] - values[i];
9615 } : function(i, j) {
9616 return sort(data[i], data[j]);
9617 });
9618 index.forEach(function(i) {
9619 arcs[i] = {
9620 data: data[i],
9621 value: v = values[i],
9622 startAngle: a,
9623 endAngle: a += v * k + pa,
9624 padAngle: p
9625 };
9626 });
9627 return arcs;
9628 }
9629 pie.value = function(_) {
9630 if (!arguments.length) return value;
9631 value = _;
9632 return pie;
9633 };
9634 pie.sort = function(_) {
9635 if (!arguments.length) return sort;
9636 sort = _;
9637 return pie;
9638 };
9639 pie.startAngle = function(_) {
9640 if (!arguments.length) return startAngle;
9641 startAngle = _;
9642 return pie;
9643 };
9644 pie.endAngle = function(_) {
9645 if (!arguments.length) return endAngle;
9646 endAngle = _;
9647 return pie;
9648 };
9649 pie.padAngle = function(_) {
9650 if (!arguments.length) return padAngle;
9651 padAngle = _;
9652 return pie;
9653 };
9654 return pie;
9655 };
9656 var d3_layout_pieSortByValue = {};
9657 d3.layout.stack = function() {
9658 var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
9659 function stack(data, index) {
9660 if (!(n = data.length)) return data;
9661 var series = data.map(function(d, i) {
9662 return values.call(stack, d, i);
9663 });
9664 var points = series.map(function(d) {
9665 return d.map(function(v, i) {
9666 return [ x.call(stack, v, i), y.call(stack, v, i) ];
9667 });
9668 });
9669 var orders = order.call(stack, points, index);
9670 series = d3.permute(series, orders);
9671 points = d3.permute(points, orders);
9672 var offsets = offset.call(stack, points, index);
9673 var m = series[0].length, n, i, j, o;
9674 for (j = 0; j < m; ++j) {
9675 out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
9676 for (i = 1; i < n; ++i) {
9677 out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
9678 }
9679 }
9680 return data;
9681 }
9682 stack.values = function(x) {
9683 if (!arguments.length) return values;
9684 values = x;
9685 return stack;
9686 };
9687 stack.order = function(x) {
9688 if (!arguments.length) return order;
9689 order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
9690 return stack;
9691 };
9692 stack.offset = function(x) {
9693 if (!arguments.length) return offset;
9694 offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
9695 return stack;
9696 };
9697 stack.x = function(z) {
9698 if (!arguments.length) return x;
9699 x = z;
9700 return stack;
9701 };
9702 stack.y = function(z) {
9703 if (!arguments.length) return y;
9704 y = z;
9705 return stack;
9706 };
9707 stack.out = function(z) {
9708 if (!arguments.length) return out;
9709 out = z;
9710 return stack;
9711 };
9712 return stack;
9713 };
9714 function d3_layout_stackX(d) {
9715 return d.x;
9716 }
9717 function d3_layout_stackY(d) {
9718 return d.y;
9719 }
9720 function d3_layout_stackOut(d, y0, y) {
9721 d.y0 = y0;
9722 d.y = y;
9723 }
9724 var d3_layout_stackOrders = d3.map({
9725 "inside-out": function(data) {
9726 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) {
9727 return max[a] - max[b];
9728 }), top = 0, bottom = 0, tops = [], bottoms = [];
9729 for (i = 0; i < n; ++i) {
9730 j = index[i];
9731 if (top < bottom) {
9732 top += sums[j];
9733 tops.push(j);
9734 } else {
9735 bottom += sums[j];
9736 bottoms.push(j);
9737 }
9738 }
9739 return bottoms.reverse().concat(tops);
9740 },
9741 reverse: function(data) {
9742 return d3.range(data.length).reverse();
9743 },
9744 "default": d3_layout_stackOrderDefault
9745 });
9746 var d3_layout_stackOffsets = d3.map({
9747 silhouette: function(data) {
9748 var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
9749 for (j = 0; j < m; ++j) {
9750 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
9751 if (o > max) max = o;
9752 sums.push(o);
9753 }
9754 for (j = 0; j < m; ++j) {
9755 y0[j] = (max - sums[j]) / 2;
9756 }
9757 return y0;
9758 },
9759 wiggle: function(data) {
9760 var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
9761 y0[0] = o = o0 = 0;
9762 for (j = 1; j < m; ++j) {
9763 for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
9764 for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
9765 for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
9766 s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
9767 }
9768 s2 += s3 * data[i][j][1];
9769 }
9770 y0[j] = o -= s1 ? s2 / s1 * dx : 0;
9771 if (o < o0) o0 = o;
9772 }
9773 for (j = 0; j < m; ++j) y0[j] -= o0;
9774 return y0;
9775 },
9776 expand: function(data) {
9777 var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
9778 for (j = 0; j < m; ++j) {
9779 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
9780 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;
9781 }
9782 for (j = 0; j < m; ++j) y0[j] = 0;
9783 return y0;
9784 },
9785 zero: d3_layout_stackOffsetZero
9786 });
9787 function d3_layout_stackOrderDefault(data) {
9788 return d3.range(data.length);
9789 }
9790 function d3_layout_stackOffsetZero(data) {
9791 var j = -1, m = data[0].length, y0 = [];
9792 while (++j < m) y0[j] = 0;
9793 return y0;
9794 }
9795 function d3_layout_stackMaxIndex(array) {
9796 var i = 1, j = 0, v = array[0][1], k, n = array.length;
9797 for (;i < n; ++i) {
9798 if ((k = array[i][1]) > v) {
9799 j = i;
9800 v = k;
9801 }
9802 }
9803 return j;
9804 }
9805 function d3_layout_stackReduceSum(d) {
9806 return d.reduce(d3_layout_stackSum, 0);
9807 }
9808 function d3_layout_stackSum(p, d) {
9809 return p + d[1];
9810 }
9811 d3.layout.histogram = function() {
9812 var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
9813 function histogram(data, i) {
9814 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;
9815 while (++i < m) {
9816 bin = bins[i] = [];
9817 bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
9818 bin.y = 0;
9819 }
9820 if (m > 0) {
9821 i = -1;
9822 while (++i < n) {
9823 x = values[i];
9824 if (x >= range[0] && x <= range[1]) {
9825 bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
9826 bin.y += k;
9827 bin.push(data[i]);
9828 }
9829 }
9830 }
9831 return bins;
9832 }
9833 histogram.value = function(x) {
9834 if (!arguments.length) return valuer;
9835 valuer = x;
9836 return histogram;
9837 };
9838 histogram.range = function(x) {
9839 if (!arguments.length) return ranger;
9840 ranger = d3_functor(x);
9841 return histogram;
9842 };
9843 histogram.bins = function(x) {
9844 if (!arguments.length) return binner;
9845 binner = typeof x === "number" ? function(range) {
9846 return d3_layout_histogramBinFixed(range, x);
9847 } : d3_functor(x);
9848 return histogram;
9849 };
9850 histogram.frequency = function(x) {
9851 if (!arguments.length) return frequency;
9852 frequency = !!x;
9853 return histogram;
9854 };
9855 return histogram;
9856 };
9857 function d3_layout_histogramBinSturges(range, values) {
9858 return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
9859 }
9860 function d3_layout_histogramBinFixed(range, n) {
9861 var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
9862 while (++x <= n) f[x] = m * x + b;
9863 return f;
9864 }
9865 function d3_layout_histogramRange(values) {
9866 return [ d3.min(values), d3.max(values) ];
9867 }
9868 d3.layout.pack = function() {
9869 var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
9870 function pack(d, i) {
9871 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() {
9872 return radius;
9873 };
9874 root.x = root.y = 0;
9875 d3_layout_hierarchyVisitAfter(root, function(d) {
9876 d.r = +r(d.value);
9877 });
9878 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
9879 if (padding) {
9880 var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
9881 d3_layout_hierarchyVisitAfter(root, function(d) {
9882 d.r += dr;
9883 });
9884 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
9885 d3_layout_hierarchyVisitAfter(root, function(d) {
9886 d.r -= dr;
9887 });
9888 }
9889 d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
9890 return nodes;
9891 }
9892 pack.size = function(_) {
9893 if (!arguments.length) return size;
9894 size = _;
9895 return pack;
9896 };
9897 pack.radius = function(_) {
9898 if (!arguments.length) return radius;
9899 radius = _ == null || typeof _ === "function" ? _ : +_;
9900 return pack;
9901 };
9902 pack.padding = function(_) {
9903 if (!arguments.length) return padding;
9904 padding = +_;
9905 return pack;
9906 };
9907 return d3_layout_hierarchyRebind(pack, hierarchy);
9908 };
9909 function d3_layout_packSort(a, b) {
9910 return a.value - b.value;
9911 }
9912 function d3_layout_packInsert(a, b) {
9913 var c = a._pack_next;
9914 a._pack_next = b;
9915 b._pack_prev = a;
9916 b._pack_next = c;
9917 c._pack_prev = b;
9918 }
9919 function d3_layout_packSplice(a, b) {
9920 a._pack_next = b;
9921 b._pack_prev = a;
9922 }
9923 function d3_layout_packIntersects(a, b) {
9924 var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
9925 return .999 * dr * dr > dx * dx + dy * dy;
9926 }
9927 function d3_layout_packSiblings(node) {
9928 if (!(nodes = node.children) || !(n = nodes.length)) return;
9929 var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
9930 function bound(node) {
9931 xMin = Math.min(node.x - node.r, xMin);
9932 xMax = Math.max(node.x + node.r, xMax);
9933 yMin = Math.min(node.y - node.r, yMin);
9934 yMax = Math.max(node.y + node.r, yMax);
9935 }
9936 nodes.forEach(d3_layout_packLink);
9937 a = nodes[0];
9938 a.x = -a.r;
9939 a.y = 0;
9940 bound(a);
9941 if (n > 1) {
9942 b = nodes[1];
9943 b.x = b.r;
9944 b.y = 0;
9945 bound(b);
9946 if (n > 2) {
9947 c = nodes[2];
9948 d3_layout_packPlace(a, b, c);
9949 bound(c);
9950 d3_layout_packInsert(a, c);
9951 a._pack_prev = c;
9952 d3_layout_packInsert(c, b);
9953 b = a._pack_next;
9954 for (i = 3; i < n; i++) {
9955 d3_layout_packPlace(a, b, c = nodes[i]);
9956 var isect = 0, s1 = 1, s2 = 1;
9957 for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
9958 if (d3_layout_packIntersects(j, c)) {
9959 isect = 1;
9960 break;
9961 }
9962 }
9963 if (isect == 1) {
9964 for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
9965 if (d3_layout_packIntersects(k, c)) {
9966 break;
9967 }
9968 }
9969 }
9970 if (isect) {
9971 if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
9972 i--;
9973 } else {
9974 d3_layout_packInsert(a, c);
9975 b = c;
9976 bound(c);
9977 }
9978 }
9979 }
9980 }
9981 var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
9982 for (i = 0; i < n; i++) {
9983 c = nodes[i];
9984 c.x -= cx;
9985 c.y -= cy;
9986 cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
9987 }
9988 node.r = cr;
9989 nodes.forEach(d3_layout_packUnlink);
9990 }
9991 function d3_layout_packLink(node) {
9992 node._pack_next = node._pack_prev = node;
9993 }
9994 function d3_layout_packUnlink(node) {
9995 delete node._pack_next;
9996 delete node._pack_prev;
9997 }
9998 function d3_layout_packTransform(node, x, y, k) {
9999 var children = node.children;
10000 node.x = x += k * node.x;
10001 node.y = y += k * node.y;
10002 node.r *= k;
10003 if (children) {
10004 var i = -1, n = children.length;
10005 while (++i < n) d3_layout_packTransform(children[i], x, y, k);
10006 }
10007 }
10008 function d3_layout_packPlace(a, b, c) {
10009 var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
10010 if (db && (dx || dy)) {
10011 var da = b.r + c.r, dc = dx * dx + dy * dy;
10012 da *= da;
10013 db *= db;
10014 var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
10015 c.x = a.x + x * dx + y * dy;
10016 c.y = a.y + x * dy - y * dx;
10017 } else {
10018 c.x = a.x + db;
10019 c.y = a.y;
10020 }
10021 }
10022 d3.layout.tree = function() {
10023 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
10024 function tree(d, i) {
10025 var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
10026 d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
10027 d3_layout_hierarchyVisitBefore(root1, secondWalk);
10028 if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
10029 var left = root0, right = root0, bottom = root0;
10030 d3_layout_hierarchyVisitBefore(root0, function(node) {
10031 if (node.x < left.x) left = node;
10032 if (node.x > right.x) right = node;
10033 if (node.depth > bottom.depth) bottom = node;
10034 });
10035 var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
10036 d3_layout_hierarchyVisitBefore(root0, function(node) {
10037 node.x = (node.x + tx) * kx;
10038 node.y = node.depth * ky;
10039 });
10040 }
10041 return nodes;
10042 }
10043 function wrapTree(root0) {
10044 var root1 = {
10045 A: null,
10046 children: [ root0 ]
10047 }, queue = [ root1 ], node1;
10048 while ((node1 = queue.pop()) != null) {
10049 for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
10050 queue.push((children[i] = child = {
10051 _: children[i],
10052 parent: node1,
10053 children: (child = children[i].children) && child.slice() || [],
10054 A: null,
10055 a: null,
10056 z: 0,
10057 m: 0,
10058 c: 0,
10059 s: 0,
10060 t: null,
10061 i: i
10062 }).a = child);
10063 }
10064 }
10065 return root1.children[0];
10066 }
10067 function firstWalk(v) {
10068 var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
10069 if (children.length) {
10070 d3_layout_treeShift(v);
10071 var midpoint = (children[0].z + children[children.length - 1].z) / 2;
10072 if (w) {
10073 v.z = w.z + separation(v._, w._);
10074 v.m = v.z - midpoint;
10075 } else {
10076 v.z = midpoint;
10077 }
10078 } else if (w) {
10079 v.z = w.z + separation(v._, w._);
10080 }
10081 v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
10082 }
10083 function secondWalk(v) {
10084 v._.x = v.z + v.parent.m;
10085 v.m += v.parent.m;
10086 }
10087 function apportion(v, w, ancestor) {
10088 if (w) {
10089 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;
10090 while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
10091 vom = d3_layout_treeLeft(vom);
10092 vop = d3_layout_treeRight(vop);
10093 vop.a = v;
10094 shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
10095 if (shift > 0) {
10096 d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
10097 sip += shift;
10098 sop += shift;
10099 }
10100 sim += vim.m;
10101 sip += vip.m;
10102 som += vom.m;
10103 sop += vop.m;
10104 }
10105 if (vim && !d3_layout_treeRight(vop)) {
10106 vop.t = vim;
10107 vop.m += sim - sop;
10108 }
10109 if (vip && !d3_layout_treeLeft(vom)) {
10110 vom.t = vip;
10111 vom.m += sip - som;
10112 ancestor = v;
10113 }
10114 }
10115 return ancestor;
10116 }
10117 function sizeNode(node) {
10118 node.x *= size[0];
10119 node.y = node.depth * size[1];
10120 }
10121 tree.separation = function(x) {
10122 if (!arguments.length) return separation;
10123 separation = x;
10124 return tree;
10125 };
10126 tree.size = function(x) {
10127 if (!arguments.length) return nodeSize ? null : size;
10128 nodeSize = (size = x) == null ? sizeNode : null;
10129 return tree;
10130 };
10131 tree.nodeSize = function(x) {
10132 if (!arguments.length) return nodeSize ? size : null;
10133 nodeSize = (size = x) == null ? null : sizeNode;
10134 return tree;
10135 };
10136 return d3_layout_hierarchyRebind(tree, hierarchy);
10137 };
10138 function d3_layout_treeSeparation(a, b) {
10139 return a.parent == b.parent ? 1 : 2;
10140 }
10141 function d3_layout_treeLeft(v) {
10142 var children = v.children;
10143 return children.length ? children[0] : v.t;
10144 }
10145 function d3_layout_treeRight(v) {
10146 var children = v.children, n;
10147 return (n = children.length) ? children[n - 1] : v.t;
10148 }
10149 function d3_layout_treeMove(wm, wp, shift) {
10150 var change = shift / (wp.i - wm.i);
10151 wp.c -= change;
10152 wp.s += shift;
10153 wm.c += change;
10154 wp.z += shift;
10155 wp.m += shift;
10156 }
10157 function d3_layout_treeShift(v) {
10158 var shift = 0, change = 0, children = v.children, i = children.length, w;
10159 while (--i >= 0) {
10160 w = children[i];
10161 w.z += shift;
10162 w.m += shift;
10163 shift += w.s + (change += w.c);
10164 }
10165 }
10166 function d3_layout_treeAncestor(vim, v, ancestor) {
10167 return vim.a.parent === v.parent ? vim.a : ancestor;
10168 }
10169 d3.layout.cluster = function() {
10170 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
10171 function cluster(d, i) {
10172 var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
10173 d3_layout_hierarchyVisitAfter(root, function(node) {
10174 var children = node.children;
10175 if (children && children.length) {
10176 node.x = d3_layout_clusterX(children);
10177 node.y = d3_layout_clusterY(children);
10178 } else {
10179 node.x = previousNode ? x += separation(node, previousNode) : 0;
10180 node.y = 0;
10181 previousNode = node;
10182 }
10183 });
10184 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;
10185 d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
10186 node.x = (node.x - root.x) * size[0];
10187 node.y = (root.y - node.y) * size[1];
10188 } : function(node) {
10189 node.x = (node.x - x0) / (x1 - x0) * size[0];
10190 node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
10191 });
10192 return nodes;
10193 }
10194 cluster.separation = function(x) {
10195 if (!arguments.length) return separation;
10196 separation = x;
10197 return cluster;
10198 };
10199 cluster.size = function(x) {
10200 if (!arguments.length) return nodeSize ? null : size;
10201 nodeSize = (size = x) == null;
10202 return cluster;
10203 };
10204 cluster.nodeSize = function(x) {
10205 if (!arguments.length) return nodeSize ? size : null;
10206 nodeSize = (size = x) != null;
10207 return cluster;
10208 };
10209 return d3_layout_hierarchyRebind(cluster, hierarchy);
10210 };
10211 function d3_layout_clusterY(children) {
10212 return 1 + d3.max(children, function(child) {
10213 return child.y;
10214 });
10215 }
10216 function d3_layout_clusterX(children) {
10217 return children.reduce(function(x, child) {
10218 return x + child.x;
10219 }, 0) / children.length;
10220 }
10221 function d3_layout_clusterLeft(node) {
10222 var children = node.children;
10223 return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
10224 }
10225 function d3_layout_clusterRight(node) {
10226 var children = node.children, n;
10227 return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
10228 }
10229 d3.layout.treemap = function() {
10230 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));
10231 function scale(children, k) {
10232 var i = -1, n = children.length, child, area;
10233 while (++i < n) {
10234 area = (child = children[i]).value * (k < 0 ? 0 : k);
10235 child.area = isNaN(area) || area <= 0 ? 0 : area;
10236 }
10237 }
10238 function squarify(node) {
10239 var children = node.children;
10240 if (children && children.length) {
10241 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;
10242 scale(remaining, rect.dx * rect.dy / node.value);
10243 row.area = 0;
10244 while ((n = remaining.length) > 0) {
10245 row.push(child = remaining[n - 1]);
10246 row.area += child.area;
10247 if (mode !== "squarify" || (score = worst(row, u)) <= best) {
10248 remaining.pop();
10249 best = score;
10250 } else {
10251 row.area -= row.pop().area;
10252 position(row, u, rect, false);
10253 u = Math.min(rect.dx, rect.dy);
10254 row.length = row.area = 0;
10255 best = Infinity;
10256 }
10257 }
10258 if (row.length) {
10259 position(row, u, rect, true);
10260 row.length = row.area = 0;
10261 }
10262 children.forEach(squarify);
10263 }
10264 }
10265 function stickify(node) {
10266 var children = node.children;
10267 if (children && children.length) {
10268 var rect = pad(node), remaining = children.slice(), child, row = [];
10269 scale(remaining, rect.dx * rect.dy / node.value);
10270 row.area = 0;
10271 while (child = remaining.pop()) {
10272 row.push(child);
10273 row.area += child.area;
10274 if (child.z != null) {
10275 position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
10276 row.length = row.area = 0;
10277 }
10278 }
10279 children.forEach(stickify);
10280 }
10281 }
10282 function worst(row, u) {
10283 var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
10284 while (++i < n) {
10285 if (!(r = row[i].area)) continue;
10286 if (r < rmin) rmin = r;
10287 if (r > rmax) rmax = r;
10288 }
10289 s *= s;
10290 u *= u;
10291 return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
10292 }
10293 function position(row, u, rect, flush) {
10294 var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
10295 if (u == rect.dx) {
10296 if (flush || v > rect.dy) v = rect.dy;
10297 while (++i < n) {
10298 o = row[i];
10299 o.x = x;
10300 o.y = y;
10301 o.dy = v;
10302 x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
10303 }
10304 o.z = true;
10305 o.dx += rect.x + rect.dx - x;
10306 rect.y += v;
10307 rect.dy -= v;
10308 } else {
10309 if (flush || v > rect.dx) v = rect.dx;
10310 while (++i < n) {
10311 o = row[i];
10312 o.x = x;
10313 o.y = y;
10314 o.dx = v;
10315 y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
10316 }
10317 o.z = false;
10318 o.dy += rect.y + rect.dy - y;
10319 rect.x += v;
10320 rect.dx -= v;
10321 }
10322 }
10323 function treemap(d) {
10324 var nodes = stickies || hierarchy(d), root = nodes[0];
10325 root.x = root.y = 0;
10326 if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0;
10327 if (stickies) hierarchy.revalue(root);
10328 scale([ root ], root.dx * root.dy / root.value);
10329 (stickies ? stickify : squarify)(root);
10330 if (sticky) stickies = nodes;
10331 return nodes;
10332 }
10333 treemap.size = function(x) {
10334 if (!arguments.length) return size;
10335 size = x;
10336 return treemap;
10337 };
10338 treemap.padding = function(x) {
10339 if (!arguments.length) return padding;
10340 function padFunction(node) {
10341 var p = x.call(treemap, node, node.depth);
10342 return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
10343 }
10344 function padConstant(node) {
10345 return d3_layout_treemapPad(node, x);
10346 }
10347 var type;
10348 pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
10349 padConstant) : padConstant;
10350 return treemap;
10351 };
10352 treemap.round = function(x) {
10353 if (!arguments.length) return round != Number;
10354 round = x ? Math.round : Number;
10355 return treemap;
10356 };
10357 treemap.sticky = function(x) {
10358 if (!arguments.length) return sticky;
10359 sticky = x;
10360 stickies = null;
10361 return treemap;
10362 };
10363 treemap.ratio = function(x) {
10364 if (!arguments.length) return ratio;
10365 ratio = x;
10366 return treemap;
10367 };
10368 treemap.mode = function(x) {
10369 if (!arguments.length) return mode;
10370 mode = x + "";
10371 return treemap;
10372 };
10373 return d3_layout_hierarchyRebind(treemap, hierarchy);
10374 };
10375 function d3_layout_treemapPadNull(node) {
10376 return {
10377 x: node.x,
10378 y: node.y,
10379 dx: node.dx,
10380 dy: node.dy
10381 };
10382 }
10383 function d3_layout_treemapPad(node, padding) {
10384 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];
10385 if (dx < 0) {
10386 x += dx / 2;
10387 dx = 0;
10388 }
10389 if (dy < 0) {
10390 y += dy / 2;
10391 dy = 0;
10392 }
10393 return {
10394 x: x,
10395 y: y,
10396 dx: dx,
10397 dy: dy
10398 };
10399 }
10400 d3.random = {
10401 normal: function(µ, σ) {
10402 var n = arguments.length;
10403 if (n < 2) σ = 1;
10404 if (n < 1) µ = 0;
10405 return function() {
10406 var x, y, r;
10407 do {
10408 x = Math.random() * 2 - 1;
10409 y = Math.random() * 2 - 1;
10410 r = x * x + y * y;
10411 } while (!r || r > 1);
10412 return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
10413 };
10414 },
10415 logNormal: function() {
10416 var random = d3.random.normal.apply(d3, arguments);
10417 return function() {
10418 return Math.exp(random());
10419 };
10420 },
10421 bates: function(m) {
10422 var random = d3.random.irwinHall(m);
10423 return function() {
10424 return random() / m;
10425 };
10426 },
10427 irwinHall: function(m) {
10428 return function() {
10429 for (var s = 0, j = 0; j < m; j++) s += Math.random();
10430 return s;
10431 };
10432 }
10433 };
10434 d3.scale = {};
10435 function d3_scaleExtent(domain) {
10436 var start = domain[0], stop = domain[domain.length - 1];
10437 return start < stop ? [ start, stop ] : [ stop, start ];
10438 }
10439 function d3_scaleRange(scale) {
10440 return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
10441 }
10442 function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
10443 var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
10444 return function(x) {
10445 return i(u(x));
10446 };
10447 }
10448 function d3_scale_nice(domain, nice) {
10449 var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
10450 if (x1 < x0) {
10451 dx = i0, i0 = i1, i1 = dx;
10452 dx = x0, x0 = x1, x1 = dx;
10453 }
10454 domain[i0] = nice.floor(x0);
10455 domain[i1] = nice.ceil(x1);
10456 return domain;
10457 }
10458 function d3_scale_niceStep(step) {
10459 return step ? {
10460 floor: function(x) {
10461 return Math.floor(x / step) * step;
10462 },
10463 ceil: function(x) {
10464 return Math.ceil(x / step) * step;
10465 }
10466 } : d3_scale_niceIdentity;
10467 }
10468 var d3_scale_niceIdentity = {
10469 floor: d3_identity,
10470 ceil: d3_identity
10471 };
10472 function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
10473 var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
10474 if (domain[k] < domain[0]) {
10475 domain = domain.slice().reverse();
10476 range = range.slice().reverse();
10477 }
10478 while (++j <= k) {
10479 u.push(uninterpolate(domain[j - 1], domain[j]));
10480 i.push(interpolate(range[j - 1], range[j]));
10481 }
10482 return function(x) {
10483 var j = d3.bisect(domain, x, 1, k) - 1;
10484 return i[j](u[j](x));
10485 };
10486 }
10487 d3.scale.linear = function() {
10488 return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
10489 };
10490 function d3_scale_linear(domain, range, interpolate, clamp) {
10491 var output, input;
10492 function rescale() {
10493 var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
10494 output = linear(domain, range, uninterpolate, interpolate);
10495 input = linear(range, domain, uninterpolate, d3_interpolate);
10496 return scale;
10497 }
10498 function scale(x) {
10499 return output(x);
10500 }
10501 scale.invert = function(y) {
10502 return input(y);
10503 };
10504 scale.domain = function(x) {
10505 if (!arguments.length) return domain;
10506 domain = x.map(Number);
10507 return rescale();
10508 };
10509 scale.range = function(x) {
10510 if (!arguments.length) return range;
10511 range = x;
10512 return rescale();
10513 };
10514 scale.rangeRound = function(x) {
10515 return scale.range(x).interpolate(d3_interpolateRound);
10516 };
10517 scale.clamp = function(x) {
10518 if (!arguments.length) return clamp;
10519 clamp = x;
10520 return rescale();
10521 };
10522 scale.interpolate = function(x) {
10523 if (!arguments.length) return interpolate;
10524 interpolate = x;
10525 return rescale();
10526 };
10527 scale.ticks = function(m) {
10528 return d3_scale_linearTicks(domain, m);
10529 };
10530 scale.tickFormat = function(m, format) {
10531 return d3_scale_linearTickFormat(domain, m, format);
10532 };
10533 scale.nice = function(m) {
10534 d3_scale_linearNice(domain, m);
10535 return rescale();
10536 };
10537 scale.copy = function() {
10538 return d3_scale_linear(domain, range, interpolate, clamp);
10539 };
10540 return rescale();
10541 }
10542 function d3_scale_linearRebind(scale, linear) {
10543 return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
10544 }
10545 function d3_scale_linearNice(domain, m) {
10546 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
10547 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
10548 return domain;
10549 }
10550 function d3_scale_linearTickRange(domain, m) {
10551 if (m == null) m = 10;
10552 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;
10553 if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
10554 extent[0] = Math.ceil(extent[0] / step) * step;
10555 extent[1] = Math.floor(extent[1] / step) * step + step * .5;
10556 extent[2] = step;
10557 return extent;
10558 }
10559 function d3_scale_linearTicks(domain, m) {
10560 return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
10561 }
10562 function d3_scale_linearTickFormat(domain, m, format) {
10563 var range = d3_scale_linearTickRange(domain, m);
10564 if (format) {
10565 var match = d3_format_re.exec(format);
10566 match.shift();
10567 if (match[8] === "s") {
10568 var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
10569 if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
10570 match[8] = "f";
10571 format = d3.format(match.join(""));
10572 return function(d) {
10573 return format(prefix.scale(d)) + prefix.symbol;
10574 };
10575 }
10576 if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
10577 format = match.join("");
10578 } else {
10579 format = ",." + d3_scale_linearPrecision(range[2]) + "f";
10580 }
10581 return d3.format(format);
10582 }
10583 var d3_scale_linearFormatSignificant = {
10584 s: 1,
10585 g: 1,
10586 p: 1,
10587 r: 1,
10588 e: 1
10589 };
10590 function d3_scale_linearPrecision(value) {
10591 return -Math.floor(Math.log(value) / Math.LN10 + .01);
10592 }
10593 function d3_scale_linearFormatPrecision(type, range) {
10594 var p = d3_scale_linearPrecision(range[2]);
10595 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;
10596 }
10597 d3.scale.log = function() {
10598 return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
10599 };
10600 function d3_scale_log(linear, base, positive, domain) {
10601 function log(x) {
10602 return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
10603 }
10604 function pow(x) {
10605 return positive ? Math.pow(base, x) : -Math.pow(base, -x);
10606 }
10607 function scale(x) {
10608 return linear(log(x));
10609 }
10610 scale.invert = function(x) {
10611 return pow(linear.invert(x));
10612 };
10613 scale.domain = function(x) {
10614 if (!arguments.length) return domain;
10615 positive = x[0] >= 0;
10616 linear.domain((domain = x.map(Number)).map(log));
10617 return scale;
10618 };
10619 scale.base = function(_) {
10620 if (!arguments.length) return base;
10621 base = +_;
10622 linear.domain(domain.map(log));
10623 return scale;
10624 };
10625 scale.nice = function() {
10626 var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
10627 linear.domain(niced);
10628 domain = niced.map(pow);
10629 return scale;
10630 };
10631 scale.ticks = function() {
10632 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;
10633 if (isFinite(j - i)) {
10634 if (positive) {
10635 for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
10636 ticks.push(pow(i));
10637 } else {
10638 ticks.push(pow(i));
10639 for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
10640 }
10641 for (i = 0; ticks[i] < u; i++) {}
10642 for (j = ticks.length; ticks[j - 1] > v; j--) {}
10643 ticks = ticks.slice(i, j);
10644 }
10645 return ticks;
10646 };
10647 scale.tickFormat = function(n, format) {
10648 if (!arguments.length) return d3_scale_logFormat;
10649 if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
10650 var k = Math.max(1, base * n / scale.ticks().length);
10651 return function(d) {
10652 var i = d / pow(Math.round(log(d)));
10653 if (i * base < base - .5) i *= base;
10654 return i <= k ? format(d) : "";
10655 };
10656 };
10657 scale.copy = function() {
10658 return d3_scale_log(linear.copy(), base, positive, domain);
10659 };
10660 return d3_scale_linearRebind(scale, linear);
10661 }
10662 var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
10663 floor: function(x) {
10664 return -Math.ceil(-x);
10665 },
10666 ceil: function(x) {
10667 return -Math.floor(-x);
10668 }
10669 };
10670 d3.scale.pow = function() {
10671 return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
10672 };
10673 function d3_scale_pow(linear, exponent, domain) {
10674 var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
10675 function scale(x) {
10676 return linear(powp(x));
10677 }
10678 scale.invert = function(x) {
10679 return powb(linear.invert(x));
10680 };
10681 scale.domain = function(x) {
10682 if (!arguments.length) return domain;
10683 linear.domain((domain = x.map(Number)).map(powp));
10684 return scale;
10685 };
10686 scale.ticks = function(m) {
10687 return d3_scale_linearTicks(domain, m);
10688 };
10689 scale.tickFormat = function(m, format) {
10690 return d3_scale_linearTickFormat(domain, m, format);
10691 };
10692 scale.nice = function(m) {
10693 return scale.domain(d3_scale_linearNice(domain, m));
10694 };
10695 scale.exponent = function(x) {
10696 if (!arguments.length) return exponent;
10697 powp = d3_scale_powPow(exponent = x);
10698 powb = d3_scale_powPow(1 / exponent);
10699 linear.domain(domain.map(powp));
10700 return scale;
10701 };
10702 scale.copy = function() {
10703 return d3_scale_pow(linear.copy(), exponent, domain);
10704 };
10705 return d3_scale_linearRebind(scale, linear);
10706 }
10707 function d3_scale_powPow(e) {
10708 return function(x) {
10709 return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
10710 };
10711 }
10712 d3.scale.sqrt = function() {
10713 return d3.scale.pow().exponent(.5);
10714 };
10715 d3.scale.ordinal = function() {
10716 return d3_scale_ordinal([], {
10717 t: "range",
10718 a: [ [] ]
10719 });
10720 };
10721 function d3_scale_ordinal(domain, ranger) {
10722 var index, range, rangeBand;
10723 function scale(x) {
10724 return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
10725 }
10726 function steps(start, step) {
10727 return d3.range(domain.length).map(function(i) {
10728 return start + step * i;
10729 });
10730 }
10731 scale.domain = function(x) {
10732 if (!arguments.length) return domain;
10733 domain = [];
10734 index = new d3_Map();
10735 var i = -1, n = x.length, xi;
10736 while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
10737 return scale[ranger.t].apply(scale, ranger.a);
10738 };
10739 scale.range = function(x) {
10740 if (!arguments.length) return range;
10741 range = x;
10742 rangeBand = 0;
10743 ranger = {
10744 t: "range",
10745 a: arguments
10746 };
10747 return scale;
10748 };
10749 scale.rangePoints = function(x, padding) {
10750 if (arguments.length < 2) padding = 0;
10751 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2,
10752 0) : (stop - start) / (domain.length - 1 + padding);
10753 range = steps(start + step * padding / 2, step);
10754 rangeBand = 0;
10755 ranger = {
10756 t: "rangePoints",
10757 a: arguments
10758 };
10759 return scale;
10760 };
10761 scale.rangeRoundPoints = function(x, padding) {
10762 if (arguments.length < 2) padding = 0;
10763 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2),
10764 0) : (stop - start) / (domain.length - 1 + padding) | 0;
10765 range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
10766 rangeBand = 0;
10767 ranger = {
10768 t: "rangeRoundPoints",
10769 a: arguments
10770 };
10771 return scale;
10772 };
10773 scale.rangeBands = function(x, padding, outerPadding) {
10774 if (arguments.length < 2) padding = 0;
10775 if (arguments.length < 3) outerPadding = padding;
10776 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
10777 range = steps(start + step * outerPadding, step);
10778 if (reverse) range.reverse();
10779 rangeBand = step * (1 - padding);
10780 ranger = {
10781 t: "rangeBands",
10782 a: arguments
10783 };
10784 return scale;
10785 };
10786 scale.rangeRoundBands = function(x, padding, outerPadding) {
10787 if (arguments.length < 2) padding = 0;
10788 if (arguments.length < 3) outerPadding = padding;
10789 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
10790 range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
10791 if (reverse) range.reverse();
10792 rangeBand = Math.round(step * (1 - padding));
10793 ranger = {
10794 t: "rangeRoundBands",
10795 a: arguments
10796 };
10797 return scale;
10798 };
10799 scale.rangeBand = function() {
10800 return rangeBand;
10801 };
10802 scale.rangeExtent = function() {
10803 return d3_scaleExtent(ranger.a[0]);
10804 };
10805 scale.copy = function() {
10806 return d3_scale_ordinal(domain, ranger);
10807 };
10808 return scale.domain(domain);
10809 }
10810 d3.scale.category10 = function() {
10811 return d3.scale.ordinal().range(d3_category10);
10812 };
10813 d3.scale.category20 = function() {
10814 return d3.scale.ordinal().range(d3_category20);
10815 };
10816 d3.scale.category20b = function() {
10817 return d3.scale.ordinal().range(d3_category20b);
10818 };
10819 d3.scale.category20c = function() {
10820 return d3.scale.ordinal().range(d3_category20c);
10821 };
10822 var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
10823 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);
10824 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);
10825 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);
10826 d3.scale.quantile = function() {
10827 return d3_scale_quantile([], []);
10828 };
10829 function d3_scale_quantile(domain, range) {
10830 var thresholds;
10831 function rescale() {
10832 var k = 0, q = range.length;
10833 thresholds = [];
10834 while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
10835 return scale;
10836 }
10837 function scale(x) {
10838 if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
10839 }
10840 scale.domain = function(x) {
10841 if (!arguments.length) return domain;
10842 domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
10843 return rescale();
10844 };
10845 scale.range = function(x) {
10846 if (!arguments.length) return range;
10847 range = x;
10848 return rescale();
10849 };
10850 scale.quantiles = function() {
10851 return thresholds;
10852 };
10853 scale.invertExtent = function(y) {
10854 y = range.indexOf(y);
10855 return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
10856 };
10857 scale.copy = function() {
10858 return d3_scale_quantile(domain, range);
10859 };
10860 return rescale();
10861 }
10862 d3.scale.quantize = function() {
10863 return d3_scale_quantize(0, 1, [ 0, 1 ]);
10864 };
10865 function d3_scale_quantize(x0, x1, range) {
10866 var kx, i;
10867 function scale(x) {
10868 return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
10869 }
10870 function rescale() {
10871 kx = range.length / (x1 - x0);
10872 i = range.length - 1;
10873 return scale;
10874 }
10875 scale.domain = function(x) {
10876 if (!arguments.length) return [ x0, x1 ];
10877 x0 = +x[0];
10878 x1 = +x[x.length - 1];
10879 return rescale();
10880 };
10881 scale.range = function(x) {
10882 if (!arguments.length) return range;
10883 range = x;
10884 return rescale();
10885 };
10886 scale.invertExtent = function(y) {
10887 y = range.indexOf(y);
10888 y = y < 0 ? NaN : y / kx + x0;
10889 return [ y, y + 1 / kx ];
10890 };
10891 scale.copy = function() {
10892 return d3_scale_quantize(x0, x1, range);
10893 };
10894 return rescale();
10895 }
10896 d3.scale.threshold = function() {
10897 return d3_scale_threshold([ .5 ], [ 0, 1 ]);
10898 };
10899 function d3_scale_threshold(domain, range) {
10900 function scale(x) {
10901 if (x <= x) return range[d3.bisect(domain, x)];
10902 }
10903 scale.domain = function(_) {
10904 if (!arguments.length) return domain;
10905 domain = _;
10906 return scale;
10907 };
10908 scale.range = function(_) {
10909 if (!arguments.length) return range;
10910 range = _;
10911 return scale;
10912 };
10913 scale.invertExtent = function(y) {
10914 y = range.indexOf(y);
10915 return [ domain[y - 1], domain[y] ];
10916 };
10917 scale.copy = function() {
10918 return d3_scale_threshold(domain, range);
10919 };
10920 return scale;
10921 }
10922 d3.scale.identity = function() {
10923 return d3_scale_identity([ 0, 1 ]);
10924 };
10925 function d3_scale_identity(domain) {
10926 function identity(x) {
10927 return +x;
10928 }
10929 identity.invert = identity;
10930 identity.domain = identity.range = function(x) {
10931 if (!arguments.length) return domain;
10932 domain = x.map(identity);
10933 return identity;
10934 };
10935 identity.ticks = function(m) {
10936 return d3_scale_linearTicks(domain, m);
10937 };
10938 identity.tickFormat = function(m, format) {
10939 return d3_scale_linearTickFormat(domain, m, format);
10940 };
10941 identity.copy = function() {
10942 return d3_scale_identity(domain);
10943 };
10944 return identity;
10945 }
10946 d3.svg = {};
10947 function d3_zero() {
10948 return 0;
10949 }
10950 d3.svg.arc = function() {
10951 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;
10952 function arc() {
10953 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;
10954 if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
10955 if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
10956 var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
10957 if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
10958 rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
10959 if (!cw) p1 *= -1;
10960 if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
10961 if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
10962 }
10963 if (r1) {
10964 x0 = r1 * Math.cos(a0 + p1);
10965 y0 = r1 * Math.sin(a0 + p1);
10966 x1 = r1 * Math.cos(a1 - p1);
10967 y1 = r1 * Math.sin(a1 - p1);
10968 var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
10969 if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
10970 var h1 = (a0 + a1) / 2;
10971 x0 = r1 * Math.cos(h1);
10972 y0 = r1 * Math.sin(h1);
10973 x1 = y1 = null;
10974 }
10975 } else {
10976 x0 = y0 = 0;
10977 }
10978 if (r0) {
10979 x2 = r0 * Math.cos(a1 - p0);
10980 y2 = r0 * Math.sin(a1 - p0);
10981 x3 = r0 * Math.cos(a0 + p0);
10982 y3 = r0 * Math.sin(a0 + p0);
10983 var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
10984 if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
10985 var h0 = (a0 + a1) / 2;
10986 x2 = r0 * Math.cos(h0);
10987 y2 = r0 * Math.sin(h0);
10988 x3 = y3 = null;
10989 }
10990 } else {
10991 x2 = y2 = 0;
10992 }
10993 if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
10994 cr = r0 < r1 ^ cw ? 0 : 1;
10995 var rc1 = rc, rc0 = rc;
10996 if (da < π) {
10997 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]);
10998 rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
10999 rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
11000 }
11001 if (x1 != null) {
11002 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);
11003 if (rc === rc1) {
11004 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]);
11005 } else {
11006 path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
11007 }
11008 } else {
11009 path.push("M", x0, ",", y0);
11010 }
11011 if (x3 != null) {
11012 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);
11013 if (rc === rc0) {
11014 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]);
11015 } else {
11016 path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
11017 }
11018 } else {
11019 path.push("L", x2, ",", y2);
11020 }
11021 } else {
11022 path.push("M", x0, ",", y0);
11023 if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
11024 path.push("L", x2, ",", y2);
11025 if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
11026 }
11027 path.push("Z");
11028 return path.join("");
11029 }
11030 function circleSegment(r1, cw) {
11031 return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
11032 }
11033 arc.innerRadius = function(v) {
11034 if (!arguments.length) return innerRadius;
11035 innerRadius = d3_functor(v);
11036 return arc;
11037 };
11038 arc.outerRadius = function(v) {
11039 if (!arguments.length) return outerRadius;
11040 outerRadius = d3_functor(v);
11041 return arc;
11042 };
11043 arc.cornerRadius = function(v) {
11044 if (!arguments.length) return cornerRadius;
11045 cornerRadius = d3_functor(v);
11046 return arc;
11047 };
11048 arc.padRadius = function(v) {
11049 if (!arguments.length) return padRadius;
11050 padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
11051 return arc;
11052 };
11053 arc.startAngle = function(v) {
11054 if (!arguments.length) return startAngle;
11055 startAngle = d3_functor(v);
11056 return arc;
11057 };
11058 arc.endAngle = function(v) {
11059 if (!arguments.length) return endAngle;
11060 endAngle = d3_functor(v);
11061 return arc;
11062 };
11063 arc.padAngle = function(v) {
11064 if (!arguments.length) return padAngle;
11065 padAngle = d3_functor(v);
11066 return arc;
11067 };
11068 arc.centroid = function() {
11069 var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
11070 return [ Math.cos(a) * r, Math.sin(a) * r ];
11071 };
11072 return arc;
11073 };
11074 var d3_svg_arcAuto = "auto";
11075 function d3_svg_arcInnerRadius(d) {
11076 return d.innerRadius;
11077 }
11078 function d3_svg_arcOuterRadius(d) {
11079 return d.outerRadius;
11080 }
11081 function d3_svg_arcStartAngle(d) {
11082 return d.startAngle;
11083 }
11084 function d3_svg_arcEndAngle(d) {
11085 return d.endAngle;
11086 }
11087 function d3_svg_arcPadAngle(d) {
11088 return d && d.padAngle;
11089 }
11090 function d3_svg_arcSweep(x0, y0, x1, y1) {
11091 return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
11092 }
11093 function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
11094 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;
11095 if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
11096 return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
11097 }
11098 function d3_svg_line(projection) {
11099 var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
11100 function line(data) {
11101 var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
11102 function segment() {
11103 segments.push("M", interpolate(projection(points), tension));
11104 }
11105 while (++i < n) {
11106 if (defined.call(this, d = data[i], i)) {
11107 points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
11108 } else if (points.length) {
11109 segment();
11110 points = [];
11111 }
11112 }
11113 if (points.length) segment();
11114 return segments.length ? segments.join("") : null;
11115 }
11116 line.x = function(_) {
11117 if (!arguments.length) return x;
11118 x = _;
11119 return line;
11120 };
11121 line.y = function(_) {
11122 if (!arguments.length) return y;
11123 y = _;
11124 return line;
11125 };
11126 line.defined = function(_) {
11127 if (!arguments.length) return defined;
11128 defined = _;
11129 return line;
11130 };
11131 line.interpolate = function(_) {
11132 if (!arguments.length) return interpolateKey;
11133 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
11134 return line;
11135 };
11136 line.tension = function(_) {
11137 if (!arguments.length) return tension;
11138 tension = _;
11139 return line;
11140 };
11141 return line;
11142 }
11143 d3.svg.line = function() {
11144 return d3_svg_line(d3_identity);
11145 };
11146 var d3_svg_lineInterpolators = d3.map({
11147 linear: d3_svg_lineLinear,
11148 "linear-closed": d3_svg_lineLinearClosed,
11149 step: d3_svg_lineStep,
11150 "step-before": d3_svg_lineStepBefore,
11151 "step-after": d3_svg_lineStepAfter,
11152 basis: d3_svg_lineBasis,
11153 "basis-open": d3_svg_lineBasisOpen,
11154 "basis-closed": d3_svg_lineBasisClosed,
11155 bundle: d3_svg_lineBundle,
11156 cardinal: d3_svg_lineCardinal,
11157 "cardinal-open": d3_svg_lineCardinalOpen,
11158 "cardinal-closed": d3_svg_lineCardinalClosed,
11159 monotone: d3_svg_lineMonotone
11160 });
11161 d3_svg_lineInterpolators.forEach(function(key, value) {
11162 value.key = key;
11163 value.closed = /-closed$/.test(key);
11164 });
11165 function d3_svg_lineLinear(points) {
11166 return points.length > 1 ? points.join("L") : points + "Z";
11167 }
11168 function d3_svg_lineLinearClosed(points) {
11169 return points.join("L") + "Z";
11170 }
11171 function d3_svg_lineStep(points) {
11172 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
11173 while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
11174 if (n > 1) path.push("H", p[0]);
11175 return path.join("");
11176 }
11177 function d3_svg_lineStepBefore(points) {
11178 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
11179 while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
11180 return path.join("");
11181 }
11182 function d3_svg_lineStepAfter(points) {
11183 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
11184 while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
11185 return path.join("");
11186 }
11187 function d3_svg_lineCardinalOpen(points, tension) {
11188 return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
11189 }
11190 function d3_svg_lineCardinalClosed(points, tension) {
11191 return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
11192 points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
11193 }
11194 function d3_svg_lineCardinal(points, tension) {
11195 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
11196 }
11197 function d3_svg_lineHermite(points, tangents) {
11198 if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
11199 return d3_svg_lineLinear(points);
11200 }
11201 var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
11202 if (quad) {
11203 path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
11204 p0 = points[1];
11205 pi = 2;
11206 }
11207 if (tangents.length > 1) {
11208 t = tangents[1];
11209 p = points[pi];
11210 pi++;
11211 path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
11212 for (var i = 2; i < tangents.length; i++, pi++) {
11213 p = points[pi];
11214 t = tangents[i];
11215 path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
11216 }
11217 }
11218 if (quad) {
11219 var lp = points[pi];
11220 path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
11221 }
11222 return path;
11223 }
11224 function d3_svg_lineCardinalTangents(points, tension) {
11225 var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
11226 while (++i < n) {
11227 p0 = p1;
11228 p1 = p2;
11229 p2 = points[i];
11230 tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
11231 }
11232 return tangents;
11233 }
11234 function d3_svg_lineBasis(points) {
11235 if (points.length < 3) return d3_svg_lineLinear(points);
11236 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) ];
11237 points.push(points[n - 1]);
11238 while (++i <= n) {
11239 pi = points[i];
11240 px.shift();
11241 px.push(pi[0]);
11242 py.shift();
11243 py.push(pi[1]);
11244 d3_svg_lineBasisBezier(path, px, py);
11245 }
11246 points.pop();
11247 path.push("L", pi);
11248 return path.join("");
11249 }
11250 function d3_svg_lineBasisOpen(points) {
11251 if (points.length < 4) return d3_svg_lineLinear(points);
11252 var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
11253 while (++i < 3) {
11254 pi = points[i];
11255 px.push(pi[0]);
11256 py.push(pi[1]);
11257 }
11258 path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
11259 --i;
11260 while (++i < n) {
11261 pi = points[i];
11262 px.shift();
11263 px.push(pi[0]);
11264 py.shift();
11265 py.push(pi[1]);
11266 d3_svg_lineBasisBezier(path, px, py);
11267 }
11268 return path.join("");
11269 }
11270 function d3_svg_lineBasisClosed(points) {
11271 var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
11272 while (++i < 4) {
11273 pi = points[i % n];
11274 px.push(pi[0]);
11275 py.push(pi[1]);
11276 }
11277 path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
11278 --i;
11279 while (++i < m) {
11280 pi = points[i % n];
11281 px.shift();
11282 px.push(pi[0]);
11283 py.shift();
11284 py.push(pi[1]);
11285 d3_svg_lineBasisBezier(path, px, py);
11286 }
11287 return path.join("");
11288 }
11289 function d3_svg_lineBundle(points, tension) {
11290 var n = points.length - 1;
11291 if (n) {
11292 var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
11293 while (++i <= n) {
11294 p = points[i];
11295 t = i / n;
11296 p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
11297 p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
11298 }
11299 }
11300 return d3_svg_lineBasis(points);
11301 }
11302 function d3_svg_lineDot4(a, b) {
11303 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
11304 }
11305 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 ];
11306 function d3_svg_lineBasisBezier(path, x, y) {
11307 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));
11308 }
11309 function d3_svg_lineSlope(p0, p1) {
11310 return (p1[1] - p0[1]) / (p1[0] - p0[0]);
11311 }
11312 function d3_svg_lineFiniteDifferences(points) {
11313 var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
11314 while (++i < j) {
11315 m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
11316 }
11317 m[i] = d;
11318 return m;
11319 }
11320 function d3_svg_lineMonotoneTangents(points) {
11321 var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
11322 while (++i < j) {
11323 d = d3_svg_lineSlope(points[i], points[i + 1]);
11324 if (abs(d) < ε) {
11325 m[i] = m[i + 1] = 0;
11326 } else {
11327 a = m[i] / d;
11328 b = m[i + 1] / d;
11329 s = a * a + b * b;
11330 if (s > 9) {
11331 s = d * 3 / Math.sqrt(s);
11332 m[i] = s * a;
11333 m[i + 1] = s * b;
11334 }
11335 }
11336 }
11337 i = -1;
11338 while (++i <= j) {
11339 s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
11340 tangents.push([ s || 0, m[i] * s || 0 ]);
11341 }
11342 return tangents;
11343 }
11344 function d3_svg_lineMonotone(points) {
11345 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
11346 }
11347 d3.svg.line.radial = function() {
11348 var line = d3_svg_line(d3_svg_lineRadial);
11349 line.radius = line.x, delete line.x;
11350 line.angle = line.y, delete line.y;
11351 return line;
11352 };
11353 function d3_svg_lineRadial(points) {
11354 var point, i = -1, n = points.length, r, a;
11355 while (++i < n) {
11356 point = points[i];
11357 r = point[0];
11358 a = point[1] - halfπ;
11359 point[0] = r * Math.cos(a);
11360 point[1] = r * Math.sin(a);
11361 }
11362 return points;
11363 }
11364 function d3_svg_area(projection) {
11365 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;
11366 function area(data) {
11367 var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
11368 return x;
11369 } : d3_functor(x1), fy1 = y0 === y1 ? function() {
11370 return y;
11371 } : d3_functor(y1), x, y;
11372 function segment() {
11373 segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
11374 }
11375 while (++i < n) {
11376 if (defined.call(this, d = data[i], i)) {
11377 points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
11378 points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
11379 } else if (points0.length) {
11380 segment();
11381 points0 = [];
11382 points1 = [];
11383 }
11384 }
11385 if (points0.length) segment();
11386 return segments.length ? segments.join("") : null;
11387 }
11388 area.x = function(_) {
11389 if (!arguments.length) return x1;
11390 x0 = x1 = _;
11391 return area;
11392 };
11393 area.x0 = function(_) {
11394 if (!arguments.length) return x0;
11395 x0 = _;
11396 return area;
11397 };
11398 area.x1 = function(_) {
11399 if (!arguments.length) return x1;
11400 x1 = _;
11401 return area;
11402 };
11403 area.y = function(_) {
11404 if (!arguments.length) return y1;
11405 y0 = y1 = _;
11406 return area;
11407 };
11408 area.y0 = function(_) {
11409 if (!arguments.length) return y0;
11410 y0 = _;
11411 return area;
11412 };
11413 area.y1 = function(_) {
11414 if (!arguments.length) return y1;
11415 y1 = _;
11416 return area;
11417 };
11418 area.defined = function(_) {
11419 if (!arguments.length) return defined;
11420 defined = _;
11421 return area;
11422 };
11423 area.interpolate = function(_) {
11424 if (!arguments.length) return interpolateKey;
11425 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
11426 interpolateReverse = interpolate.reverse || interpolate;
11427 L = interpolate.closed ? "M" : "L";
11428 return area;
11429 };
11430 area.tension = function(_) {
11431 if (!arguments.length) return tension;
11432 tension = _;
11433 return area;
11434 };
11435 return area;
11436 }
11437 d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
11438 d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
11439 d3.svg.area = function() {
11440 return d3_svg_area(d3_identity);
11441 };
11442 d3.svg.area.radial = function() {
11443 var area = d3_svg_area(d3_svg_lineRadial);
11444 area.radius = area.x, delete area.x;
11445 area.innerRadius = area.x0, delete area.x0;
11446 area.outerRadius = area.x1, delete area.x1;
11447 area.angle = area.y, delete area.y;
11448 area.startAngle = area.y0, delete area.y0;
11449 area.endAngle = area.y1, delete area.y1;
11450 return area;
11451 };
11452 d3.svg.chord = function() {
11453 var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
11454 function chord(d, i) {
11455 var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
11456 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";
11457 }
11458 function subgroup(self, f, d, i) {
11459 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π;
11460 return {
11461 r: r,
11462 a0: a0,
11463 a1: a1,
11464 p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
11465 p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
11466 };
11467 }
11468 function equals(a, b) {
11469 return a.a0 == b.a0 && a.a1 == b.a1;
11470 }
11471 function arc(r, p, a) {
11472 return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
11473 }
11474 function curve(r0, p0, r1, p1) {
11475 return "Q 0,0 " + p1;
11476 }
11477 chord.radius = function(v) {
11478 if (!arguments.length) return radius;
11479 radius = d3_functor(v);
11480 return chord;
11481 };
11482 chord.source = function(v) {
11483 if (!arguments.length) return source;
11484 source = d3_functor(v);
11485 return chord;
11486 };
11487 chord.target = function(v) {
11488 if (!arguments.length) return target;
11489 target = d3_functor(v);
11490 return chord;
11491 };
11492 chord.startAngle = function(v) {
11493 if (!arguments.length) return startAngle;
11494 startAngle = d3_functor(v);
11495 return chord;
11496 };
11497 chord.endAngle = function(v) {
11498 if (!arguments.length) return endAngle;
11499 endAngle = d3_functor(v);
11500 return chord;
11501 };
11502 return chord;
11503 };
11504 function d3_svg_chordRadius(d) {
11505 return d.radius;
11506 }
11507 d3.svg.diagonal = function() {
11508 var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
11509 function diagonal(d, i) {
11510 var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
11511 x: p0.x,
11512 y: m
11513 }, {
11514 x: p3.x,
11515 y: m
11516 }, p3 ];
11517 p = p.map(projection);
11518 return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
11519 }
11520 diagonal.source = function(x) {
11521 if (!arguments.length) return source;
11522 source = d3_functor(x);
11523 return diagonal;
11524 };
11525 diagonal.target = function(x) {
11526 if (!arguments.length) return target;
11527 target = d3_functor(x);
11528 return diagonal;
11529 };
11530 diagonal.projection = function(x) {
11531 if (!arguments.length) return projection;
11532 projection = x;
11533 return diagonal;
11534 };
11535 return diagonal;
11536 };
11537 function d3_svg_diagonalProjection(d) {
11538 return [ d.x, d.y ];
11539 }
11540 d3.svg.diagonal.radial = function() {
11541 var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
11542 diagonal.projection = function(x) {
11543 return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
11544 };
11545 return diagonal;
11546 };
11547 function d3_svg_diagonalRadialProjection(projection) {
11548 return function() {
11549 var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
11550 return [ r * Math.cos(a), r * Math.sin(a) ];
11551 };
11552 }
11553 d3.svg.symbol = function() {
11554 var type = d3_svg_symbolType, size = d3_svg_symbolSize;
11555 function symbol(d, i) {
11556 return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
11557 }
11558 symbol.type = function(x) {
11559 if (!arguments.length) return type;
11560 type = d3_functor(x);
11561 return symbol;
11562 };
11563 symbol.size = function(x) {
11564 if (!arguments.length) return size;
11565 size = d3_functor(x);
11566 return symbol;
11567 };
11568 return symbol;
11569 };
11570 function d3_svg_symbolSize() {
11571 return 64;
11572 }
11573 function d3_svg_symbolType() {
11574 return "circle";
11575 }
11576 function d3_svg_symbolCircle(size) {
11577 var r = Math.sqrt(size / π);
11578 return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
11579 }
11580 var d3_svg_symbols = d3.map({
11581 circle: d3_svg_symbolCircle,
11582 cross: function(size) {
11583 var r = Math.sqrt(size / 5) / 2;
11584 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";
11585 },
11586 diamond: function(size) {
11587 var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
11588 return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
11589 },
11590 square: function(size) {
11591 var r = Math.sqrt(size) / 2;
11592 return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
11593 },
11594 "triangle-down": function(size) {
11595 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
11596 return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
11597 },
11598 "triangle-up": function(size) {
11599 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
11600 return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
11601 }
11602 });
11603 d3.svg.symbolTypes = d3_svg_symbols.keys();
11604 var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
11605 d3_selectionPrototype.transition = function(name) {
11606 var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
11607 time: Date.now(),
11608 ease: d3_ease_cubicInOut,
11609 delay: 0,
11610 duration: 250
11611 };
11612 for (var j = -1, m = this.length; ++j < m; ) {
11613 subgroups.push(subgroup = []);
11614 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
11615 if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
11616 subgroup.push(node);
11617 }
11618 }
11619 return d3_transition(subgroups, ns, id);
11620 };
11621 d3_selectionPrototype.interrupt = function(name) {
11622 return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
11623 };
11624 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
11625 function d3_selection_interruptNS(ns) {
11626 return function() {
11627 var lock, activeId, active;
11628 if ((lock = this[ns]) && (active = lock[activeId = lock.active])) {
11629 active.timer.c = null;
11630 active.timer.t = NaN;
11631 if (--lock.count) delete lock[activeId]; else delete this[ns];
11632 lock.active += .5;
11633 active.event && active.event.interrupt.call(this, this.__data__, active.index);
11634 }
11635 };
11636 }
11637 function d3_transition(groups, ns, id) {
11638 d3_subclass(groups, d3_transitionPrototype);
11639 groups.namespace = ns;
11640 groups.id = id;
11641 return groups;
11642 }
11643 var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
11644 d3_transitionPrototype.call = d3_selectionPrototype.call;
11645 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
11646 d3_transitionPrototype.node = d3_selectionPrototype.node;
11647 d3_transitionPrototype.size = d3_selectionPrototype.size;
11648 d3.transition = function(selection, name) {
11649 return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection);
11650 };
11651 d3.transition.prototype = d3_transitionPrototype;
11652 d3_transitionPrototype.select = function(selector) {
11653 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
11654 selector = d3_selection_selector(selector);
11655 for (var j = -1, m = this.length; ++j < m; ) {
11656 subgroups.push(subgroup = []);
11657 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
11658 if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
11659 if ("__data__" in node) subnode.__data__ = node.__data__;
11660 d3_transitionNode(subnode, i, ns, id, node[ns][id]);
11661 subgroup.push(subnode);
11662 } else {
11663 subgroup.push(null);
11664 }
11665 }
11666 }
11667 return d3_transition(subgroups, ns, id);
11668 };
11669 d3_transitionPrototype.selectAll = function(selector) {
11670 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
11671 selector = d3_selection_selectorAll(selector);
11672 for (var j = -1, m = this.length; ++j < m; ) {
11673 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
11674 if (node = group[i]) {
11675 transition = node[ns][id];
11676 subnodes = selector.call(node, node.__data__, i, j);
11677 subgroups.push(subgroup = []);
11678 for (var k = -1, o = subnodes.length; ++k < o; ) {
11679 if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
11680 subgroup.push(subnode);
11681 }
11682 }
11683 }
11684 }
11685 return d3_transition(subgroups, ns, id);
11686 };
11687 d3_transitionPrototype.filter = function(filter) {
11688 var subgroups = [], subgroup, group, node;
11689 if (typeof filter !== "function") filter = d3_selection_filter(filter);
11690 for (var j = 0, m = this.length; j < m; j++) {
11691 subgroups.push(subgroup = []);
11692 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
11693 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
11694 subgroup.push(node);
11695 }
11696 }
11697 }
11698 return d3_transition(subgroups, this.namespace, this.id);
11699 };
11700 d3_transitionPrototype.tween = function(name, tween) {
11701 var id = this.id, ns = this.namespace;
11702 if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
11703 return d3_selection_each(this, tween == null ? function(node) {
11704 node[ns][id].tween.remove(name);
11705 } : function(node) {
11706 node[ns][id].tween.set(name, tween);
11707 });
11708 };
11709 function d3_transition_tween(groups, name, value, tween) {
11710 var id = groups.id, ns = groups.namespace;
11711 return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
11712 node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
11713 } : (value = tween(value), function(node) {
11714 node[ns][id].tween.set(name, value);
11715 }));
11716 }
11717 d3_transitionPrototype.attr = function(nameNS, value) {
11718 if (arguments.length < 2) {
11719 for (value in nameNS) this.attr(value, nameNS[value]);
11720 return this;
11721 }
11722 var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
11723 function attrNull() {
11724 this.removeAttribute(name);
11725 }
11726 function attrNullNS() {
11727 this.removeAttributeNS(name.space, name.local);
11728 }
11729 function attrTween(b) {
11730 return b == null ? attrNull : (b += "", function() {
11731 var a = this.getAttribute(name), i;
11732 return a !== b && (i = interpolate(a, b), function(t) {
11733 this.setAttribute(name, i(t));
11734 });
11735 });
11736 }
11737 function attrTweenNS(b) {
11738 return b == null ? attrNullNS : (b += "", function() {
11739 var a = this.getAttributeNS(name.space, name.local), i;
11740 return a !== b && (i = interpolate(a, b), function(t) {
11741 this.setAttributeNS(name.space, name.local, i(t));
11742 });
11743 });
11744 }
11745 return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
11746 };
11747 d3_transitionPrototype.attrTween = function(nameNS, tween) {
11748 var name = d3.ns.qualify(nameNS);
11749 function attrTween(d, i) {
11750 var f = tween.call(this, d, i, this.getAttribute(name));
11751 return f && function(t) {
11752 this.setAttribute(name, f(t));
11753 };
11754 }
11755 function attrTweenNS(d, i) {
11756 var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
11757 return f && function(t) {
11758 this.setAttributeNS(name.space, name.local, f(t));
11759 };
11760 }
11761 return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
11762 };
11763 d3_transitionPrototype.style = function(name, value, priority) {
11764 var n = arguments.length;
11765 if (n < 3) {
11766 if (typeof name !== "string") {
11767 if (n < 2) value = "";
11768 for (priority in name) this.style(priority, name[priority], value);
11769 return this;
11770 }
11771 priority = "";
11772 }
11773 function styleNull() {
11774 this.style.removeProperty(name);
11775 }
11776 function styleString(b) {
11777 return b == null ? styleNull : (b += "", function() {
11778 var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
11779 return a !== b && (i = d3_interpolate(a, b), function(t) {
11780 this.style.setProperty(name, i(t), priority);
11781 });
11782 });
11783 }
11784 return d3_transition_tween(this, "style." + name, value, styleString);
11785 };
11786 d3_transitionPrototype.styleTween = function(name, tween, priority) {
11787 if (arguments.length < 3) priority = "";
11788 function styleTween(d, i) {
11789 var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
11790 return f && function(t) {
11791 this.style.setProperty(name, f(t), priority);
11792 };
11793 }
11794 return this.tween("style." + name, styleTween);
11795 };
11796 d3_transitionPrototype.text = function(value) {
11797 return d3_transition_tween(this, "text", value, d3_transition_text);
11798 };
11799 function d3_transition_text(b) {
11800 if (b == null) b = "";
11801 return function() {
11802 this.textContent = b;
11803 };
11804 }
11805 d3_transitionPrototype.remove = function() {
11806 var ns = this.namespace;
11807 return this.each("end.transition", function() {
11808 var p;
11809 if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
11810 });
11811 };
11812 d3_transitionPrototype.ease = function(value) {
11813 var id = this.id, ns = this.namespace;
11814 if (arguments.length < 1) return this.node()[ns][id].ease;
11815 if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
11816 return d3_selection_each(this, function(node) {
11817 node[ns][id].ease = value;
11818 });
11819 };
11820 d3_transitionPrototype.delay = function(value) {
11821 var id = this.id, ns = this.namespace;
11822 if (arguments.length < 1) return this.node()[ns][id].delay;
11823 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
11824 node[ns][id].delay = +value.call(node, node.__data__, i, j);
11825 } : (value = +value, function(node) {
11826 node[ns][id].delay = value;
11827 }));
11828 };
11829 d3_transitionPrototype.duration = function(value) {
11830 var id = this.id, ns = this.namespace;
11831 if (arguments.length < 1) return this.node()[ns][id].duration;
11832 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
11833 node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
11834 } : (value = Math.max(1, value), function(node) {
11835 node[ns][id].duration = value;
11836 }));
11837 };
11838 d3_transitionPrototype.each = function(type, listener) {
11839 var id = this.id, ns = this.namespace;
11840 if (arguments.length < 2) {
11841 var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
11842 try {
11843 d3_transitionInheritId = id;
11844 d3_selection_each(this, function(node, i, j) {
11845 d3_transitionInherit = node[ns][id];
11846 type.call(node, node.__data__, i, j);
11847 });
11848 } finally {
11849 d3_transitionInherit = inherit;
11850 d3_transitionInheritId = inheritId;
11851 }
11852 } else {
11853 d3_selection_each(this, function(node) {
11854 var transition = node[ns][id];
11855 (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
11856 });
11857 }
11858 return this;
11859 };
11860 d3_transitionPrototype.transition = function() {
11861 var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
11862 for (var j = 0, m = this.length; j < m; j++) {
11863 subgroups.push(subgroup = []);
11864 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
11865 if (node = group[i]) {
11866 transition = node[ns][id0];
11867 d3_transitionNode(node, i, ns, id1, {
11868 time: transition.time,
11869 ease: transition.ease,
11870 delay: transition.delay + transition.duration,
11871 duration: transition.duration
11872 });
11873 }
11874 subgroup.push(node);
11875 }
11876 }
11877 return d3_transition(subgroups, ns, id1);
11878 };
11879 function d3_transitionNamespace(name) {
11880 return name == null ? "__transition__" : "__transition_" + name + "__";
11881 }
11882 function d3_transitionNode(node, i, ns, id, inherit) {
11883 var lock = node[ns] || (node[ns] = {
11884 active: 0,
11885 count: 0
11886 }), transition = lock[id], time, timer, duration, ease, tweens;
11887 function schedule(elapsed) {
11888 var delay = transition.delay;
11889 timer.t = delay + time;
11890 if (delay <= elapsed) return start(elapsed - delay);
11891 timer.c = start;
11892 }
11893 function start(elapsed) {
11894 var activeId = lock.active, active = lock[activeId];
11895 if (active) {
11896 active.timer.c = null;
11897 active.timer.t = NaN;
11898 --lock.count;
11899 delete lock[activeId];
11900 active.event && active.event.interrupt.call(node, node.__data__, active.index);
11901 }
11902 for (var cancelId in lock) {
11903 if (+cancelId < id) {
11904 var cancel = lock[cancelId];
11905 cancel.timer.c = null;
11906 cancel.timer.t = NaN;
11907 --lock.count;
11908 delete lock[cancelId];
11909 }
11910 }
11911 timer.c = tick;
11912 d3_timer(function() {
11913 if (timer.c && tick(elapsed || 1)) {
11914 timer.c = null;
11915 timer.t = NaN;
11916 }
11917 return 1;
11918 }, 0, time);
11919 lock.active = id;
11920 transition.event && transition.event.start.call(node, node.__data__, i);
11921 tweens = [];
11922 transition.tween.forEach(function(key, value) {
11923 if (value = value.call(node, node.__data__, i)) {
11924 tweens.push(value);
11925 }
11926 });
11927 ease = transition.ease;
11928 duration = transition.duration;
11929 }
11930 function tick(elapsed) {
11931 var t = elapsed / duration, e = ease(t), n = tweens.length;
11932 while (n > 0) {
11933 tweens[--n].call(node, e);
11934 }
11935 if (t >= 1) {
11936 transition.event && transition.event.end.call(node, node.__data__, i);
11937 if (--lock.count) delete lock[id]; else delete node[ns];
11938 return 1;
11939 }
11940 }
11941 if (!transition) {
11942 time = inherit.time;
11943 timer = d3_timer(schedule, 0, time);
11944 transition = lock[id] = {
11945 tween: new d3_Map(),
11946 time: time,
11947 timer: timer,
11948 delay: inherit.delay,
11949 duration: inherit.duration,
11950 ease: inherit.ease,
11951 index: i
11952 };
11953 inherit = null;
11954 ++lock.count;
11955 }
11956 }
11957 d3.svg.axis = function() {
11958 var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
11959 function axis(g) {
11960 g.each(function() {
11961 var g = d3.select(this);
11962 var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
11963 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;
11964 var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
11965 d3.transition(path));
11966 tickEnter.append("line");
11967 tickEnter.append("text");
11968 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;
11969 if (orient === "bottom" || orient === "top") {
11970 tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
11971 text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
11972 pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
11973 } else {
11974 tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
11975 text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
11976 pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
11977 }
11978 lineEnter.attr(y2, sign * innerTickSize);
11979 textEnter.attr(y1, sign * tickSpacing);
11980 lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
11981 textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
11982 if (scale1.rangeBand) {
11983 var x = scale1, dx = x.rangeBand() / 2;
11984 scale0 = scale1 = function(d) {
11985 return x(d) + dx;
11986 };
11987 } else if (scale0.rangeBand) {
11988 scale0 = scale1;
11989 } else {
11990 tickExit.call(tickTransform, scale1, scale0);
11991 }
11992 tickEnter.call(tickTransform, scale0, scale1);
11993 tickUpdate.call(tickTransform, scale1, scale1);
11994 });
11995 }
11996 axis.scale = function(x) {
11997 if (!arguments.length) return scale;
11998 scale = x;
11999 return axis;
12000 };
12001 axis.orient = function(x) {
12002 if (!arguments.length) return orient;
12003 orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
12004 return axis;
12005 };
12006 axis.ticks = function() {
12007 if (!arguments.length) return tickArguments_;
12008 tickArguments_ = d3_array(arguments);
12009 return axis;
12010 };
12011 axis.tickValues = function(x) {
12012 if (!arguments.length) return tickValues;
12013 tickValues = x;
12014 return axis;
12015 };
12016 axis.tickFormat = function(x) {
12017 if (!arguments.length) return tickFormat_;
12018 tickFormat_ = x;
12019 return axis;
12020 };
12021 axis.tickSize = function(x) {
12022 var n = arguments.length;
12023 if (!n) return innerTickSize;
12024 innerTickSize = +x;
12025 outerTickSize = +arguments[n - 1];
12026 return axis;
12027 };
12028 axis.innerTickSize = function(x) {
12029 if (!arguments.length) return innerTickSize;
12030 innerTickSize = +x;
12031 return axis;
12032 };
12033 axis.outerTickSize = function(x) {
12034 if (!arguments.length) return outerTickSize;
12035 outerTickSize = +x;
12036 return axis;
12037 };
12038 axis.tickPadding = function(x) {
12039 if (!arguments.length) return tickPadding;
12040 tickPadding = +x;
12041 return axis;
12042 };
12043 axis.tickSubdivide = function() {
12044 return arguments.length && axis;
12045 };
12046 return axis;
12047 };
12048 var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
12049 top: 1,
12050 right: 1,
12051 bottom: 1,
12052 left: 1
12053 };
12054 function d3_svg_axisX(selection, x0, x1) {
12055 selection.attr("transform", function(d) {
12056 var v0 = x0(d);
12057 return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
12058 });
12059 }
12060 function d3_svg_axisY(selection, y0, y1) {
12061 selection.attr("transform", function(d) {
12062 var v0 = y0(d);
12063 return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
12064 });
12065 }
12066 d3.svg.brush = function() {
12067 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];
12068 function brush(g) {
12069 g.each(function() {
12070 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);
12071 var background = g.selectAll(".background").data([ 0 ]);
12072 background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
12073 g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
12074 var resize = g.selectAll(".resize").data(resizes, d3_identity);
12075 resize.exit().remove();
12076 resize.enter().append("g").attr("class", function(d) {
12077 return "resize " + d;
12078 }).style("cursor", function(d) {
12079 return d3_svg_brushCursor[d];
12080 }).append("rect").attr("x", function(d) {
12081 return /[ew]$/.test(d) ? -3 : null;
12082 }).attr("y", function(d) {
12083 return /^[ns]/.test(d) ? -3 : null;
12084 }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
12085 resize.style("display", brush.empty() ? "none" : null);
12086 var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
12087 if (x) {
12088 range = d3_scaleRange(x);
12089 backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
12090 redrawX(gUpdate);
12091 }
12092 if (y) {
12093 range = d3_scaleRange(y);
12094 backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
12095 redrawY(gUpdate);
12096 }
12097 redraw(gUpdate);
12098 });
12099 }
12100 brush.event = function(g) {
12101 g.each(function() {
12102 var event_ = event.of(this, arguments), extent1 = {
12103 x: xExtent,
12104 y: yExtent,
12105 i: xExtentDomain,
12106 j: yExtentDomain
12107 }, extent0 = this.__chart__ || extent1;
12108 this.__chart__ = extent1;
12109 if (d3_transitionInheritId) {
12110 d3.select(this).transition().each("start.brush", function() {
12111 xExtentDomain = extent0.i;
12112 yExtentDomain = extent0.j;
12113 xExtent = extent0.x;
12114 yExtent = extent0.y;
12115 event_({
12116 type: "brushstart"
12117 });
12118 }).tween("brush:brush", function() {
12119 var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
12120 xExtentDomain = yExtentDomain = null;
12121 return function(t) {
12122 xExtent = extent1.x = xi(t);
12123 yExtent = extent1.y = yi(t);
12124 event_({
12125 type: "brush",
12126 mode: "resize"
12127 });
12128 };
12129 }).each("end.brush", function() {
12130 xExtentDomain = extent1.i;
12131 yExtentDomain = extent1.j;
12132 event_({
12133 type: "brush",
12134 mode: "resize"
12135 });
12136 event_({
12137 type: "brushend"
12138 });
12139 });
12140 } else {
12141 event_({
12142 type: "brushstart"
12143 });
12144 event_({
12145 type: "brush",
12146 mode: "resize"
12147 });
12148 event_({
12149 type: "brushend"
12150 });
12151 }
12152 });
12153 };
12154 function redraw(g) {
12155 g.selectAll(".resize").attr("transform", function(d) {
12156 return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
12157 });
12158 }
12159 function redrawX(g) {
12160 g.select(".extent").attr("x", xExtent[0]);
12161 g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
12162 }
12163 function redrawY(g) {
12164 g.select(".extent").attr("y", yExtent[0]);
12165 g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
12166 }
12167 function brushstart() {
12168 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;
12169 var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup);
12170 if (d3.event.changedTouches) {
12171 w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
12172 } else {
12173 w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
12174 }
12175 g.interrupt().selectAll("*").interrupt();
12176 if (dragging) {
12177 origin[0] = xExtent[0] - origin[0];
12178 origin[1] = yExtent[0] - origin[1];
12179 } else if (resizing) {
12180 var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
12181 offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
12182 origin[0] = xExtent[ex];
12183 origin[1] = yExtent[ey];
12184 } else if (d3.event.altKey) center = origin.slice();
12185 g.style("pointer-events", "none").selectAll(".resize").style("display", null);
12186 d3.select("body").style("cursor", eventTarget.style("cursor"));
12187 event_({
12188 type: "brushstart"
12189 });
12190 brushmove();
12191 function keydown() {
12192 if (d3.event.keyCode == 32) {
12193 if (!dragging) {
12194 center = null;
12195 origin[0] -= xExtent[1];
12196 origin[1] -= yExtent[1];
12197 dragging = 2;
12198 }
12199 d3_eventPreventDefault();
12200 }
12201 }
12202 function keyup() {
12203 if (d3.event.keyCode == 32 && dragging == 2) {
12204 origin[0] += xExtent[1];
12205 origin[1] += yExtent[1];
12206 dragging = 0;
12207 d3_eventPreventDefault();
12208 }
12209 }
12210 function brushmove() {
12211 var point = d3.mouse(target), moved = false;
12212 if (offset) {
12213 point[0] += offset[0];
12214 point[1] += offset[1];
12215 }
12216 if (!dragging) {
12217 if (d3.event.altKey) {
12218 if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
12219 origin[0] = xExtent[+(point[0] < center[0])];
12220 origin[1] = yExtent[+(point[1] < center[1])];
12221 } else center = null;
12222 }
12223 if (resizingX && move1(point, x, 0)) {
12224 redrawX(g);
12225 moved = true;
12226 }
12227 if (resizingY && move1(point, y, 1)) {
12228 redrawY(g);
12229 moved = true;
12230 }
12231 if (moved) {
12232 redraw(g);
12233 event_({
12234 type: "brush",
12235 mode: dragging ? "move" : "resize"
12236 });
12237 }
12238 }
12239 function move1(point, scale, i) {
12240 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;
12241 if (dragging) {
12242 r0 -= position;
12243 r1 -= size + position;
12244 }
12245 min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
12246 if (dragging) {
12247 max = (min += position) + size;
12248 } else {
12249 if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
12250 if (position < min) {
12251 max = min;
12252 min = position;
12253 } else {
12254 max = position;
12255 }
12256 }
12257 if (extent[0] != min || extent[1] != max) {
12258 if (i) yExtentDomain = null; else xExtentDomain = null;
12259 extent[0] = min;
12260 extent[1] = max;
12261 return true;
12262 }
12263 }
12264 function brushend() {
12265 brushmove();
12266 g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
12267 d3.select("body").style("cursor", null);
12268 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);
12269 dragRestore();
12270 event_({
12271 type: "brushend"
12272 });
12273 }
12274 }
12275 brush.x = function(z) {
12276 if (!arguments.length) return x;
12277 x = z;
12278 resizes = d3_svg_brushResizes[!x << 1 | !y];
12279 return brush;
12280 };
12281 brush.y = function(z) {
12282 if (!arguments.length) return y;
12283 y = z;
12284 resizes = d3_svg_brushResizes[!x << 1 | !y];
12285 return brush;
12286 };
12287 brush.clamp = function(z) {
12288 if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
12289 if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
12290 return brush;
12291 };
12292 brush.extent = function(z) {
12293 var x0, x1, y0, y1, t;
12294 if (!arguments.length) {
12295 if (x) {
12296 if (xExtentDomain) {
12297 x0 = xExtentDomain[0], x1 = xExtentDomain[1];
12298 } else {
12299 x0 = xExtent[0], x1 = xExtent[1];
12300 if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
12301 if (x1 < x0) t = x0, x0 = x1, x1 = t;
12302 }
12303 }
12304 if (y) {
12305 if (yExtentDomain) {
12306 y0 = yExtentDomain[0], y1 = yExtentDomain[1];
12307 } else {
12308 y0 = yExtent[0], y1 = yExtent[1];
12309 if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
12310 if (y1 < y0) t = y0, y0 = y1, y1 = t;
12311 }
12312 }
12313 return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
12314 }
12315 if (x) {
12316 x0 = z[0], x1 = z[1];
12317 if (y) x0 = x0[0], x1 = x1[0];
12318 xExtentDomain = [ x0, x1 ];
12319 if (x.invert) x0 = x(x0), x1 = x(x1);
12320 if (x1 < x0) t = x0, x0 = x1, x1 = t;
12321 if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
12322 }
12323 if (y) {
12324 y0 = z[0], y1 = z[1];
12325 if (x) y0 = y0[1], y1 = y1[1];
12326 yExtentDomain = [ y0, y1 ];
12327 if (y.invert) y0 = y(y0), y1 = y(y1);
12328 if (y1 < y0) t = y0, y0 = y1, y1 = t;
12329 if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
12330 }
12331 return brush;
12332 };
12333 brush.clear = function() {
12334 if (!brush.empty()) {
12335 xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
12336 xExtentDomain = yExtentDomain = null;
12337 }
12338 return brush;
12339 };
12340 brush.empty = function() {
12341 return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
12342 };
12343 return d3.rebind(brush, event, "on");
12344 };
12345 var d3_svg_brushCursor = {
12346 n: "ns-resize",
12347 e: "ew-resize",
12348 s: "ns-resize",
12349 w: "ew-resize",
12350 nw: "nwse-resize",
12351 ne: "nesw-resize",
12352 se: "nwse-resize",
12353 sw: "nesw-resize"
12354 };
12355 var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
12356 var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
12357 var d3_time_formatUtc = d3_time_format.utc;
12358 var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
12359 d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
12360 function d3_time_formatIsoNative(date) {
12361 return date.toISOString();
12362 }
12363 d3_time_formatIsoNative.parse = function(string) {
12364 var date = new Date(string);
12365 return isNaN(date) ? null : date;
12366 };
12367 d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
12368 d3_time.second = d3_time_interval(function(date) {
12369 return new d3_date(Math.floor(date / 1e3) * 1e3);
12370 }, function(date, offset) {
12371 date.setTime(date.getTime() + Math.floor(offset) * 1e3);
12372 }, function(date) {
12373 return date.getSeconds();
12374 });
12375 d3_time.seconds = d3_time.second.range;
12376 d3_time.seconds.utc = d3_time.second.utc.range;
12377 d3_time.minute = d3_time_interval(function(date) {
12378 return new d3_date(Math.floor(date / 6e4) * 6e4);
12379 }, function(date, offset) {
12380 date.setTime(date.getTime() + Math.floor(offset) * 6e4);
12381 }, function(date) {
12382 return date.getMinutes();
12383 });
12384 d3_time.minutes = d3_time.minute.range;
12385 d3_time.minutes.utc = d3_time.minute.utc.range;
12386 d3_time.hour = d3_time_interval(function(date) {
12387 var timezone = date.getTimezoneOffset() / 60;
12388 return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
12389 }, function(date, offset) {
12390 date.setTime(date.getTime() + Math.floor(offset) * 36e5);
12391 }, function(date) {
12392 return date.getHours();
12393 });
12394 d3_time.hours = d3_time.hour.range;
12395 d3_time.hours.utc = d3_time.hour.utc.range;
12396 d3_time.month = d3_time_interval(function(date) {
12397 date = d3_time.day(date);
12398 date.setDate(1);
12399 return date;
12400 }, function(date, offset) {
12401 date.setMonth(date.getMonth() + offset);
12402 }, function(date) {
12403 return date.getMonth();
12404 });
12405 d3_time.months = d3_time.month.range;
12406 d3_time.months.utc = d3_time.month.utc.range;
12407 function d3_time_scale(linear, methods, format) {
12408 function scale(x) {
12409 return linear(x);
12410 }
12411 scale.invert = function(x) {
12412 return d3_time_scaleDate(linear.invert(x));
12413 };
12414 scale.domain = function(x) {
12415 if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
12416 linear.domain(x);
12417 return scale;
12418 };
12419 function tickMethod(extent, count) {
12420 var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
12421 return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
12422 return d / 31536e6;
12423 }), 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];
12424 }
12425 scale.nice = function(interval, skip) {
12426 var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
12427 if (method) interval = method[0], skip = method[1];
12428 function skipped(date) {
12429 return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
12430 }
12431 return scale.domain(d3_scale_nice(domain, skip > 1 ? {
12432 floor: function(date) {
12433 while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
12434 return date;
12435 },
12436 ceil: function(date) {
12437 while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
12438 return date;
12439 }
12440 } : interval));
12441 };
12442 scale.ticks = function(interval, skip) {
12443 var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
12444 range: interval
12445 }, skip ];
12446 if (method) interval = method[0], skip = method[1];
12447 return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
12448 };
12449 scale.tickFormat = function() {
12450 return format;
12451 };
12452 scale.copy = function() {
12453 return d3_time_scale(linear.copy(), methods, format);
12454 };
12455 return d3_scale_linearRebind(scale, linear);
12456 }
12457 function d3_time_scaleDate(t) {
12458 return new Date(t);
12459 }
12460 var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
12461 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 ] ];
12462 var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
12463 return d.getMilliseconds();
12464 } ], [ ":%S", function(d) {
12465 return d.getSeconds();
12466 } ], [ "%I:%M", function(d) {
12467 return d.getMinutes();
12468 } ], [ "%I %p", function(d) {
12469 return d.getHours();
12470 } ], [ "%a %d", function(d) {
12471 return d.getDay() && d.getDate() != 1;
12472 } ], [ "%b %d", function(d) {
12473 return d.getDate() != 1;
12474 } ], [ "%B", function(d) {
12475 return d.getMonth();
12476 } ], [ "%Y", d3_true ] ]);
12477 var d3_time_scaleMilliseconds = {
12478 range: function(start, stop, step) {
12479 return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
12480 },
12481 floor: d3_identity,
12482 ceil: d3_identity
12483 };
12484 d3_time_scaleLocalMethods.year = d3_time.year;
12485 d3_time.scale = function() {
12486 return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
12487 };
12488 var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
12489 return [ m[0].utc, m[1] ];
12490 });
12491 var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
12492 return d.getUTCMilliseconds();
12493 } ], [ ":%S", function(d) {
12494 return d.getUTCSeconds();
12495 } ], [ "%I:%M", function(d) {
12496 return d.getUTCMinutes();
12497 } ], [ "%I %p", function(d) {
12498 return d.getUTCHours();
12499 } ], [ "%a %d", function(d) {
12500 return d.getUTCDay() && d.getUTCDate() != 1;
12501 } ], [ "%b %d", function(d) {
12502 return d.getUTCDate() != 1;
12503 } ], [ "%B", function(d) {
12504 return d.getUTCMonth();
12505 } ], [ "%Y", d3_true ] ]);
12506 d3_time_scaleUtcMethods.year = d3_time.year.utc;
12507 d3_time.scale.utc = function() {
12508 return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
12509 };
12510 d3.text = d3_xhrType(function(request) {
12511 return request.responseText;
12512 });
12513 d3.json = function(url, callback) {
12514 return d3_xhr(url, "application/json", d3_json, callback);
12515 };
12516 function d3_json(request) {
12517 return JSON.parse(request.responseText);
12518 }
12519 d3.html = function(url, callback) {
12520 return d3_xhr(url, "text/html", d3_html, callback);
12521 };
12522 function d3_html(request) {
12523 var range = d3_document.createRange();
12524 range.selectNode(d3_document.body);
12525 return range.createContextualFragment(request.responseText);
12526 }
12527 d3.xml = d3_xhrType(function(request) {
12528 return request.responseXML;
12529 });
12530 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;
12531}();
12532},{}],14:[function(_dereq_,module,exports){
12533(function (process,global){
12534/*!
12535 * @overview es6-promise - a tiny implementation of Promises/A+.
12536 * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
12537 * @license Licensed under MIT license
12538 * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
12539 * @version v4.2.8+1e68dce6
12540 */
12541
12542(function (global, factory) {
12543 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12544 typeof define === 'function' && define.amd ? define(factory) :
12545 (global.ES6Promise = factory());
12546}(this, (function () { 'use strict';
12547
12548function objectOrFunction(x) {
12549 var type = typeof x;
12550 return x !== null && (type === 'object' || type === 'function');
12551}
12552
12553function isFunction(x) {
12554 return typeof x === 'function';
12555}
12556
12557
12558
12559var _isArray = void 0;
12560if (Array.isArray) {
12561 _isArray = Array.isArray;
12562} else {
12563 _isArray = function (x) {
12564 return Object.prototype.toString.call(x) === '[object Array]';
12565 };
12566}
12567
12568var isArray = _isArray;
12569
12570var len = 0;
12571var vertxNext = void 0;
12572var customSchedulerFn = void 0;
12573
12574var asap = function asap(callback, arg) {
12575 queue[len] = callback;
12576 queue[len + 1] = arg;
12577 len += 2;
12578 if (len === 2) {
12579 // If len is 2, that means that we need to schedule an async flush.
12580 // If additional callbacks are queued before the queue is flushed, they
12581 // will be processed by this flush that we are scheduling.
12582 if (customSchedulerFn) {
12583 customSchedulerFn(flush);
12584 } else {
12585 scheduleFlush();
12586 }
12587 }
12588};
12589
12590function setScheduler(scheduleFn) {
12591 customSchedulerFn = scheduleFn;
12592}
12593
12594function setAsap(asapFn) {
12595 asap = asapFn;
12596}
12597
12598var browserWindow = typeof window !== 'undefined' ? window : undefined;
12599var browserGlobal = browserWindow || {};
12600var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
12601var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
12602
12603// test for web worker but not in IE10
12604var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
12605
12606// node
12607function useNextTick() {
12608 // node version 0.10.x displays a deprecation warning when nextTick is used recursively
12609 // see https://github.com/cujojs/when/issues/410 for details
12610 return function () {
12611 return process.nextTick(flush);
12612 };
12613}
12614
12615// vertx
12616function useVertxTimer() {
12617 if (typeof vertxNext !== 'undefined') {
12618 return function () {
12619 vertxNext(flush);
12620 };
12621 }
12622
12623 return useSetTimeout();
12624}
12625
12626function useMutationObserver() {
12627 var iterations = 0;
12628 var observer = new BrowserMutationObserver(flush);
12629 var node = document.createTextNode('');
12630 observer.observe(node, { characterData: true });
12631
12632 return function () {
12633 node.data = iterations = ++iterations % 2;
12634 };
12635}
12636
12637// web worker
12638function useMessageChannel() {
12639 var channel = new MessageChannel();
12640 channel.port1.onmessage = flush;
12641 return function () {
12642 return channel.port2.postMessage(0);
12643 };
12644}
12645
12646function useSetTimeout() {
12647 // Store setTimeout reference so es6-promise will be unaffected by
12648 // other code modifying setTimeout (like sinon.useFakeTimers())
12649 var globalSetTimeout = setTimeout;
12650 return function () {
12651 return globalSetTimeout(flush, 1);
12652 };
12653}
12654
12655var queue = new Array(1000);
12656function flush() {
12657 for (var i = 0; i < len; i += 2) {
12658 var callback = queue[i];
12659 var arg = queue[i + 1];
12660
12661 callback(arg);
12662
12663 queue[i] = undefined;
12664 queue[i + 1] = undefined;
12665 }
12666
12667 len = 0;
12668}
12669
12670function attemptVertx() {
12671 try {
12672 var vertx = Function('return this')().require('vertx');
12673 vertxNext = vertx.runOnLoop || vertx.runOnContext;
12674 return useVertxTimer();
12675 } catch (e) {
12676 return useSetTimeout();
12677 }
12678}
12679
12680var scheduleFlush = void 0;
12681// Decide what async method to use to triggering processing of queued callbacks:
12682if (isNode) {
12683 scheduleFlush = useNextTick();
12684} else if (BrowserMutationObserver) {
12685 scheduleFlush = useMutationObserver();
12686} else if (isWorker) {
12687 scheduleFlush = useMessageChannel();
12688} else if (browserWindow === undefined && typeof _dereq_ === 'function') {
12689 scheduleFlush = attemptVertx();
12690} else {
12691 scheduleFlush = useSetTimeout();
12692}
12693
12694function then(onFulfillment, onRejection) {
12695 var parent = this;
12696
12697 var child = new this.constructor(noop);
12698
12699 if (child[PROMISE_ID] === undefined) {
12700 makePromise(child);
12701 }
12702
12703 var _state = parent._state;
12704
12705
12706 if (_state) {
12707 var callback = arguments[_state - 1];
12708 asap(function () {
12709 return invokeCallback(_state, child, callback, parent._result);
12710 });
12711 } else {
12712 subscribe(parent, child, onFulfillment, onRejection);
12713 }
12714
12715 return child;
12716}
12717
12718/**
12719 `Promise.resolve` returns a promise that will become resolved with the
12720 passed `value`. It is shorthand for the following:
12721
12722 ```javascript
12723 let promise = new Promise(function(resolve, reject){
12724 resolve(1);
12725 });
12726
12727 promise.then(function(value){
12728 // value === 1
12729 });
12730 ```
12731
12732 Instead of writing the above, your code now simply becomes the following:
12733
12734 ```javascript
12735 let promise = Promise.resolve(1);
12736
12737 promise.then(function(value){
12738 // value === 1
12739 });
12740 ```
12741
12742 @method resolve
12743 @static
12744 @param {Any} value value that the returned promise will be resolved with
12745 Useful for tooling.
12746 @return {Promise} a promise that will become fulfilled with the given
12747 `value`
12748*/
12749function resolve$1(object) {
12750 /*jshint validthis:true */
12751 var Constructor = this;
12752
12753 if (object && typeof object === 'object' && object.constructor === Constructor) {
12754 return object;
12755 }
12756
12757 var promise = new Constructor(noop);
12758 resolve(promise, object);
12759 return promise;
12760}
12761
12762var PROMISE_ID = Math.random().toString(36).substring(2);
12763
12764function noop() {}
12765
12766var PENDING = void 0;
12767var FULFILLED = 1;
12768var REJECTED = 2;
12769
12770function selfFulfillment() {
12771 return new TypeError("You cannot resolve a promise with itself");
12772}
12773
12774function cannotReturnOwn() {
12775 return new TypeError('A promises callback cannot return that same promise.');
12776}
12777
12778function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
12779 try {
12780 then$$1.call(value, fulfillmentHandler, rejectionHandler);
12781 } catch (e) {
12782 return e;
12783 }
12784}
12785
12786function handleForeignThenable(promise, thenable, then$$1) {
12787 asap(function (promise) {
12788 var sealed = false;
12789 var error = tryThen(then$$1, thenable, function (value) {
12790 if (sealed) {
12791 return;
12792 }
12793 sealed = true;
12794 if (thenable !== value) {
12795 resolve(promise, value);
12796 } else {
12797 fulfill(promise, value);
12798 }
12799 }, function (reason) {
12800 if (sealed) {
12801 return;
12802 }
12803 sealed = true;
12804
12805 reject(promise, reason);
12806 }, 'Settle: ' + (promise._label || ' unknown promise'));
12807
12808 if (!sealed && error) {
12809 sealed = true;
12810 reject(promise, error);
12811 }
12812 }, promise);
12813}
12814
12815function handleOwnThenable(promise, thenable) {
12816 if (thenable._state === FULFILLED) {
12817 fulfill(promise, thenable._result);
12818 } else if (thenable._state === REJECTED) {
12819 reject(promise, thenable._result);
12820 } else {
12821 subscribe(thenable, undefined, function (value) {
12822 return resolve(promise, value);
12823 }, function (reason) {
12824 return reject(promise, reason);
12825 });
12826 }
12827}
12828
12829function handleMaybeThenable(promise, maybeThenable, then$$1) {
12830 if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
12831 handleOwnThenable(promise, maybeThenable);
12832 } else {
12833 if (then$$1 === undefined) {
12834 fulfill(promise, maybeThenable);
12835 } else if (isFunction(then$$1)) {
12836 handleForeignThenable(promise, maybeThenable, then$$1);
12837 } else {
12838 fulfill(promise, maybeThenable);
12839 }
12840 }
12841}
12842
12843function resolve(promise, value) {
12844 if (promise === value) {
12845 reject(promise, selfFulfillment());
12846 } else if (objectOrFunction(value)) {
12847 var then$$1 = void 0;
12848 try {
12849 then$$1 = value.then;
12850 } catch (error) {
12851 reject(promise, error);
12852 return;
12853 }
12854 handleMaybeThenable(promise, value, then$$1);
12855 } else {
12856 fulfill(promise, value);
12857 }
12858}
12859
12860function publishRejection(promise) {
12861 if (promise._onerror) {
12862 promise._onerror(promise._result);
12863 }
12864
12865 publish(promise);
12866}
12867
12868function fulfill(promise, value) {
12869 if (promise._state !== PENDING) {
12870 return;
12871 }
12872
12873 promise._result = value;
12874 promise._state = FULFILLED;
12875
12876 if (promise._subscribers.length !== 0) {
12877 asap(publish, promise);
12878 }
12879}
12880
12881function reject(promise, reason) {
12882 if (promise._state !== PENDING) {
12883 return;
12884 }
12885 promise._state = REJECTED;
12886 promise._result = reason;
12887
12888 asap(publishRejection, promise);
12889}
12890
12891function subscribe(parent, child, onFulfillment, onRejection) {
12892 var _subscribers = parent._subscribers;
12893 var length = _subscribers.length;
12894
12895
12896 parent._onerror = null;
12897
12898 _subscribers[length] = child;
12899 _subscribers[length + FULFILLED] = onFulfillment;
12900 _subscribers[length + REJECTED] = onRejection;
12901
12902 if (length === 0 && parent._state) {
12903 asap(publish, parent);
12904 }
12905}
12906
12907function publish(promise) {
12908 var subscribers = promise._subscribers;
12909 var settled = promise._state;
12910
12911 if (subscribers.length === 0) {
12912 return;
12913 }
12914
12915 var child = void 0,
12916 callback = void 0,
12917 detail = promise._result;
12918
12919 for (var i = 0; i < subscribers.length; i += 3) {
12920 child = subscribers[i];
12921 callback = subscribers[i + settled];
12922
12923 if (child) {
12924 invokeCallback(settled, child, callback, detail);
12925 } else {
12926 callback(detail);
12927 }
12928 }
12929
12930 promise._subscribers.length = 0;
12931}
12932
12933function invokeCallback(settled, promise, callback, detail) {
12934 var hasCallback = isFunction(callback),
12935 value = void 0,
12936 error = void 0,
12937 succeeded = true;
12938
12939 if (hasCallback) {
12940 try {
12941 value = callback(detail);
12942 } catch (e) {
12943 succeeded = false;
12944 error = e;
12945 }
12946
12947 if (promise === value) {
12948 reject(promise, cannotReturnOwn());
12949 return;
12950 }
12951 } else {
12952 value = detail;
12953 }
12954
12955 if (promise._state !== PENDING) {
12956 // noop
12957 } else if (hasCallback && succeeded) {
12958 resolve(promise, value);
12959 } else if (succeeded === false) {
12960 reject(promise, error);
12961 } else if (settled === FULFILLED) {
12962 fulfill(promise, value);
12963 } else if (settled === REJECTED) {
12964 reject(promise, value);
12965 }
12966}
12967
12968function initializePromise(promise, resolver) {
12969 try {
12970 resolver(function resolvePromise(value) {
12971 resolve(promise, value);
12972 }, function rejectPromise(reason) {
12973 reject(promise, reason);
12974 });
12975 } catch (e) {
12976 reject(promise, e);
12977 }
12978}
12979
12980var id = 0;
12981function nextId() {
12982 return id++;
12983}
12984
12985function makePromise(promise) {
12986 promise[PROMISE_ID] = id++;
12987 promise._state = undefined;
12988 promise._result = undefined;
12989 promise._subscribers = [];
12990}
12991
12992function validationError() {
12993 return new Error('Array Methods must be provided an Array');
12994}
12995
12996var Enumerator = function () {
12997 function Enumerator(Constructor, input) {
12998 this._instanceConstructor = Constructor;
12999 this.promise = new Constructor(noop);
13000
13001 if (!this.promise[PROMISE_ID]) {
13002 makePromise(this.promise);
13003 }
13004
13005 if (isArray(input)) {
13006 this.length = input.length;
13007 this._remaining = input.length;
13008
13009 this._result = new Array(this.length);
13010
13011 if (this.length === 0) {
13012 fulfill(this.promise, this._result);
13013 } else {
13014 this.length = this.length || 0;
13015 this._enumerate(input);
13016 if (this._remaining === 0) {
13017 fulfill(this.promise, this._result);
13018 }
13019 }
13020 } else {
13021 reject(this.promise, validationError());
13022 }
13023 }
13024
13025 Enumerator.prototype._enumerate = function _enumerate(input) {
13026 for (var i = 0; this._state === PENDING && i < input.length; i++) {
13027 this._eachEntry(input[i], i);
13028 }
13029 };
13030
13031 Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {
13032 var c = this._instanceConstructor;
13033 var resolve$$1 = c.resolve;
13034
13035
13036 if (resolve$$1 === resolve$1) {
13037 var _then = void 0;
13038 var error = void 0;
13039 var didError = false;
13040 try {
13041 _then = entry.then;
13042 } catch (e) {
13043 didError = true;
13044 error = e;
13045 }
13046
13047 if (_then === then && entry._state !== PENDING) {
13048 this._settledAt(entry._state, i, entry._result);
13049 } else if (typeof _then !== 'function') {
13050 this._remaining--;
13051 this._result[i] = entry;
13052 } else if (c === Promise$1) {
13053 var promise = new c(noop);
13054 if (didError) {
13055 reject(promise, error);
13056 } else {
13057 handleMaybeThenable(promise, entry, _then);
13058 }
13059 this._willSettleAt(promise, i);
13060 } else {
13061 this._willSettleAt(new c(function (resolve$$1) {
13062 return resolve$$1(entry);
13063 }), i);
13064 }
13065 } else {
13066 this._willSettleAt(resolve$$1(entry), i);
13067 }
13068 };
13069
13070 Enumerator.prototype._settledAt = function _settledAt(state, i, value) {
13071 var promise = this.promise;
13072
13073
13074 if (promise._state === PENDING) {
13075 this._remaining--;
13076
13077 if (state === REJECTED) {
13078 reject(promise, value);
13079 } else {
13080 this._result[i] = value;
13081 }
13082 }
13083
13084 if (this._remaining === 0) {
13085 fulfill(promise, this._result);
13086 }
13087 };
13088
13089 Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {
13090 var enumerator = this;
13091
13092 subscribe(promise, undefined, function (value) {
13093 return enumerator._settledAt(FULFILLED, i, value);
13094 }, function (reason) {
13095 return enumerator._settledAt(REJECTED, i, reason);
13096 });
13097 };
13098
13099 return Enumerator;
13100}();
13101
13102/**
13103 `Promise.all` accepts an array of promises, and returns a new promise which
13104 is fulfilled with an array of fulfillment values for the passed promises, or
13105 rejected with the reason of the first passed promise to be rejected. It casts all
13106 elements of the passed iterable to promises as it runs this algorithm.
13107
13108 Example:
13109
13110 ```javascript
13111 let promise1 = resolve(1);
13112 let promise2 = resolve(2);
13113 let promise3 = resolve(3);
13114 let promises = [ promise1, promise2, promise3 ];
13115
13116 Promise.all(promises).then(function(array){
13117 // The array here would be [ 1, 2, 3 ];
13118 });
13119 ```
13120
13121 If any of the `promises` given to `all` are rejected, the first promise
13122 that is rejected will be given as an argument to the returned promises's
13123 rejection handler. For example:
13124
13125 Example:
13126
13127 ```javascript
13128 let promise1 = resolve(1);
13129 let promise2 = reject(new Error("2"));
13130 let promise3 = reject(new Error("3"));
13131 let promises = [ promise1, promise2, promise3 ];
13132
13133 Promise.all(promises).then(function(array){
13134 // Code here never runs because there are rejected promises!
13135 }, function(error) {
13136 // error.message === "2"
13137 });
13138 ```
13139
13140 @method all
13141 @static
13142 @param {Array} entries array of promises
13143 @param {String} label optional string for labeling the promise.
13144 Useful for tooling.
13145 @return {Promise} promise that is fulfilled when all `promises` have been
13146 fulfilled, or rejected if any of them become rejected.
13147 @static
13148*/
13149function all(entries) {
13150 return new Enumerator(this, entries).promise;
13151}
13152
13153/**
13154 `Promise.race` returns a new promise which is settled in the same way as the
13155 first passed promise to settle.
13156
13157 Example:
13158
13159 ```javascript
13160 let promise1 = new Promise(function(resolve, reject){
13161 setTimeout(function(){
13162 resolve('promise 1');
13163 }, 200);
13164 });
13165
13166 let promise2 = new Promise(function(resolve, reject){
13167 setTimeout(function(){
13168 resolve('promise 2');
13169 }, 100);
13170 });
13171
13172 Promise.race([promise1, promise2]).then(function(result){
13173 // result === 'promise 2' because it was resolved before promise1
13174 // was resolved.
13175 });
13176 ```
13177
13178 `Promise.race` is deterministic in that only the state of the first
13179 settled promise matters. For example, even if other promises given to the
13180 `promises` array argument are resolved, but the first settled promise has
13181 become rejected before the other promises became fulfilled, the returned
13182 promise will become rejected:
13183
13184 ```javascript
13185 let promise1 = new Promise(function(resolve, reject){
13186 setTimeout(function(){
13187 resolve('promise 1');
13188 }, 200);
13189 });
13190
13191 let promise2 = new Promise(function(resolve, reject){
13192 setTimeout(function(){
13193 reject(new Error('promise 2'));
13194 }, 100);
13195 });
13196
13197 Promise.race([promise1, promise2]).then(function(result){
13198 // Code here never runs
13199 }, function(reason){
13200 // reason.message === 'promise 2' because promise 2 became rejected before
13201 // promise 1 became fulfilled
13202 });
13203 ```
13204
13205 An example real-world use case is implementing timeouts:
13206
13207 ```javascript
13208 Promise.race([ajax('foo.json'), timeout(5000)])
13209 ```
13210
13211 @method race
13212 @static
13213 @param {Array} promises array of promises to observe
13214 Useful for tooling.
13215 @return {Promise} a promise which settles in the same way as the first passed
13216 promise to settle.
13217*/
13218function race(entries) {
13219 /*jshint validthis:true */
13220 var Constructor = this;
13221
13222 if (!isArray(entries)) {
13223 return new Constructor(function (_, reject) {
13224 return reject(new TypeError('You must pass an array to race.'));
13225 });
13226 } else {
13227 return new Constructor(function (resolve, reject) {
13228 var length = entries.length;
13229 for (var i = 0; i < length; i++) {
13230 Constructor.resolve(entries[i]).then(resolve, reject);
13231 }
13232 });
13233 }
13234}
13235
13236/**
13237 `Promise.reject` returns a promise rejected with the passed `reason`.
13238 It is shorthand for the following:
13239
13240 ```javascript
13241 let promise = new Promise(function(resolve, reject){
13242 reject(new Error('WHOOPS'));
13243 });
13244
13245 promise.then(function(value){
13246 // Code here doesn't run because the promise is rejected!
13247 }, function(reason){
13248 // reason.message === 'WHOOPS'
13249 });
13250 ```
13251
13252 Instead of writing the above, your code now simply becomes the following:
13253
13254 ```javascript
13255 let promise = Promise.reject(new Error('WHOOPS'));
13256
13257 promise.then(function(value){
13258 // Code here doesn't run because the promise is rejected!
13259 }, function(reason){
13260 // reason.message === 'WHOOPS'
13261 });
13262 ```
13263
13264 @method reject
13265 @static
13266 @param {Any} reason value that the returned promise will be rejected with.
13267 Useful for tooling.
13268 @return {Promise} a promise rejected with the given `reason`.
13269*/
13270function reject$1(reason) {
13271 /*jshint validthis:true */
13272 var Constructor = this;
13273 var promise = new Constructor(noop);
13274 reject(promise, reason);
13275 return promise;
13276}
13277
13278function needsResolver() {
13279 throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
13280}
13281
13282function needsNew() {
13283 throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
13284}
13285
13286/**
13287 Promise objects represent the eventual result of an asynchronous operation. The
13288 primary way of interacting with a promise is through its `then` method, which
13289 registers callbacks to receive either a promise's eventual value or the reason
13290 why the promise cannot be fulfilled.
13291
13292 Terminology
13293 -----------
13294
13295 - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
13296 - `thenable` is an object or function that defines a `then` method.
13297 - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
13298 - `exception` is a value that is thrown using the throw statement.
13299 - `reason` is a value that indicates why a promise was rejected.
13300 - `settled` the final resting state of a promise, fulfilled or rejected.
13301
13302 A promise can be in one of three states: pending, fulfilled, or rejected.
13303
13304 Promises that are fulfilled have a fulfillment value and are in the fulfilled
13305 state. Promises that are rejected have a rejection reason and are in the
13306 rejected state. A fulfillment value is never a thenable.
13307
13308 Promises can also be said to *resolve* a value. If this value is also a
13309 promise, then the original promise's settled state will match the value's
13310 settled state. So a promise that *resolves* a promise that rejects will
13311 itself reject, and a promise that *resolves* a promise that fulfills will
13312 itself fulfill.
13313
13314
13315 Basic Usage:
13316 ------------
13317
13318 ```js
13319 let promise = new Promise(function(resolve, reject) {
13320 // on success
13321 resolve(value);
13322
13323 // on failure
13324 reject(reason);
13325 });
13326
13327 promise.then(function(value) {
13328 // on fulfillment
13329 }, function(reason) {
13330 // on rejection
13331 });
13332 ```
13333
13334 Advanced Usage:
13335 ---------------
13336
13337 Promises shine when abstracting away asynchronous interactions such as
13338 `XMLHttpRequest`s.
13339
13340 ```js
13341 function getJSON(url) {
13342 return new Promise(function(resolve, reject){
13343 let xhr = new XMLHttpRequest();
13344
13345 xhr.open('GET', url);
13346 xhr.onreadystatechange = handler;
13347 xhr.responseType = 'json';
13348 xhr.setRequestHeader('Accept', 'application/json');
13349 xhr.send();
13350
13351 function handler() {
13352 if (this.readyState === this.DONE) {
13353 if (this.status === 200) {
13354 resolve(this.response);
13355 } else {
13356 reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
13357 }
13358 }
13359 };
13360 });
13361 }
13362
13363 getJSON('/posts.json').then(function(json) {
13364 // on fulfillment
13365 }, function(reason) {
13366 // on rejection
13367 });
13368 ```
13369
13370 Unlike callbacks, promises are great composable primitives.
13371
13372 ```js
13373 Promise.all([
13374 getJSON('/posts'),
13375 getJSON('/comments')
13376 ]).then(function(values){
13377 values[0] // => postsJSON
13378 values[1] // => commentsJSON
13379
13380 return values;
13381 });
13382 ```
13383
13384 @class Promise
13385 @param {Function} resolver
13386 Useful for tooling.
13387 @constructor
13388*/
13389
13390var Promise$1 = function () {
13391 function Promise(resolver) {
13392 this[PROMISE_ID] = nextId();
13393 this._result = this._state = undefined;
13394 this._subscribers = [];
13395
13396 if (noop !== resolver) {
13397 typeof resolver !== 'function' && needsResolver();
13398 this instanceof Promise ? initializePromise(this, resolver) : needsNew();
13399 }
13400 }
13401
13402 /**
13403 The primary way of interacting with a promise is through its `then` method,
13404 which registers callbacks to receive either a promise's eventual value or the
13405 reason why the promise cannot be fulfilled.
13406 ```js
13407 findUser().then(function(user){
13408 // user is available
13409 }, function(reason){
13410 // user is unavailable, and you are given the reason why
13411 });
13412 ```
13413 Chaining
13414 --------
13415 The return value of `then` is itself a promise. This second, 'downstream'
13416 promise is resolved with the return value of the first promise's fulfillment
13417 or rejection handler, or rejected if the handler throws an exception.
13418 ```js
13419 findUser().then(function (user) {
13420 return user.name;
13421 }, function (reason) {
13422 return 'default name';
13423 }).then(function (userName) {
13424 // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
13425 // will be `'default name'`
13426 });
13427 findUser().then(function (user) {
13428 throw new Error('Found user, but still unhappy');
13429 }, function (reason) {
13430 throw new Error('`findUser` rejected and we're unhappy');
13431 }).then(function (value) {
13432 // never reached
13433 }, function (reason) {
13434 // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
13435 // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
13436 });
13437 ```
13438 If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
13439 ```js
13440 findUser().then(function (user) {
13441 throw new PedagogicalException('Upstream error');
13442 }).then(function (value) {
13443 // never reached
13444 }).then(function (value) {
13445 // never reached
13446 }, function (reason) {
13447 // The `PedgagocialException` is propagated all the way down to here
13448 });
13449 ```
13450 Assimilation
13451 ------------
13452 Sometimes the value you want to propagate to a downstream promise can only be
13453 retrieved asynchronously. This can be achieved by returning a promise in the
13454 fulfillment or rejection handler. The downstream promise will then be pending
13455 until the returned promise is settled. This is called *assimilation*.
13456 ```js
13457 findUser().then(function (user) {
13458 return findCommentsByAuthor(user);
13459 }).then(function (comments) {
13460 // The user's comments are now available
13461 });
13462 ```
13463 If the assimliated promise rejects, then the downstream promise will also reject.
13464 ```js
13465 findUser().then(function (user) {
13466 return findCommentsByAuthor(user);
13467 }).then(function (comments) {
13468 // If `findCommentsByAuthor` fulfills, we'll have the value here
13469 }, function (reason) {
13470 // If `findCommentsByAuthor` rejects, we'll have the reason here
13471 });
13472 ```
13473 Simple Example
13474 --------------
13475 Synchronous Example
13476 ```javascript
13477 let result;
13478 try {
13479 result = findResult();
13480 // success
13481 } catch(reason) {
13482 // failure
13483 }
13484 ```
13485 Errback Example
13486 ```js
13487 findResult(function(result, err){
13488 if (err) {
13489 // failure
13490 } else {
13491 // success
13492 }
13493 });
13494 ```
13495 Promise Example;
13496 ```javascript
13497 findResult().then(function(result){
13498 // success
13499 }, function(reason){
13500 // failure
13501 });
13502 ```
13503 Advanced Example
13504 --------------
13505 Synchronous Example
13506 ```javascript
13507 let author, books;
13508 try {
13509 author = findAuthor();
13510 books = findBooksByAuthor(author);
13511 // success
13512 } catch(reason) {
13513 // failure
13514 }
13515 ```
13516 Errback Example
13517 ```js
13518 function foundBooks(books) {
13519 }
13520 function failure(reason) {
13521 }
13522 findAuthor(function(author, err){
13523 if (err) {
13524 failure(err);
13525 // failure
13526 } else {
13527 try {
13528 findBoooksByAuthor(author, function(books, err) {
13529 if (err) {
13530 failure(err);
13531 } else {
13532 try {
13533 foundBooks(books);
13534 } catch(reason) {
13535 failure(reason);
13536 }
13537 }
13538 });
13539 } catch(error) {
13540 failure(err);
13541 }
13542 // success
13543 }
13544 });
13545 ```
13546 Promise Example;
13547 ```javascript
13548 findAuthor().
13549 then(findBooksByAuthor).
13550 then(function(books){
13551 // found books
13552 }).catch(function(reason){
13553 // something went wrong
13554 });
13555 ```
13556 @method then
13557 @param {Function} onFulfilled
13558 @param {Function} onRejected
13559 Useful for tooling.
13560 @return {Promise}
13561 */
13562
13563 /**
13564 `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
13565 as the catch block of a try/catch statement.
13566 ```js
13567 function findAuthor(){
13568 throw new Error('couldn't find that author');
13569 }
13570 // synchronous
13571 try {
13572 findAuthor();
13573 } catch(reason) {
13574 // something went wrong
13575 }
13576 // async with promises
13577 findAuthor().catch(function(reason){
13578 // something went wrong
13579 });
13580 ```
13581 @method catch
13582 @param {Function} onRejection
13583 Useful for tooling.
13584 @return {Promise}
13585 */
13586
13587
13588 Promise.prototype.catch = function _catch(onRejection) {
13589 return this.then(null, onRejection);
13590 };
13591
13592 /**
13593 `finally` will be invoked regardless of the promise's fate just as native
13594 try/catch/finally behaves
13595
13596 Synchronous example:
13597
13598 ```js
13599 findAuthor() {
13600 if (Math.random() > 0.5) {
13601 throw new Error();
13602 }
13603 return new Author();
13604 }
13605
13606 try {
13607 return findAuthor(); // succeed or fail
13608 } catch(error) {
13609 return findOtherAuther();
13610 } finally {
13611 // always runs
13612 // doesn't affect the return value
13613 }
13614 ```
13615
13616 Asynchronous example:
13617
13618 ```js
13619 findAuthor().catch(function(reason){
13620 return findOtherAuther();
13621 }).finally(function(){
13622 // author was either found, or not
13623 });
13624 ```
13625
13626 @method finally
13627 @param {Function} callback
13628 @return {Promise}
13629 */
13630
13631
13632 Promise.prototype.finally = function _finally(callback) {
13633 var promise = this;
13634 var constructor = promise.constructor;
13635
13636 if (isFunction(callback)) {
13637 return promise.then(function (value) {
13638 return constructor.resolve(callback()).then(function () {
13639 return value;
13640 });
13641 }, function (reason) {
13642 return constructor.resolve(callback()).then(function () {
13643 throw reason;
13644 });
13645 });
13646 }
13647
13648 return promise.then(callback, callback);
13649 };
13650
13651 return Promise;
13652}();
13653
13654Promise$1.prototype.then = then;
13655Promise$1.all = all;
13656Promise$1.race = race;
13657Promise$1.resolve = resolve$1;
13658Promise$1.reject = reject$1;
13659Promise$1._setScheduler = setScheduler;
13660Promise$1._setAsap = setAsap;
13661Promise$1._asap = asap;
13662
13663/*global self*/
13664function polyfill() {
13665 var local = void 0;
13666
13667 if (typeof global !== 'undefined') {
13668 local = global;
13669 } else if (typeof self !== 'undefined') {
13670 local = self;
13671 } else {
13672 try {
13673 local = Function('return this')();
13674 } catch (e) {
13675 throw new Error('polyfill failed because global object is unavailable in this environment');
13676 }
13677 }
13678
13679 var P = local.Promise;
13680
13681 if (P) {
13682 var promiseToString = null;
13683 try {
13684 promiseToString = Object.prototype.toString.call(P.resolve());
13685 } catch (e) {
13686 // silently ignored
13687 }
13688
13689 if (promiseToString === '[object Promise]' && !P.cast) {
13690 return;
13691 }
13692 }
13693
13694 local.Promise = Promise$1;
13695}
13696
13697// Strange compat..
13698Promise$1.polyfill = polyfill;
13699Promise$1.Promise = Promise$1;
13700
13701return Promise$1;
13702
13703})));
13704
13705
13706
13707
13708
13709}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
13710},{"_process":31}],15:[function(_dereq_,module,exports){
13711/**
13712 * inspired by is-number <https://github.com/jonschlinkert/is-number>
13713 * but significantly simplified and sped up by ignoring number and string constructors
13714 * ie these return false:
13715 * new Number(1)
13716 * new String('1')
13717 */
13718
13719'use strict';
13720
13721var allBlankCharCodes = _dereq_('is-string-blank');
13722
13723module.exports = function(n) {
13724 var type = typeof n;
13725 if(type === 'string') {
13726 var original = n;
13727 n = +n;
13728 // whitespace strings cast to zero - filter them out
13729 if(n===0 && allBlankCharCodes(original)) return false;
13730 }
13731 else if(type !== 'number') return false;
13732
13733 return n - n < 1;
13734};
13735
13736},{"is-string-blank":20}],16:[function(_dereq_,module,exports){
13737module.exports = fromQuat;
13738
13739/**
13740 * Creates a matrix from a quaternion rotation.
13741 *
13742 * @param {mat4} out mat4 receiving operation result
13743 * @param {quat4} q Rotation quaternion
13744 * @returns {mat4} out
13745 */
13746function fromQuat(out, q) {
13747 var x = q[0], y = q[1], z = q[2], w = q[3],
13748 x2 = x + x,
13749 y2 = y + y,
13750 z2 = z + z,
13751
13752 xx = x * x2,
13753 yx = y * x2,
13754 yy = y * y2,
13755 zx = z * x2,
13756 zy = z * y2,
13757 zz = z * z2,
13758 wx = w * x2,
13759 wy = w * y2,
13760 wz = w * z2;
13761
13762 out[0] = 1 - yy - zz;
13763 out[1] = yx + wz;
13764 out[2] = zx - wy;
13765 out[3] = 0;
13766
13767 out[4] = yx - wz;
13768 out[5] = 1 - xx - zz;
13769 out[6] = zy + wx;
13770 out[7] = 0;
13771
13772 out[8] = zx + wy;
13773 out[9] = zy - wx;
13774 out[10] = 1 - xx - yy;
13775 out[11] = 0;
13776
13777 out[12] = 0;
13778 out[13] = 0;
13779 out[14] = 0;
13780 out[15] = 1;
13781
13782 return out;
13783};
13784},{}],17:[function(_dereq_,module,exports){
13785(function (global){
13786'use strict'
13787
13788var isBrowser = _dereq_('is-browser')
13789var hasHover
13790
13791if (typeof global.matchMedia === 'function') {
13792 hasHover = !global.matchMedia('(hover: none)').matches
13793}
13794else {
13795 hasHover = isBrowser
13796}
13797
13798module.exports = hasHover
13799
13800}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
13801},{"is-browser":19}],18:[function(_dereq_,module,exports){
13802'use strict'
13803
13804var isBrowser = _dereq_('is-browser')
13805
13806function detect() {
13807 var supported = false
13808
13809 try {
13810 var opts = Object.defineProperty({}, 'passive', {
13811 get: function() {
13812 supported = true
13813 }
13814 })
13815
13816 window.addEventListener('test', null, opts)
13817 window.removeEventListener('test', null, opts)
13818 } catch(e) {
13819 supported = false
13820 }
13821
13822 return supported
13823}
13824
13825module.exports = isBrowser && detect()
13826
13827},{"is-browser":19}],19:[function(_dereq_,module,exports){
13828module.exports = true;
13829},{}],20:[function(_dereq_,module,exports){
13830'use strict';
13831
13832/**
13833 * Is this string all whitespace?
13834 * This solution kind of makes my brain hurt, but it's significantly faster
13835 * than !str.trim() or any other solution I could find.
13836 *
13837 * whitespace codes from: http://en.wikipedia.org/wiki/Whitespace_character
13838 * and verified with:
13839 *
13840 * for(var i = 0; i < 65536; i++) {
13841 * var s = String.fromCharCode(i);
13842 * if(+s===0 && !s.trim()) console.log(i, s);
13843 * }
13844 *
13845 * which counts a couple of these as *not* whitespace, but finds nothing else
13846 * that *is* whitespace. Note that charCodeAt stops at 16 bits, but it appears
13847 * that there are no whitespace characters above this, and code points above
13848 * this do not map onto white space characters.
13849 */
13850
13851module.exports = function(str){
13852 var l = str.length,
13853 a;
13854 for(var i = 0; i < l; i++) {
13855 a = str.charCodeAt(i);
13856 if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) &&
13857 (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) &&
13858 (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) &&
13859 (a !== 8288) && (a !== 12288) && (a !== 65279)) {
13860 return false;
13861 }
13862 }
13863 return true;
13864}
13865
13866},{}],21:[function(_dereq_,module,exports){
13867var rootPosition = { left: 0, top: 0 }
13868
13869module.exports = mouseEventOffset
13870function mouseEventOffset (ev, target, out) {
13871 target = target || ev.currentTarget || ev.srcElement
13872 if (!Array.isArray(out)) {
13873 out = [ 0, 0 ]
13874 }
13875 var cx = ev.clientX || 0
13876 var cy = ev.clientY || 0
13877 var rect = getBoundingClientOffset(target)
13878 out[0] = cx - rect.left
13879 out[1] = cy - rect.top
13880 return out
13881}
13882
13883function getBoundingClientOffset (element) {
13884 if (element === window ||
13885 element === document ||
13886 element === document.body) {
13887 return rootPosition
13888 } else {
13889 return element.getBoundingClientRect()
13890 }
13891}
13892
13893},{}],22:[function(_dereq_,module,exports){
13894
13895module.exports = parse
13896
13897/**
13898 * expected argument lengths
13899 * @type {Object}
13900 */
13901
13902var length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}
13903
13904/**
13905 * segment pattern
13906 * @type {RegExp}
13907 */
13908
13909var segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig
13910
13911/**
13912 * parse an svg path data string. Generates an Array
13913 * of commands where each command is an Array of the
13914 * form `[command, arg1, arg2, ...]`
13915 *
13916 * @param {String} path
13917 * @return {Array}
13918 */
13919
13920function parse(path) {
13921 var data = []
13922 path.replace(segment, function(_, command, args){
13923 var type = command.toLowerCase()
13924 args = parseValues(args)
13925
13926 // overloaded moveTo
13927 if (type == 'm' && args.length > 2) {
13928 data.push([command].concat(args.splice(0, 2)))
13929 type = 'l'
13930 command = command == 'm' ? 'l' : 'L'
13931 }
13932
13933 while (true) {
13934 if (args.length == length[type]) {
13935 args.unshift(command)
13936 return data.push(args)
13937 }
13938 if (args.length < length[type]) throw new Error('malformed path data')
13939 data.push([command].concat(args.splice(0, length[type])))
13940 }
13941 })
13942 return data
13943}
13944
13945var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig
13946
13947function parseValues(args) {
13948 var numbers = args.match(number)
13949 return numbers ? numbers.map(Number) : []
13950}
13951
13952},{}],23:[function(_dereq_,module,exports){
13953/*
13954 * @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc
13955 * @license MIT
13956 * @preserve Project Home: https://github.com/voidqk/polybooljs
13957 */
13958
13959var BuildLog = _dereq_('./lib/build-log');
13960var Epsilon = _dereq_('./lib/epsilon');
13961var Intersecter = _dereq_('./lib/intersecter');
13962var SegmentChainer = _dereq_('./lib/segment-chainer');
13963var SegmentSelector = _dereq_('./lib/segment-selector');
13964var GeoJSON = _dereq_('./lib/geojson');
13965
13966var buildLog = false;
13967var epsilon = Epsilon();
13968
13969var PolyBool;
13970PolyBool = {
13971 // getter/setter for buildLog
13972 buildLog: function(bl){
13973 if (bl === true)
13974 buildLog = BuildLog();
13975 else if (bl === false)
13976 buildLog = false;
13977 return buildLog === false ? false : buildLog.list;
13978 },
13979 // getter/setter for epsilon
13980 epsilon: function(v){
13981 return epsilon.epsilon(v);
13982 },
13983
13984 // core API
13985 segments: function(poly){
13986 var i = Intersecter(true, epsilon, buildLog);
13987 poly.regions.forEach(i.addRegion);
13988 return {
13989 segments: i.calculate(poly.inverted),
13990 inverted: poly.inverted
13991 };
13992 },
13993 combine: function(segments1, segments2){
13994 var i3 = Intersecter(false, epsilon, buildLog);
13995 return {
13996 combined: i3.calculate(
13997 segments1.segments, segments1.inverted,
13998 segments2.segments, segments2.inverted
13999 ),
14000 inverted1: segments1.inverted,
14001 inverted2: segments2.inverted
14002 };
14003 },
14004 selectUnion: function(combined){
14005 return {
14006 segments: SegmentSelector.union(combined.combined, buildLog),
14007 inverted: combined.inverted1 || combined.inverted2
14008 }
14009 },
14010 selectIntersect: function(combined){
14011 return {
14012 segments: SegmentSelector.intersect(combined.combined, buildLog),
14013 inverted: combined.inverted1 && combined.inverted2
14014 }
14015 },
14016 selectDifference: function(combined){
14017 return {
14018 segments: SegmentSelector.difference(combined.combined, buildLog),
14019 inverted: combined.inverted1 && !combined.inverted2
14020 }
14021 },
14022 selectDifferenceRev: function(combined){
14023 return {
14024 segments: SegmentSelector.differenceRev(combined.combined, buildLog),
14025 inverted: !combined.inverted1 && combined.inverted2
14026 }
14027 },
14028 selectXor: function(combined){
14029 return {
14030 segments: SegmentSelector.xor(combined.combined, buildLog),
14031 inverted: combined.inverted1 !== combined.inverted2
14032 }
14033 },
14034 polygon: function(segments){
14035 return {
14036 regions: SegmentChainer(segments.segments, epsilon, buildLog),
14037 inverted: segments.inverted
14038 };
14039 },
14040
14041 // GeoJSON converters
14042 polygonFromGeoJSON: function(geojson){
14043 return GeoJSON.toPolygon(PolyBool, geojson);
14044 },
14045 polygonToGeoJSON: function(poly){
14046 return GeoJSON.fromPolygon(PolyBool, epsilon, poly);
14047 },
14048
14049 // helper functions for common operations
14050 union: function(poly1, poly2){
14051 return operate(poly1, poly2, PolyBool.selectUnion);
14052 },
14053 intersect: function(poly1, poly2){
14054 return operate(poly1, poly2, PolyBool.selectIntersect);
14055 },
14056 difference: function(poly1, poly2){
14057 return operate(poly1, poly2, PolyBool.selectDifference);
14058 },
14059 differenceRev: function(poly1, poly2){
14060 return operate(poly1, poly2, PolyBool.selectDifferenceRev);
14061 },
14062 xor: function(poly1, poly2){
14063 return operate(poly1, poly2, PolyBool.selectXor);
14064 }
14065};
14066
14067function operate(poly1, poly2, selector){
14068 var seg1 = PolyBool.segments(poly1);
14069 var seg2 = PolyBool.segments(poly2);
14070 var comb = PolyBool.combine(seg1, seg2);
14071 var seg3 = selector(comb);
14072 return PolyBool.polygon(seg3);
14073}
14074
14075if (typeof window === 'object')
14076 window.PolyBool = PolyBool;
14077
14078module.exports = PolyBool;
14079
14080},{"./lib/build-log":24,"./lib/epsilon":25,"./lib/geojson":26,"./lib/intersecter":27,"./lib/segment-chainer":29,"./lib/segment-selector":30}],24:[function(_dereq_,module,exports){
14081// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14082// MIT License
14083// Project Home: https://github.com/voidqk/polybooljs
14084
14085//
14086// used strictly for logging the processing of the algorithm... only useful if you intend on
14087// looking under the covers (for pretty UI's or debugging)
14088//
14089
14090function BuildLog(){
14091 var my;
14092 var nextSegmentId = 0;
14093 var curVert = false;
14094
14095 function push(type, data){
14096 my.list.push({
14097 type: type,
14098 data: data ? JSON.parse(JSON.stringify(data)) : void 0
14099 });
14100 return my;
14101 }
14102
14103 my = {
14104 list: [],
14105 segmentId: function(){
14106 return nextSegmentId++;
14107 },
14108 checkIntersection: function(seg1, seg2){
14109 return push('check', { seg1: seg1, seg2: seg2 });
14110 },
14111 segmentChop: function(seg, end){
14112 push('div_seg', { seg: seg, pt: end });
14113 return push('chop', { seg: seg, pt: end });
14114 },
14115 statusRemove: function(seg){
14116 return push('pop_seg', { seg: seg });
14117 },
14118 segmentUpdate: function(seg){
14119 return push('seg_update', { seg: seg });
14120 },
14121 segmentNew: function(seg, primary){
14122 return push('new_seg', { seg: seg, primary: primary });
14123 },
14124 segmentRemove: function(seg){
14125 return push('rem_seg', { seg: seg });
14126 },
14127 tempStatus: function(seg, above, below){
14128 return push('temp_status', { seg: seg, above: above, below: below });
14129 },
14130 rewind: function(seg){
14131 return push('rewind', { seg: seg });
14132 },
14133 status: function(seg, above, below){
14134 return push('status', { seg: seg, above: above, below: below });
14135 },
14136 vert: function(x){
14137 if (x === curVert)
14138 return my;
14139 curVert = x;
14140 return push('vert', { x: x });
14141 },
14142 log: function(data){
14143 if (typeof data !== 'string')
14144 data = JSON.stringify(data, false, ' ');
14145 return push('log', { txt: data });
14146 },
14147 reset: function(){
14148 return push('reset');
14149 },
14150 selected: function(segs){
14151 return push('selected', { segs: segs });
14152 },
14153 chainStart: function(seg){
14154 return push('chain_start', { seg: seg });
14155 },
14156 chainRemoveHead: function(index, pt){
14157 return push('chain_rem_head', { index: index, pt: pt });
14158 },
14159 chainRemoveTail: function(index, pt){
14160 return push('chain_rem_tail', { index: index, pt: pt });
14161 },
14162 chainNew: function(pt1, pt2){
14163 return push('chain_new', { pt1: pt1, pt2: pt2 });
14164 },
14165 chainMatch: function(index){
14166 return push('chain_match', { index: index });
14167 },
14168 chainClose: function(index){
14169 return push('chain_close', { index: index });
14170 },
14171 chainAddHead: function(index, pt){
14172 return push('chain_add_head', { index: index, pt: pt });
14173 },
14174 chainAddTail: function(index, pt){
14175 return push('chain_add_tail', { index: index, pt: pt, });
14176 },
14177 chainConnect: function(index1, index2){
14178 return push('chain_con', { index1: index1, index2: index2 });
14179 },
14180 chainReverse: function(index){
14181 return push('chain_rev', { index: index });
14182 },
14183 chainJoin: function(index1, index2){
14184 return push('chain_join', { index1: index1, index2: index2 });
14185 },
14186 done: function(){
14187 return push('done');
14188 }
14189 };
14190 return my;
14191}
14192
14193module.exports = BuildLog;
14194
14195},{}],25:[function(_dereq_,module,exports){
14196// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14197// MIT License
14198// Project Home: https://github.com/voidqk/polybooljs
14199
14200//
14201// provides the raw computation functions that takes epsilon into account
14202//
14203// zero is defined to be between (-epsilon, epsilon) exclusive
14204//
14205
14206function Epsilon(eps){
14207 if (typeof eps !== 'number')
14208 eps = 0.0000000001; // sane default? sure why not
14209 var my = {
14210 epsilon: function(v){
14211 if (typeof v === 'number')
14212 eps = v;
14213 return eps;
14214 },
14215 pointAboveOrOnLine: function(pt, left, right){
14216 var Ax = left[0];
14217 var Ay = left[1];
14218 var Bx = right[0];
14219 var By = right[1];
14220 var Cx = pt[0];
14221 var Cy = pt[1];
14222 return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax) >= -eps;
14223 },
14224 pointBetween: function(p, left, right){
14225 // p must be collinear with left->right
14226 // returns false if p == left, p == right, or left == right
14227 var d_py_ly = p[1] - left[1];
14228 var d_rx_lx = right[0] - left[0];
14229 var d_px_lx = p[0] - left[0];
14230 var d_ry_ly = right[1] - left[1];
14231
14232 var dot = d_px_lx * d_rx_lx + d_py_ly * d_ry_ly;
14233 // if `dot` is 0, then `p` == `left` or `left` == `right` (reject)
14234 // if `dot` is less than 0, then `p` is to the left of `left` (reject)
14235 if (dot < eps)
14236 return false;
14237
14238 var sqlen = d_rx_lx * d_rx_lx + d_ry_ly * d_ry_ly;
14239 // if `dot` > `sqlen`, then `p` is to the right of `right` (reject)
14240 // therefore, if `dot - sqlen` is greater than 0, then `p` is to the right of `right` (reject)
14241 if (dot - sqlen > -eps)
14242 return false;
14243
14244 return true;
14245 },
14246 pointsSameX: function(p1, p2){
14247 return Math.abs(p1[0] - p2[0]) < eps;
14248 },
14249 pointsSameY: function(p1, p2){
14250 return Math.abs(p1[1] - p2[1]) < eps;
14251 },
14252 pointsSame: function(p1, p2){
14253 return my.pointsSameX(p1, p2) && my.pointsSameY(p1, p2);
14254 },
14255 pointsCompare: function(p1, p2){
14256 // returns -1 if p1 is smaller, 1 if p2 is smaller, 0 if equal
14257 if (my.pointsSameX(p1, p2))
14258 return my.pointsSameY(p1, p2) ? 0 : (p1[1] < p2[1] ? -1 : 1);
14259 return p1[0] < p2[0] ? -1 : 1;
14260 },
14261 pointsCollinear: function(pt1, pt2, pt3){
14262 // does pt1->pt2->pt3 make a straight line?
14263 // essentially this is just checking to see if the slope(pt1->pt2) === slope(pt2->pt3)
14264 // if slopes are equal, then they must be collinear, because they share pt2
14265 var dx1 = pt1[0] - pt2[0];
14266 var dy1 = pt1[1] - pt2[1];
14267 var dx2 = pt2[0] - pt3[0];
14268 var dy2 = pt2[1] - pt3[1];
14269 return Math.abs(dx1 * dy2 - dx2 * dy1) < eps;
14270 },
14271 linesIntersect: function(a0, a1, b0, b1){
14272 // returns false if the lines are coincident (e.g., parallel or on top of each other)
14273 //
14274 // returns an object if the lines intersect:
14275 // {
14276 // pt: [x, y], where the intersection point is at
14277 // alongA: where intersection point is along A,
14278 // alongB: where intersection point is along B
14279 // }
14280 //
14281 // alongA and alongB will each be one of: -2, -1, 0, 1, 2
14282 //
14283 // with the following meaning:
14284 //
14285 // -2 intersection point is before segment's first point
14286 // -1 intersection point is directly on segment's first point
14287 // 0 intersection point is between segment's first and second points (exclusive)
14288 // 1 intersection point is directly on segment's second point
14289 // 2 intersection point is after segment's second point
14290 var adx = a1[0] - a0[0];
14291 var ady = a1[1] - a0[1];
14292 var bdx = b1[0] - b0[0];
14293 var bdy = b1[1] - b0[1];
14294
14295 var axb = adx * bdy - ady * bdx;
14296 if (Math.abs(axb) < eps)
14297 return false; // lines are coincident
14298
14299 var dx = a0[0] - b0[0];
14300 var dy = a0[1] - b0[1];
14301
14302 var A = (bdx * dy - bdy * dx) / axb;
14303 var B = (adx * dy - ady * dx) / axb;
14304
14305 var ret = {
14306 alongA: 0,
14307 alongB: 0,
14308 pt: [
14309 a0[0] + A * adx,
14310 a0[1] + A * ady
14311 ]
14312 };
14313
14314 // categorize where intersection point is along A and B
14315
14316 if (A <= -eps)
14317 ret.alongA = -2;
14318 else if (A < eps)
14319 ret.alongA = -1;
14320 else if (A - 1 <= -eps)
14321 ret.alongA = 0;
14322 else if (A - 1 < eps)
14323 ret.alongA = 1;
14324 else
14325 ret.alongA = 2;
14326
14327 if (B <= -eps)
14328 ret.alongB = -2;
14329 else if (B < eps)
14330 ret.alongB = -1;
14331 else if (B - 1 <= -eps)
14332 ret.alongB = 0;
14333 else if (B - 1 < eps)
14334 ret.alongB = 1;
14335 else
14336 ret.alongB = 2;
14337
14338 return ret;
14339 },
14340 pointInsideRegion: function(pt, region){
14341 var x = pt[0];
14342 var y = pt[1];
14343 var last_x = region[region.length - 1][0];
14344 var last_y = region[region.length - 1][1];
14345 var inside = false;
14346 for (var i = 0; i < region.length; i++){
14347 var curr_x = region[i][0];
14348 var curr_y = region[i][1];
14349
14350 // if y is between curr_y and last_y, and
14351 // x is to the right of the boundary created by the line
14352 if ((curr_y - y > eps) != (last_y - y > eps) &&
14353 (last_x - curr_x) * (y - curr_y) / (last_y - curr_y) + curr_x - x > eps)
14354 inside = !inside
14355
14356 last_x = curr_x;
14357 last_y = curr_y;
14358 }
14359 return inside;
14360 }
14361 };
14362 return my;
14363}
14364
14365module.exports = Epsilon;
14366
14367},{}],26:[function(_dereq_,module,exports){
14368// (c) Copyright 2017, Sean Connelly (@voidqk), http://syntheti.cc
14369// MIT License
14370// Project Home: https://github.com/voidqk/polybooljs
14371
14372//
14373// convert between PolyBool polygon format and GeoJSON formats (Polygon and MultiPolygon)
14374//
14375
14376var GeoJSON = {
14377 // convert a GeoJSON object to a PolyBool polygon
14378 toPolygon: function(PolyBool, geojson){
14379
14380 // converts list of LineString's to segments
14381 function GeoPoly(coords){
14382 // check for empty coords
14383 if (coords.length <= 0)
14384 return PolyBool.segments({ inverted: false, regions: [] });
14385
14386 // convert LineString to segments
14387 function LineString(ls){
14388 // remove tail which should be the same as head
14389 var reg = ls.slice(0, ls.length - 1);
14390 return PolyBool.segments({ inverted: false, regions: [reg] });
14391 }
14392
14393 // the first LineString is considered the outside
14394 var out = LineString(coords[0]);
14395
14396 // the rest of the LineStrings are considered interior holes, so subtract them from the
14397 // current result
14398 for (var i = 1; i < coords.length; i++)
14399 out = PolyBool.selectDifference(PolyBool.combine(out, LineString(coords[i])));
14400
14401 return out;
14402 }
14403
14404 if (geojson.type === 'Polygon'){
14405 // single polygon, so just convert it and we're done
14406 return PolyBool.polygon(GeoPoly(geojson.coordinates));
14407 }
14408 else if (geojson.type === 'MultiPolygon'){
14409 // multiple polygons, so union all the polygons together
14410 var out = PolyBool.segments({ inverted: false, regions: [] });
14411 for (var i = 0; i < geojson.coordinates.length; i++)
14412 out = PolyBool.selectUnion(PolyBool.combine(out, GeoPoly(geojson.coordinates[i])));
14413 return PolyBool.polygon(out);
14414 }
14415 throw new Error('PolyBool: Cannot convert GeoJSON object to PolyBool polygon');
14416 },
14417
14418 // convert a PolyBool polygon to a GeoJSON object
14419 fromPolygon: function(PolyBool, eps, poly){
14420 // make sure out polygon is clean
14421 poly = PolyBool.polygon(PolyBool.segments(poly));
14422
14423 // test if r1 is inside r2
14424 function regionInsideRegion(r1, r2){
14425 // we're guaranteed no lines intersect (because the polygon is clean), but a vertex
14426 // could be on the edge -- so we just average pt[0] and pt[1] to produce a point on the
14427 // edge of the first line, which cannot be on an edge
14428 return eps.pointInsideRegion([
14429 (r1[0][0] + r1[1][0]) * 0.5,
14430 (r1[0][1] + r1[1][1]) * 0.5
14431 ], r2);
14432 }
14433
14434 // calculate inside heirarchy
14435 //
14436 // _____________________ _______ roots -> A -> F
14437 // | A | | F | | |
14438 // | _______ _______ | | ___ | +-- B +-- G
14439 // | | B | | C | | | | | | | |
14440 // | | ___ | | ___ | | | | | | | +-- D
14441 // | | | D | | | | E | | | | | G | | |
14442 // | | |___| | | |___| | | | | | | +-- C
14443 // | |_______| |_______| | | |___| | |
14444 // |_____________________| |_______| +-- E
14445
14446 function newNode(region){
14447 return {
14448 region: region,
14449 children: []
14450 };
14451 }
14452
14453 var roots = newNode(null);
14454
14455 function addChild(root, region){
14456 // first check if we're inside any children
14457 for (var i = 0; i < root.children.length; i++){
14458 var child = root.children[i];
14459 if (regionInsideRegion(region, child.region)){
14460 // we are, so insert inside them instead
14461 addChild(child, region);
14462 return;
14463 }
14464 }
14465
14466 // not inside any children, so check to see if any children are inside us
14467 var node = newNode(region);
14468 for (var i = 0; i < root.children.length; i++){
14469 var child = root.children[i];
14470 if (regionInsideRegion(child.region, region)){
14471 // oops... move the child beneath us, and remove them from root
14472 node.children.push(child);
14473 root.children.splice(i, 1);
14474 i--;
14475 }
14476 }
14477
14478 // now we can add ourselves
14479 root.children.push(node);
14480 }
14481
14482 // add all regions to the root
14483 for (var i = 0; i < poly.regions.length; i++){
14484 var region = poly.regions[i];
14485 if (region.length < 3) // regions must have at least 3 points (sanity check)
14486 continue;
14487 addChild(roots, region);
14488 }
14489
14490 // with our heirarchy, we can distinguish between exterior borders, and interior holes
14491 // the root nodes are exterior, children are interior, children's children are exterior,
14492 // children's children's children are interior, etc
14493
14494 // while we're at it, exteriors are counter-clockwise, and interiors are clockwise
14495
14496 function forceWinding(region, clockwise){
14497 // first, see if we're clockwise or counter-clockwise
14498 // https://en.wikipedia.org/wiki/Shoelace_formula
14499 var winding = 0;
14500 var last_x = region[region.length - 1][0];
14501 var last_y = region[region.length - 1][1];
14502 var copy = [];
14503 for (var i = 0; i < region.length; i++){
14504 var curr_x = region[i][0];
14505 var curr_y = region[i][1];
14506 copy.push([curr_x, curr_y]); // create a copy while we're at it
14507 winding += curr_y * last_x - curr_x * last_y;
14508 last_x = curr_x;
14509 last_y = curr_y;
14510 }
14511 // this assumes Cartesian coordinates (Y is positive going up)
14512 var isclockwise = winding < 0;
14513 if (isclockwise !== clockwise)
14514 copy.reverse();
14515 // while we're here, the last point must be the first point...
14516 copy.push([copy[0][0], copy[0][1]]);
14517 return copy;
14518 }
14519
14520 var geopolys = [];
14521
14522 function addExterior(node){
14523 var poly = [forceWinding(node.region, false)];
14524 geopolys.push(poly);
14525 // children of exteriors are interior
14526 for (var i = 0; i < node.children.length; i++)
14527 poly.push(getInterior(node.children[i]));
14528 }
14529
14530 function getInterior(node){
14531 // children of interiors are exterior
14532 for (var i = 0; i < node.children.length; i++)
14533 addExterior(node.children[i]);
14534 // return the clockwise interior
14535 return forceWinding(node.region, true);
14536 }
14537
14538 // root nodes are exterior
14539 for (var i = 0; i < roots.children.length; i++)
14540 addExterior(roots.children[i]);
14541
14542 // lastly, construct the approrpriate GeoJSON object
14543
14544 if (geopolys.length <= 0) // empty GeoJSON Polygon
14545 return { type: 'Polygon', coordinates: [] };
14546 if (geopolys.length == 1) // use a GeoJSON Polygon
14547 return { type: 'Polygon', coordinates: geopolys[0] };
14548 return { // otherwise, use a GeoJSON MultiPolygon
14549 type: 'MultiPolygon',
14550 coordinates: geopolys
14551 };
14552 }
14553};
14554
14555module.exports = GeoJSON;
14556
14557},{}],27:[function(_dereq_,module,exports){
14558// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14559// MIT License
14560// Project Home: https://github.com/voidqk/polybooljs
14561
14562//
14563// this is the core work-horse
14564//
14565
14566var LinkedList = _dereq_('./linked-list');
14567
14568function Intersecter(selfIntersection, eps, buildLog){
14569 // selfIntersection is true/false depending on the phase of the overall algorithm
14570
14571 //
14572 // segment creation
14573 //
14574
14575 function segmentNew(start, end){
14576 return {
14577 id: buildLog ? buildLog.segmentId() : -1,
14578 start: start,
14579 end: end,
14580 myFill: {
14581 above: null, // is there fill above us?
14582 below: null // is there fill below us?
14583 },
14584 otherFill: null
14585 };
14586 }
14587
14588 function segmentCopy(start, end, seg){
14589 return {
14590 id: buildLog ? buildLog.segmentId() : -1,
14591 start: start,
14592 end: end,
14593 myFill: {
14594 above: seg.myFill.above,
14595 below: seg.myFill.below
14596 },
14597 otherFill: null
14598 };
14599 }
14600
14601 //
14602 // event logic
14603 //
14604
14605 var event_root = LinkedList.create();
14606
14607 function eventCompare(p1_isStart, p1_1, p1_2, p2_isStart, p2_1, p2_2){
14608 // compare the selected points first
14609 var comp = eps.pointsCompare(p1_1, p2_1);
14610 if (comp !== 0)
14611 return comp;
14612 // the selected points are the same
14613
14614 if (eps.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too...
14615 return 0; // then the segments are equal
14616
14617 if (p1_isStart !== p2_isStart) // if one is a start and the other isn't...
14618 return p1_isStart ? 1 : -1; // favor the one that isn't the start
14619
14620 // otherwise, we'll have to calculate which one is below the other manually
14621 return eps.pointAboveOrOnLine(p1_2,
14622 p2_isStart ? p2_1 : p2_2, // order matters
14623 p2_isStart ? p2_2 : p2_1
14624 ) ? 1 : -1;
14625 }
14626
14627 function eventAdd(ev, other_pt){
14628 event_root.insertBefore(ev, function(here){
14629 // should ev be inserted before here?
14630 var comp = eventCompare(
14631 ev .isStart, ev .pt, other_pt,
14632 here.isStart, here.pt, here.other.pt
14633 );
14634 return comp < 0;
14635 });
14636 }
14637
14638 function eventAddSegmentStart(seg, primary){
14639 var ev_start = LinkedList.node({
14640 isStart: true,
14641 pt: seg.start,
14642 seg: seg,
14643 primary: primary,
14644 other: null,
14645 status: null
14646 });
14647 eventAdd(ev_start, seg.end);
14648 return ev_start;
14649 }
14650
14651 function eventAddSegmentEnd(ev_start, seg, primary){
14652 var ev_end = LinkedList.node({
14653 isStart: false,
14654 pt: seg.end,
14655 seg: seg,
14656 primary: primary,
14657 other: ev_start,
14658 status: null
14659 });
14660 ev_start.other = ev_end;
14661 eventAdd(ev_end, ev_start.pt);
14662 }
14663
14664 function eventAddSegment(seg, primary){
14665 var ev_start = eventAddSegmentStart(seg, primary);
14666 eventAddSegmentEnd(ev_start, seg, primary);
14667 return ev_start;
14668 }
14669
14670 function eventUpdateEnd(ev, end){
14671 // slides an end backwards
14672 // (start)------------(end) to:
14673 // (start)---(end)
14674
14675 if (buildLog)
14676 buildLog.segmentChop(ev.seg, end);
14677
14678 ev.other.remove();
14679 ev.seg.end = end;
14680 ev.other.pt = end;
14681 eventAdd(ev.other, ev.pt);
14682 }
14683
14684 function eventDivide(ev, pt){
14685 var ns = segmentCopy(pt, ev.seg.end, ev.seg);
14686 eventUpdateEnd(ev, pt);
14687 return eventAddSegment(ns, ev.primary);
14688 }
14689
14690 function calculate(primaryPolyInverted, secondaryPolyInverted){
14691 // if selfIntersection is true then there is no secondary polygon, so that isn't used
14692
14693 //
14694 // status logic
14695 //
14696
14697 var status_root = LinkedList.create();
14698
14699 function statusCompare(ev1, ev2){
14700 var a1 = ev1.seg.start;
14701 var a2 = ev1.seg.end;
14702 var b1 = ev2.seg.start;
14703 var b2 = ev2.seg.end;
14704
14705 if (eps.pointsCollinear(a1, b1, b2)){
14706 if (eps.pointsCollinear(a2, b1, b2))
14707 return 1;//eventCompare(true, a1, a2, true, b1, b2);
14708 return eps.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1;
14709 }
14710 return eps.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1;
14711 }
14712
14713 function statusFindSurrounding(ev){
14714 return status_root.findTransition(function(here){
14715 var comp = statusCompare(ev, here.ev);
14716 return comp > 0;
14717 });
14718 }
14719
14720 function checkIntersection(ev1, ev2){
14721 // returns the segment equal to ev1, or false if nothing equal
14722
14723 var seg1 = ev1.seg;
14724 var seg2 = ev2.seg;
14725 var a1 = seg1.start;
14726 var a2 = seg1.end;
14727 var b1 = seg2.start;
14728 var b2 = seg2.end;
14729
14730 if (buildLog)
14731 buildLog.checkIntersection(seg1, seg2);
14732
14733 var i = eps.linesIntersect(a1, a2, b1, b2);
14734
14735 if (i === false){
14736 // segments are parallel or coincident
14737
14738 // if points aren't collinear, then the segments are parallel, so no intersections
14739 if (!eps.pointsCollinear(a1, a2, b1))
14740 return false;
14741 // otherwise, segments are on top of each other somehow (aka coincident)
14742
14743 if (eps.pointsSame(a1, b2) || eps.pointsSame(a2, b1))
14744 return false; // segments touch at endpoints... no intersection
14745
14746 var a1_equ_b1 = eps.pointsSame(a1, b1);
14747 var a2_equ_b2 = eps.pointsSame(a2, b2);
14748
14749 if (a1_equ_b1 && a2_equ_b2)
14750 return ev2; // segments are exactly equal
14751
14752 var a1_between = !a1_equ_b1 && eps.pointBetween(a1, b1, b2);
14753 var a2_between = !a2_equ_b2 && eps.pointBetween(a2, b1, b2);
14754
14755 // handy for debugging:
14756 // buildLog.log({
14757 // a1_equ_b1: a1_equ_b1,
14758 // a2_equ_b2: a2_equ_b2,
14759 // a1_between: a1_between,
14760 // a2_between: a2_between
14761 // });
14762
14763 if (a1_equ_b1){
14764 if (a2_between){
14765 // (a1)---(a2)
14766 // (b1)----------(b2)
14767 eventDivide(ev2, a2);
14768 }
14769 else{
14770 // (a1)----------(a2)
14771 // (b1)---(b2)
14772 eventDivide(ev1, b2);
14773 }
14774 return ev2;
14775 }
14776 else if (a1_between){
14777 if (!a2_equ_b2){
14778 // make a2 equal to b2
14779 if (a2_between){
14780 // (a1)---(a2)
14781 // (b1)-----------------(b2)
14782 eventDivide(ev2, a2);
14783 }
14784 else{
14785 // (a1)----------(a2)
14786 // (b1)----------(b2)
14787 eventDivide(ev1, b2);
14788 }
14789 }
14790
14791 // (a1)---(a2)
14792 // (b1)----------(b2)
14793 eventDivide(ev2, a1);
14794 }
14795 }
14796 else{
14797 // otherwise, lines intersect at i.pt, which may or may not be between the endpoints
14798
14799 // is A divided between its endpoints? (exclusive)
14800 if (i.alongA === 0){
14801 if (i.alongB === -1) // yes, at exactly b1
14802 eventDivide(ev1, b1);
14803 else if (i.alongB === 0) // yes, somewhere between B's endpoints
14804 eventDivide(ev1, i.pt);
14805 else if (i.alongB === 1) // yes, at exactly b2
14806 eventDivide(ev1, b2);
14807 }
14808
14809 // is B divided between its endpoints? (exclusive)
14810 if (i.alongB === 0){
14811 if (i.alongA === -1) // yes, at exactly a1
14812 eventDivide(ev2, a1);
14813 else if (i.alongA === 0) // yes, somewhere between A's endpoints (exclusive)
14814 eventDivide(ev2, i.pt);
14815 else if (i.alongA === 1) // yes, at exactly a2
14816 eventDivide(ev2, a2);
14817 }
14818 }
14819 return false;
14820 }
14821
14822 //
14823 // main event loop
14824 //
14825 var segments = [];
14826 while (!event_root.isEmpty()){
14827 var ev = event_root.getHead();
14828
14829 if (buildLog)
14830 buildLog.vert(ev.pt[0]);
14831
14832 if (ev.isStart){
14833
14834 if (buildLog)
14835 buildLog.segmentNew(ev.seg, ev.primary);
14836
14837 var surrounding = statusFindSurrounding(ev);
14838 var above = surrounding.before ? surrounding.before.ev : null;
14839 var below = surrounding.after ? surrounding.after.ev : null;
14840
14841 if (buildLog){
14842 buildLog.tempStatus(
14843 ev.seg,
14844 above ? above.seg : false,
14845 below ? below.seg : false
14846 );
14847 }
14848
14849 function checkBothIntersections(){
14850 if (above){
14851 var eve = checkIntersection(ev, above);
14852 if (eve)
14853 return eve;
14854 }
14855 if (below)
14856 return checkIntersection(ev, below);
14857 return false;
14858 }
14859
14860 var eve = checkBothIntersections();
14861 if (eve){
14862 // ev and eve are equal
14863 // we'll keep eve and throw away ev
14864
14865 // merge ev.seg's fill information into eve.seg
14866
14867 if (selfIntersection){
14868 var toggle; // are we a toggling edge?
14869 if (ev.seg.myFill.below === null)
14870 toggle = true;
14871 else
14872 toggle = ev.seg.myFill.above !== ev.seg.myFill.below;
14873
14874 // merge two segments that belong to the same polygon
14875 // think of this as sandwiching two segments together, where `eve.seg` is
14876 // the bottom -- this will cause the above fill flag to toggle
14877 if (toggle)
14878 eve.seg.myFill.above = !eve.seg.myFill.above;
14879 }
14880 else{
14881 // merge two segments that belong to different polygons
14882 // each segment has distinct knowledge, so no special logic is needed
14883 // note that this can only happen once per segment in this phase, because we
14884 // are guaranteed that all self-intersections are gone
14885 eve.seg.otherFill = ev.seg.myFill;
14886 }
14887
14888 if (buildLog)
14889 buildLog.segmentUpdate(eve.seg);
14890
14891 ev.other.remove();
14892 ev.remove();
14893 }
14894
14895 if (event_root.getHead() !== ev){
14896 // something was inserted before us in the event queue, so loop back around and
14897 // process it before continuing
14898 if (buildLog)
14899 buildLog.rewind(ev.seg);
14900 continue;
14901 }
14902
14903 //
14904 // calculate fill flags
14905 //
14906 if (selfIntersection){
14907 var toggle; // are we a toggling edge?
14908 if (ev.seg.myFill.below === null) // if we are a new segment...
14909 toggle = true; // then we toggle
14910 else // we are a segment that has previous knowledge from a division
14911 toggle = ev.seg.myFill.above !== ev.seg.myFill.below; // calculate toggle
14912
14913 // next, calculate whether we are filled below us
14914 if (!below){ // if nothing is below us...
14915 // we are filled below us if the polygon is inverted
14916 ev.seg.myFill.below = primaryPolyInverted;
14917 }
14918 else{
14919 // otherwise, we know the answer -- it's the same if whatever is below
14920 // us is filled above it
14921 ev.seg.myFill.below = below.seg.myFill.above;
14922 }
14923
14924 // since now we know if we're filled below us, we can calculate whether
14925 // we're filled above us by applying toggle to whatever is below us
14926 if (toggle)
14927 ev.seg.myFill.above = !ev.seg.myFill.below;
14928 else
14929 ev.seg.myFill.above = ev.seg.myFill.below;
14930 }
14931 else{
14932 // now we fill in any missing transition information, since we are all-knowing
14933 // at this point
14934
14935 if (ev.seg.otherFill === null){
14936 // if we don't have other information, then we need to figure out if we're
14937 // inside the other polygon
14938 var inside;
14939 if (!below){
14940 // if nothing is below us, then we're inside if the other polygon is
14941 // inverted
14942 inside =
14943 ev.primary ? secondaryPolyInverted : primaryPolyInverted;
14944 }
14945 else{ // otherwise, something is below us
14946 // so copy the below segment's other polygon's above
14947 if (ev.primary === below.primary)
14948 inside = below.seg.otherFill.above;
14949 else
14950 inside = below.seg.myFill.above;
14951 }
14952 ev.seg.otherFill = {
14953 above: inside,
14954 below: inside
14955 };
14956 }
14957 }
14958
14959 if (buildLog){
14960 buildLog.status(
14961 ev.seg,
14962 above ? above.seg : false,
14963 below ? below.seg : false
14964 );
14965 }
14966
14967 // insert the status and remember it for later removal
14968 ev.other.status = surrounding.insert(LinkedList.node({ ev: ev }));
14969 }
14970 else{
14971 var st = ev.status;
14972
14973 if (st === null){
14974 throw new Error('PolyBool: Zero-length segment detected; your epsilon is ' +
14975 'probably too small or too large');
14976 }
14977
14978 // removing the status will create two new adjacent edges, so we'll need to check
14979 // for those
14980 if (status_root.exists(st.prev) && status_root.exists(st.next))
14981 checkIntersection(st.prev.ev, st.next.ev);
14982
14983 if (buildLog)
14984 buildLog.statusRemove(st.ev.seg);
14985
14986 // remove the status
14987 st.remove();
14988
14989 // if we've reached this point, we've calculated everything there is to know, so
14990 // save the segment for reporting
14991 if (!ev.primary){
14992 // make sure `seg.myFill` actually points to the primary polygon though
14993 var s = ev.seg.myFill;
14994 ev.seg.myFill = ev.seg.otherFill;
14995 ev.seg.otherFill = s;
14996 }
14997 segments.push(ev.seg);
14998 }
14999
15000 // remove the event and continue
15001 event_root.getHead().remove();
15002 }
15003
15004 if (buildLog)
15005 buildLog.done();
15006
15007 return segments;
15008 }
15009
15010 // return the appropriate API depending on what we're doing
15011 if (!selfIntersection){
15012 // performing combination of polygons, so only deal with already-processed segments
15013 return {
15014 calculate: function(segments1, inverted1, segments2, inverted2){
15015 // segmentsX come from the self-intersection API, or this API
15016 // invertedX is whether we treat that list of segments as an inverted polygon or not
15017 // returns segments that can be used for further operations
15018 segments1.forEach(function(seg){
15019 eventAddSegment(segmentCopy(seg.start, seg.end, seg), true);
15020 });
15021 segments2.forEach(function(seg){
15022 eventAddSegment(segmentCopy(seg.start, seg.end, seg), false);
15023 });
15024 return calculate(inverted1, inverted2);
15025 }
15026 };
15027 }
15028
15029 // otherwise, performing self-intersection, so deal with regions
15030 return {
15031 addRegion: function(region){
15032 // regions are a list of points:
15033 // [ [0, 0], [100, 0], [50, 100] ]
15034 // you can add multiple regions before running calculate
15035 var pt1;
15036 var pt2 = region[region.length - 1];
15037 for (var i = 0; i < region.length; i++){
15038 pt1 = pt2;
15039 pt2 = region[i];
15040
15041 var forward = eps.pointsCompare(pt1, pt2);
15042 if (forward === 0) // points are equal, so we have a zero-length segment
15043 continue; // just skip it
15044
15045 eventAddSegment(
15046 segmentNew(
15047 forward < 0 ? pt1 : pt2,
15048 forward < 0 ? pt2 : pt1
15049 ),
15050 true
15051 );
15052 }
15053 },
15054 calculate: function(inverted){
15055 // is the polygon inverted?
15056 // returns segments
15057 return calculate(inverted, false);
15058 }
15059 };
15060}
15061
15062module.exports = Intersecter;
15063
15064},{"./linked-list":28}],28:[function(_dereq_,module,exports){
15065// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15066// MIT License
15067// Project Home: https://github.com/voidqk/polybooljs
15068
15069//
15070// simple linked list implementation that allows you to traverse down nodes and save positions
15071//
15072
15073var LinkedList = {
15074 create: function(){
15075 var my = {
15076 root: { root: true, next: null },
15077 exists: function(node){
15078 if (node === null || node === my.root)
15079 return false;
15080 return true;
15081 },
15082 isEmpty: function(){
15083 return my.root.next === null;
15084 },
15085 getHead: function(){
15086 return my.root.next;
15087 },
15088 insertBefore: function(node, check){
15089 var last = my.root;
15090 var here = my.root.next;
15091 while (here !== null){
15092 if (check(here)){
15093 node.prev = here.prev;
15094 node.next = here;
15095 here.prev.next = node;
15096 here.prev = node;
15097 return;
15098 }
15099 last = here;
15100 here = here.next;
15101 }
15102 last.next = node;
15103 node.prev = last;
15104 node.next = null;
15105 },
15106 findTransition: function(check){
15107 var prev = my.root;
15108 var here = my.root.next;
15109 while (here !== null){
15110 if (check(here))
15111 break;
15112 prev = here;
15113 here = here.next;
15114 }
15115 return {
15116 before: prev === my.root ? null : prev,
15117 after: here,
15118 insert: function(node){
15119 node.prev = prev;
15120 node.next = here;
15121 prev.next = node;
15122 if (here !== null)
15123 here.prev = node;
15124 return node;
15125 }
15126 };
15127 }
15128 };
15129 return my;
15130 },
15131 node: function(data){
15132 data.prev = null;
15133 data.next = null;
15134 data.remove = function(){
15135 data.prev.next = data.next;
15136 if (data.next)
15137 data.next.prev = data.prev;
15138 data.prev = null;
15139 data.next = null;
15140 };
15141 return data;
15142 }
15143};
15144
15145module.exports = LinkedList;
15146
15147},{}],29:[function(_dereq_,module,exports){
15148// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15149// MIT License
15150// Project Home: https://github.com/voidqk/polybooljs
15151
15152//
15153// converts a list of segments into a list of regions, while also removing unnecessary verticies
15154//
15155
15156function SegmentChainer(segments, eps, buildLog){
15157 var chains = [];
15158 var regions = [];
15159
15160 segments.forEach(function(seg){
15161 var pt1 = seg.start;
15162 var pt2 = seg.end;
15163 if (eps.pointsSame(pt1, pt2)){
15164 console.warn('PolyBool: Warning: Zero-length segment detected; your epsilon is ' +
15165 'probably too small or too large');
15166 return;
15167 }
15168
15169 if (buildLog)
15170 buildLog.chainStart(seg);
15171
15172 // search for two chains that this segment matches
15173 var first_match = {
15174 index: 0,
15175 matches_head: false,
15176 matches_pt1: false
15177 };
15178 var second_match = {
15179 index: 0,
15180 matches_head: false,
15181 matches_pt1: false
15182 };
15183 var next_match = first_match;
15184 function setMatch(index, matches_head, matches_pt1){
15185 // return true if we've matched twice
15186 next_match.index = index;
15187 next_match.matches_head = matches_head;
15188 next_match.matches_pt1 = matches_pt1;
15189 if (next_match === first_match){
15190 next_match = second_match;
15191 return false;
15192 }
15193 next_match = null;
15194 return true; // we've matched twice, we're done here
15195 }
15196 for (var i = 0; i < chains.length; i++){
15197 var chain = chains[i];
15198 var head = chain[0];
15199 var head2 = chain[1];
15200 var tail = chain[chain.length - 1];
15201 var tail2 = chain[chain.length - 2];
15202 if (eps.pointsSame(head, pt1)){
15203 if (setMatch(i, true, true))
15204 break;
15205 }
15206 else if (eps.pointsSame(head, pt2)){
15207 if (setMatch(i, true, false))
15208 break;
15209 }
15210 else if (eps.pointsSame(tail, pt1)){
15211 if (setMatch(i, false, true))
15212 break;
15213 }
15214 else if (eps.pointsSame(tail, pt2)){
15215 if (setMatch(i, false, false))
15216 break;
15217 }
15218 }
15219
15220 if (next_match === first_match){
15221 // we didn't match anything, so create a new chain
15222 chains.push([ pt1, pt2 ]);
15223 if (buildLog)
15224 buildLog.chainNew(pt1, pt2);
15225 return;
15226 }
15227
15228 if (next_match === second_match){
15229 // we matched a single chain
15230
15231 if (buildLog)
15232 buildLog.chainMatch(first_match.index);
15233
15234 // add the other point to the apporpriate end, and check to see if we've closed the
15235 // chain into a loop
15236
15237 var index = first_match.index;
15238 var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc
15239 var addToHead = first_match.matches_head; // if we matched at head, then add to the head
15240
15241 var chain = chains[index];
15242 var grow = addToHead ? chain[0] : chain[chain.length - 1];
15243 var grow2 = addToHead ? chain[1] : chain[chain.length - 2];
15244 var oppo = addToHead ? chain[chain.length - 1] : chain[0];
15245 var oppo2 = addToHead ? chain[chain.length - 2] : chain[1];
15246
15247 if (eps.pointsCollinear(grow2, grow, pt)){
15248 // grow isn't needed because it's directly between grow2 and pt:
15249 // grow2 ---grow---> pt
15250 if (addToHead){
15251 if (buildLog)
15252 buildLog.chainRemoveHead(first_match.index, pt);
15253 chain.shift();
15254 }
15255 else{
15256 if (buildLog)
15257 buildLog.chainRemoveTail(first_match.index, pt);
15258 chain.pop();
15259 }
15260 grow = grow2; // old grow is gone... new grow is what grow2 was
15261 }
15262
15263 if (eps.pointsSame(oppo, pt)){
15264 // we're closing the loop, so remove chain from chains
15265 chains.splice(index, 1);
15266
15267 if (eps.pointsCollinear(oppo2, oppo, grow)){
15268 // oppo isn't needed because it's directly between oppo2 and grow:
15269 // oppo2 ---oppo--->grow
15270 if (addToHead){
15271 if (buildLog)
15272 buildLog.chainRemoveTail(first_match.index, grow);
15273 chain.pop();
15274 }
15275 else{
15276 if (buildLog)
15277 buildLog.chainRemoveHead(first_match.index, grow);
15278 chain.shift();
15279 }
15280 }
15281
15282 if (buildLog)
15283 buildLog.chainClose(first_match.index);
15284
15285 // we have a closed chain!
15286 regions.push(chain);
15287 return;
15288 }
15289
15290 // not closing a loop, so just add it to the apporpriate side
15291 if (addToHead){
15292 if (buildLog)
15293 buildLog.chainAddHead(first_match.index, pt);
15294 chain.unshift(pt);
15295 }
15296 else{
15297 if (buildLog)
15298 buildLog.chainAddTail(first_match.index, pt);
15299 chain.push(pt);
15300 }
15301 return;
15302 }
15303
15304 // otherwise, we matched two chains, so we need to combine those chains together
15305
15306 function reverseChain(index){
15307 if (buildLog)
15308 buildLog.chainReverse(index);
15309 chains[index].reverse(); // gee, that's easy
15310 }
15311
15312 function appendChain(index1, index2){
15313 // index1 gets index2 appended to it, and index2 is removed
15314 var chain1 = chains[index1];
15315 var chain2 = chains[index2];
15316 var tail = chain1[chain1.length - 1];
15317 var tail2 = chain1[chain1.length - 2];
15318 var head = chain2[0];
15319 var head2 = chain2[1];
15320
15321 if (eps.pointsCollinear(tail2, tail, head)){
15322 // tail isn't needed because it's directly between tail2 and head
15323 // tail2 ---tail---> head
15324 if (buildLog)
15325 buildLog.chainRemoveTail(index1, tail);
15326 chain1.pop();
15327 tail = tail2; // old tail is gone... new tail is what tail2 was
15328 }
15329
15330 if (eps.pointsCollinear(tail, head, head2)){
15331 // head isn't needed because it's directly between tail and head2
15332 // tail ---head---> head2
15333 if (buildLog)
15334 buildLog.chainRemoveHead(index2, head);
15335 chain2.shift();
15336 }
15337
15338 if (buildLog)
15339 buildLog.chainJoin(index1, index2);
15340 chains[index1] = chain1.concat(chain2);
15341 chains.splice(index2, 1);
15342 }
15343
15344 var F = first_match.index;
15345 var S = second_match.index;
15346
15347 if (buildLog)
15348 buildLog.chainConnect(F, S);
15349
15350 var reverseF = chains[F].length < chains[S].length; // reverse the shorter chain, if needed
15351 if (first_match.matches_head){
15352 if (second_match.matches_head){
15353 if (reverseF){
15354 // <<<< F <<<< --- >>>> S >>>>
15355 reverseChain(F);
15356 // >>>> F >>>> --- >>>> S >>>>
15357 appendChain(F, S);
15358 }
15359 else{
15360 // <<<< F <<<< --- >>>> S >>>>
15361 reverseChain(S);
15362 // <<<< F <<<< --- <<<< S <<<< logically same as:
15363 // >>>> S >>>> --- >>>> F >>>>
15364 appendChain(S, F);
15365 }
15366 }
15367 else{
15368 // <<<< F <<<< --- <<<< S <<<< logically same as:
15369 // >>>> S >>>> --- >>>> F >>>>
15370 appendChain(S, F);
15371 }
15372 }
15373 else{
15374 if (second_match.matches_head){
15375 // >>>> F >>>> --- >>>> S >>>>
15376 appendChain(F, S);
15377 }
15378 else{
15379 if (reverseF){
15380 // >>>> F >>>> --- <<<< S <<<<
15381 reverseChain(F);
15382 // <<<< F <<<< --- <<<< S <<<< logically same as:
15383 // >>>> S >>>> --- >>>> F >>>>
15384 appendChain(S, F);
15385 }
15386 else{
15387 // >>>> F >>>> --- <<<< S <<<<
15388 reverseChain(S);
15389 // >>>> F >>>> --- >>>> S >>>>
15390 appendChain(F, S);
15391 }
15392 }
15393 }
15394 });
15395
15396 return regions;
15397}
15398
15399module.exports = SegmentChainer;
15400
15401},{}],30:[function(_dereq_,module,exports){
15402// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15403// MIT License
15404// Project Home: https://github.com/voidqk/polybooljs
15405
15406//
15407// filter a list of segments based on boolean operations
15408//
15409
15410function select(segments, selection, buildLog){
15411 var result = [];
15412 segments.forEach(function(seg){
15413 var index =
15414 (seg.myFill.above ? 8 : 0) +
15415 (seg.myFill.below ? 4 : 0) +
15416 ((seg.otherFill && seg.otherFill.above) ? 2 : 0) +
15417 ((seg.otherFill && seg.otherFill.below) ? 1 : 0);
15418 if (selection[index] !== 0){
15419 // copy the segment to the results, while also calculating the fill status
15420 result.push({
15421 id: buildLog ? buildLog.segmentId() : -1,
15422 start: seg.start,
15423 end: seg.end,
15424 myFill: {
15425 above: selection[index] === 1, // 1 if filled above
15426 below: selection[index] === 2 // 2 if filled below
15427 },
15428 otherFill: null
15429 });
15430 }
15431 });
15432
15433 if (buildLog)
15434 buildLog.selected(result);
15435
15436 return result;
15437}
15438
15439var SegmentSelector = {
15440 union: function(segments, buildLog){ // primary | secondary
15441 // above1 below1 above2 below2 Keep? Value
15442 // 0 0 0 0 => no 0
15443 // 0 0 0 1 => yes filled below 2
15444 // 0 0 1 0 => yes filled above 1
15445 // 0 0 1 1 => no 0
15446 // 0 1 0 0 => yes filled below 2
15447 // 0 1 0 1 => yes filled below 2
15448 // 0 1 1 0 => no 0
15449 // 0 1 1 1 => no 0
15450 // 1 0 0 0 => yes filled above 1
15451 // 1 0 0 1 => no 0
15452 // 1 0 1 0 => yes filled above 1
15453 // 1 0 1 1 => no 0
15454 // 1 1 0 0 => no 0
15455 // 1 1 0 1 => no 0
15456 // 1 1 1 0 => no 0
15457 // 1 1 1 1 => no 0
15458 return select(segments, [
15459 0, 2, 1, 0,
15460 2, 2, 0, 0,
15461 1, 0, 1, 0,
15462 0, 0, 0, 0
15463 ], buildLog);
15464 },
15465 intersect: function(segments, buildLog){ // primary & secondary
15466 // above1 below1 above2 below2 Keep? Value
15467 // 0 0 0 0 => no 0
15468 // 0 0 0 1 => no 0
15469 // 0 0 1 0 => no 0
15470 // 0 0 1 1 => no 0
15471 // 0 1 0 0 => no 0
15472 // 0 1 0 1 => yes filled below 2
15473 // 0 1 1 0 => no 0
15474 // 0 1 1 1 => yes filled below 2
15475 // 1 0 0 0 => no 0
15476 // 1 0 0 1 => no 0
15477 // 1 0 1 0 => yes filled above 1
15478 // 1 0 1 1 => yes filled above 1
15479 // 1 1 0 0 => no 0
15480 // 1 1 0 1 => yes filled below 2
15481 // 1 1 1 0 => yes filled above 1
15482 // 1 1 1 1 => no 0
15483 return select(segments, [
15484 0, 0, 0, 0,
15485 0, 2, 0, 2,
15486 0, 0, 1, 1,
15487 0, 2, 1, 0
15488 ], buildLog);
15489 },
15490 difference: function(segments, buildLog){ // primary - secondary
15491 // above1 below1 above2 below2 Keep? Value
15492 // 0 0 0 0 => no 0
15493 // 0 0 0 1 => no 0
15494 // 0 0 1 0 => no 0
15495 // 0 0 1 1 => no 0
15496 // 0 1 0 0 => yes filled below 2
15497 // 0 1 0 1 => no 0
15498 // 0 1 1 0 => yes filled below 2
15499 // 0 1 1 1 => no 0
15500 // 1 0 0 0 => yes filled above 1
15501 // 1 0 0 1 => yes filled above 1
15502 // 1 0 1 0 => no 0
15503 // 1 0 1 1 => no 0
15504 // 1 1 0 0 => no 0
15505 // 1 1 0 1 => yes filled above 1
15506 // 1 1 1 0 => yes filled below 2
15507 // 1 1 1 1 => no 0
15508 return select(segments, [
15509 0, 0, 0, 0,
15510 2, 0, 2, 0,
15511 1, 1, 0, 0,
15512 0, 1, 2, 0
15513 ], buildLog);
15514 },
15515 differenceRev: function(segments, buildLog){ // secondary - primary
15516 // above1 below1 above2 below2 Keep? Value
15517 // 0 0 0 0 => no 0
15518 // 0 0 0 1 => yes filled below 2
15519 // 0 0 1 0 => yes filled above 1
15520 // 0 0 1 1 => no 0
15521 // 0 1 0 0 => no 0
15522 // 0 1 0 1 => no 0
15523 // 0 1 1 0 => yes filled above 1
15524 // 0 1 1 1 => yes filled above 1
15525 // 1 0 0 0 => no 0
15526 // 1 0 0 1 => yes filled below 2
15527 // 1 0 1 0 => no 0
15528 // 1 0 1 1 => yes filled below 2
15529 // 1 1 0 0 => no 0
15530 // 1 1 0 1 => no 0
15531 // 1 1 1 0 => no 0
15532 // 1 1 1 1 => no 0
15533 return select(segments, [
15534 0, 2, 1, 0,
15535 0, 0, 1, 1,
15536 0, 2, 0, 2,
15537 0, 0, 0, 0
15538 ], buildLog);
15539 },
15540 xor: function(segments, buildLog){ // primary ^ secondary
15541 // above1 below1 above2 below2 Keep? Value
15542 // 0 0 0 0 => no 0
15543 // 0 0 0 1 => yes filled below 2
15544 // 0 0 1 0 => yes filled above 1
15545 // 0 0 1 1 => no 0
15546 // 0 1 0 0 => yes filled below 2
15547 // 0 1 0 1 => no 0
15548 // 0 1 1 0 => no 0
15549 // 0 1 1 1 => yes filled above 1
15550 // 1 0 0 0 => yes filled above 1
15551 // 1 0 0 1 => no 0
15552 // 1 0 1 0 => no 0
15553 // 1 0 1 1 => yes filled below 2
15554 // 1 1 0 0 => no 0
15555 // 1 1 0 1 => yes filled above 1
15556 // 1 1 1 0 => yes filled below 2
15557 // 1 1 1 1 => no 0
15558 return select(segments, [
15559 0, 2, 1, 0,
15560 2, 0, 0, 1,
15561 1, 0, 0, 2,
15562 0, 1, 2, 0
15563 ], buildLog);
15564 }
15565};
15566
15567module.exports = SegmentSelector;
15568
15569},{}],31:[function(_dereq_,module,exports){
15570// shim for using process in browser
15571var process = module.exports = {};
15572
15573// cached from whatever global is present so that test runners that stub it
15574// don't break things. But we need to wrap it in a try catch in case it is
15575// wrapped in strict mode code which doesn't define any globals. It's inside a
15576// function because try/catches deoptimize in certain engines.
15577
15578var cachedSetTimeout;
15579var cachedClearTimeout;
15580
15581function defaultSetTimout() {
15582 throw new Error('setTimeout has not been defined');
15583}
15584function defaultClearTimeout () {
15585 throw new Error('clearTimeout has not been defined');
15586}
15587(function () {
15588 try {
15589 if (typeof setTimeout === 'function') {
15590 cachedSetTimeout = setTimeout;
15591 } else {
15592 cachedSetTimeout = defaultSetTimout;
15593 }
15594 } catch (e) {
15595 cachedSetTimeout = defaultSetTimout;
15596 }
15597 try {
15598 if (typeof clearTimeout === 'function') {
15599 cachedClearTimeout = clearTimeout;
15600 } else {
15601 cachedClearTimeout = defaultClearTimeout;
15602 }
15603 } catch (e) {
15604 cachedClearTimeout = defaultClearTimeout;
15605 }
15606} ())
15607function runTimeout(fun) {
15608 if (cachedSetTimeout === setTimeout) {
15609 //normal enviroments in sane situations
15610 return setTimeout(fun, 0);
15611 }
15612 // if setTimeout wasn't available but was latter defined
15613 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
15614 cachedSetTimeout = setTimeout;
15615 return setTimeout(fun, 0);
15616 }
15617 try {
15618 // when when somebody has screwed with setTimeout but no I.E. maddness
15619 return cachedSetTimeout(fun, 0);
15620 } catch(e){
15621 try {
15622 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
15623 return cachedSetTimeout.call(null, fun, 0);
15624 } catch(e){
15625 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
15626 return cachedSetTimeout.call(this, fun, 0);
15627 }
15628 }
15629
15630
15631}
15632function runClearTimeout(marker) {
15633 if (cachedClearTimeout === clearTimeout) {
15634 //normal enviroments in sane situations
15635 return clearTimeout(marker);
15636 }
15637 // if clearTimeout wasn't available but was latter defined
15638 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
15639 cachedClearTimeout = clearTimeout;
15640 return clearTimeout(marker);
15641 }
15642 try {
15643 // when when somebody has screwed with setTimeout but no I.E. maddness
15644 return cachedClearTimeout(marker);
15645 } catch (e){
15646 try {
15647 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
15648 return cachedClearTimeout.call(null, marker);
15649 } catch (e){
15650 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
15651 // Some versions of I.E. have different rules for clearTimeout vs setTimeout
15652 return cachedClearTimeout.call(this, marker);
15653 }
15654 }
15655
15656
15657
15658}
15659var queue = [];
15660var draining = false;
15661var currentQueue;
15662var queueIndex = -1;
15663
15664function cleanUpNextTick() {
15665 if (!draining || !currentQueue) {
15666 return;
15667 }
15668 draining = false;
15669 if (currentQueue.length) {
15670 queue = currentQueue.concat(queue);
15671 } else {
15672 queueIndex = -1;
15673 }
15674 if (queue.length) {
15675 drainQueue();
15676 }
15677}
15678
15679function drainQueue() {
15680 if (draining) {
15681 return;
15682 }
15683 var timeout = runTimeout(cleanUpNextTick);
15684 draining = true;
15685
15686 var len = queue.length;
15687 while(len) {
15688 currentQueue = queue;
15689 queue = [];
15690 while (++queueIndex < len) {
15691 if (currentQueue) {
15692 currentQueue[queueIndex].run();
15693 }
15694 }
15695 queueIndex = -1;
15696 len = queue.length;
15697 }
15698 currentQueue = null;
15699 draining = false;
15700 runClearTimeout(timeout);
15701}
15702
15703process.nextTick = function (fun) {
15704 var args = new Array(arguments.length - 1);
15705 if (arguments.length > 1) {
15706 for (var i = 1; i < arguments.length; i++) {
15707 args[i - 1] = arguments[i];
15708 }
15709 }
15710 queue.push(new Item(fun, args));
15711 if (queue.length === 1 && !draining) {
15712 runTimeout(drainQueue);
15713 }
15714};
15715
15716// v8 likes predictible objects
15717function Item(fun, array) {
15718 this.fun = fun;
15719 this.array = array;
15720}
15721Item.prototype.run = function () {
15722 this.fun.apply(null, this.array);
15723};
15724process.title = 'browser';
15725process.browser = true;
15726process.env = {};
15727process.argv = [];
15728process.version = ''; // empty string to avoid regexp issues
15729process.versions = {};
15730
15731function noop() {}
15732
15733process.on = noop;
15734process.addListener = noop;
15735process.once = noop;
15736process.off = noop;
15737process.removeListener = noop;
15738process.removeAllListeners = noop;
15739process.emit = noop;
15740process.prependListener = noop;
15741process.prependOnceListener = noop;
15742
15743process.listeners = function (name) { return [] }
15744
15745process.binding = function (name) {
15746 throw new Error('process.binding is not supported');
15747};
15748
15749process.cwd = function () { return '/' };
15750process.chdir = function (dir) {
15751 throw new Error('process.chdir is not supported');
15752};
15753process.umask = function() { return 0; };
15754
15755},{}],32:[function(_dereq_,module,exports){
15756// TinyColor v1.4.1
15757// https://github.com/bgrins/TinyColor
15758// Brian Grinstead, MIT License
15759
15760(function(Math) {
15761
15762var trimLeft = /^\s+/,
15763 trimRight = /\s+$/,
15764 tinyCounter = 0,
15765 mathRound = Math.round,
15766 mathMin = Math.min,
15767 mathMax = Math.max,
15768 mathRandom = Math.random;
15769
15770function tinycolor (color, opts) {
15771
15772 color = (color) ? color : '';
15773 opts = opts || { };
15774
15775 // If input is already a tinycolor, return itself
15776 if (color instanceof tinycolor) {
15777 return color;
15778 }
15779 // If we are called as a function, call using new instead
15780 if (!(this instanceof tinycolor)) {
15781 return new tinycolor(color, opts);
15782 }
15783
15784 var rgb = inputToRGB(color);
15785 this._originalInput = color,
15786 this._r = rgb.r,
15787 this._g = rgb.g,
15788 this._b = rgb.b,
15789 this._a = rgb.a,
15790 this._roundA = mathRound(100*this._a) / 100,
15791 this._format = opts.format || rgb.format;
15792 this._gradientType = opts.gradientType;
15793
15794 // Don't let the range of [0,255] come back in [0,1].
15795 // Potentially lose a little bit of precision here, but will fix issues where
15796 // .5 gets interpreted as half of the total, instead of half of 1
15797 // If it was supposed to be 128, this was already taken care of by `inputToRgb`
15798 if (this._r < 1) { this._r = mathRound(this._r); }
15799 if (this._g < 1) { this._g = mathRound(this._g); }
15800 if (this._b < 1) { this._b = mathRound(this._b); }
15801
15802 this._ok = rgb.ok;
15803 this._tc_id = tinyCounter++;
15804}
15805
15806tinycolor.prototype = {
15807 isDark: function() {
15808 return this.getBrightness() < 128;
15809 },
15810 isLight: function() {
15811 return !this.isDark();
15812 },
15813 isValid: function() {
15814 return this._ok;
15815 },
15816 getOriginalInput: function() {
15817 return this._originalInput;
15818 },
15819 getFormat: function() {
15820 return this._format;
15821 },
15822 getAlpha: function() {
15823 return this._a;
15824 },
15825 getBrightness: function() {
15826 //http://www.w3.org/TR/AERT#color-contrast
15827 var rgb = this.toRgb();
15828 return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
15829 },
15830 getLuminance: function() {
15831 //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
15832 var rgb = this.toRgb();
15833 var RsRGB, GsRGB, BsRGB, R, G, B;
15834 RsRGB = rgb.r/255;
15835 GsRGB = rgb.g/255;
15836 BsRGB = rgb.b/255;
15837
15838 if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
15839 if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
15840 if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
15841 return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
15842 },
15843 setAlpha: function(value) {
15844 this._a = boundAlpha(value);
15845 this._roundA = mathRound(100*this._a) / 100;
15846 return this;
15847 },
15848 toHsv: function() {
15849 var hsv = rgbToHsv(this._r, this._g, this._b);
15850 return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
15851 },
15852 toHsvString: function() {
15853 var hsv = rgbToHsv(this._r, this._g, this._b);
15854 var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
15855 return (this._a == 1) ?
15856 "hsv(" + h + ", " + s + "%, " + v + "%)" :
15857 "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
15858 },
15859 toHsl: function() {
15860 var hsl = rgbToHsl(this._r, this._g, this._b);
15861 return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
15862 },
15863 toHslString: function() {
15864 var hsl = rgbToHsl(this._r, this._g, this._b);
15865 var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
15866 return (this._a == 1) ?
15867 "hsl(" + h + ", " + s + "%, " + l + "%)" :
15868 "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
15869 },
15870 toHex: function(allow3Char) {
15871 return rgbToHex(this._r, this._g, this._b, allow3Char);
15872 },
15873 toHexString: function(allow3Char) {
15874 return '#' + this.toHex(allow3Char);
15875 },
15876 toHex8: function(allow4Char) {
15877 return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
15878 },
15879 toHex8String: function(allow4Char) {
15880 return '#' + this.toHex8(allow4Char);
15881 },
15882 toRgb: function() {
15883 return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
15884 },
15885 toRgbString: function() {
15886 return (this._a == 1) ?
15887 "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
15888 "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
15889 },
15890 toPercentageRgb: function() {
15891 return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
15892 },
15893 toPercentageRgbString: function() {
15894 return (this._a == 1) ?
15895 "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
15896 "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
15897 },
15898 toName: function() {
15899 if (this._a === 0) {
15900 return "transparent";
15901 }
15902
15903 if (this._a < 1) {
15904 return false;
15905 }
15906
15907 return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
15908 },
15909 toFilter: function(secondColor) {
15910 var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
15911 var secondHex8String = hex8String;
15912 var gradientType = this._gradientType ? "GradientType = 1, " : "";
15913
15914 if (secondColor) {
15915 var s = tinycolor(secondColor);
15916 secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
15917 }
15918
15919 return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
15920 },
15921 toString: function(format) {
15922 var formatSet = !!format;
15923 format = format || this._format;
15924
15925 var formattedString = false;
15926 var hasAlpha = this._a < 1 && this._a >= 0;
15927 var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
15928
15929 if (needsAlphaFormat) {
15930 // Special case for "transparent", all other non-alpha formats
15931 // will return rgba when there is transparency.
15932 if (format === "name" && this._a === 0) {
15933 return this.toName();
15934 }
15935 return this.toRgbString();
15936 }
15937 if (format === "rgb") {
15938 formattedString = this.toRgbString();
15939 }
15940 if (format === "prgb") {
15941 formattedString = this.toPercentageRgbString();
15942 }
15943 if (format === "hex" || format === "hex6") {
15944 formattedString = this.toHexString();
15945 }
15946 if (format === "hex3") {
15947 formattedString = this.toHexString(true);
15948 }
15949 if (format === "hex4") {
15950 formattedString = this.toHex8String(true);
15951 }
15952 if (format === "hex8") {
15953 formattedString = this.toHex8String();
15954 }
15955 if (format === "name") {
15956 formattedString = this.toName();
15957 }
15958 if (format === "hsl") {
15959 formattedString = this.toHslString();
15960 }
15961 if (format === "hsv") {
15962 formattedString = this.toHsvString();
15963 }
15964
15965 return formattedString || this.toHexString();
15966 },
15967 clone: function() {
15968 return tinycolor(this.toString());
15969 },
15970
15971 _applyModification: function(fn, args) {
15972 var color = fn.apply(null, [this].concat([].slice.call(args)));
15973 this._r = color._r;
15974 this._g = color._g;
15975 this._b = color._b;
15976 this.setAlpha(color._a);
15977 return this;
15978 },
15979 lighten: function() {
15980 return this._applyModification(lighten, arguments);
15981 },
15982 brighten: function() {
15983 return this._applyModification(brighten, arguments);
15984 },
15985 darken: function() {
15986 return this._applyModification(darken, arguments);
15987 },
15988 desaturate: function() {
15989 return this._applyModification(desaturate, arguments);
15990 },
15991 saturate: function() {
15992 return this._applyModification(saturate, arguments);
15993 },
15994 greyscale: function() {
15995 return this._applyModification(greyscale, arguments);
15996 },
15997 spin: function() {
15998 return this._applyModification(spin, arguments);
15999 },
16000
16001 _applyCombination: function(fn, args) {
16002 return fn.apply(null, [this].concat([].slice.call(args)));
16003 },
16004 analogous: function() {
16005 return this._applyCombination(analogous, arguments);
16006 },
16007 complement: function() {
16008 return this._applyCombination(complement, arguments);
16009 },
16010 monochromatic: function() {
16011 return this._applyCombination(monochromatic, arguments);
16012 },
16013 splitcomplement: function() {
16014 return this._applyCombination(splitcomplement, arguments);
16015 },
16016 triad: function() {
16017 return this._applyCombination(triad, arguments);
16018 },
16019 tetrad: function() {
16020 return this._applyCombination(tetrad, arguments);
16021 }
16022};
16023
16024// If input is an object, force 1 into "1.0" to handle ratios properly
16025// String input requires "1.0" as input, so 1 will be treated as 1
16026tinycolor.fromRatio = function(color, opts) {
16027 if (typeof color == "object") {
16028 var newColor = {};
16029 for (var i in color) {
16030 if (color.hasOwnProperty(i)) {
16031 if (i === "a") {
16032 newColor[i] = color[i];
16033 }
16034 else {
16035 newColor[i] = convertToPercentage(color[i]);
16036 }
16037 }
16038 }
16039 color = newColor;
16040 }
16041
16042 return tinycolor(color, opts);
16043};
16044
16045// Given a string or object, convert that input to RGB
16046// Possible string inputs:
16047//
16048// "red"
16049// "#f00" or "f00"
16050// "#ff0000" or "ff0000"
16051// "#ff000000" or "ff000000"
16052// "rgb 255 0 0" or "rgb (255, 0, 0)"
16053// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
16054// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
16055// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
16056// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
16057// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
16058// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
16059//
16060function inputToRGB(color) {
16061
16062 var rgb = { r: 0, g: 0, b: 0 };
16063 var a = 1;
16064 var s = null;
16065 var v = null;
16066 var l = null;
16067 var ok = false;
16068 var format = false;
16069
16070 if (typeof color == "string") {
16071 color = stringInputToObject(color);
16072 }
16073
16074 if (typeof color == "object") {
16075 if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
16076 rgb = rgbToRgb(color.r, color.g, color.b);
16077 ok = true;
16078 format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
16079 }
16080 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
16081 s = convertToPercentage(color.s);
16082 v = convertToPercentage(color.v);
16083 rgb = hsvToRgb(color.h, s, v);
16084 ok = true;
16085 format = "hsv";
16086 }
16087 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
16088 s = convertToPercentage(color.s);
16089 l = convertToPercentage(color.l);
16090 rgb = hslToRgb(color.h, s, l);
16091 ok = true;
16092 format = "hsl";
16093 }
16094
16095 if (color.hasOwnProperty("a")) {
16096 a = color.a;
16097 }
16098 }
16099
16100 a = boundAlpha(a);
16101
16102 return {
16103 ok: ok,
16104 format: color.format || format,
16105 r: mathMin(255, mathMax(rgb.r, 0)),
16106 g: mathMin(255, mathMax(rgb.g, 0)),
16107 b: mathMin(255, mathMax(rgb.b, 0)),
16108 a: a
16109 };
16110}
16111
16112
16113// Conversion Functions
16114// --------------------
16115
16116// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
16117// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
16118
16119// `rgbToRgb`
16120// Handle bounds / percentage checking to conform to CSS color spec
16121// <http://www.w3.org/TR/css3-color/>
16122// *Assumes:* r, g, b in [0, 255] or [0, 1]
16123// *Returns:* { r, g, b } in [0, 255]
16124function rgbToRgb(r, g, b){
16125 return {
16126 r: bound01(r, 255) * 255,
16127 g: bound01(g, 255) * 255,
16128 b: bound01(b, 255) * 255
16129 };
16130}
16131
16132// `rgbToHsl`
16133// Converts an RGB color value to HSL.
16134// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
16135// *Returns:* { h, s, l } in [0,1]
16136function rgbToHsl(r, g, b) {
16137
16138 r = bound01(r, 255);
16139 g = bound01(g, 255);
16140 b = bound01(b, 255);
16141
16142 var max = mathMax(r, g, b), min = mathMin(r, g, b);
16143 var h, s, l = (max + min) / 2;
16144
16145 if(max == min) {
16146 h = s = 0; // achromatic
16147 }
16148 else {
16149 var d = max - min;
16150 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
16151 switch(max) {
16152 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
16153 case g: h = (b - r) / d + 2; break;
16154 case b: h = (r - g) / d + 4; break;
16155 }
16156
16157 h /= 6;
16158 }
16159
16160 return { h: h, s: s, l: l };
16161}
16162
16163// `hslToRgb`
16164// Converts an HSL color value to RGB.
16165// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
16166// *Returns:* { r, g, b } in the set [0, 255]
16167function hslToRgb(h, s, l) {
16168 var r, g, b;
16169
16170 h = bound01(h, 360);
16171 s = bound01(s, 100);
16172 l = bound01(l, 100);
16173
16174 function hue2rgb(p, q, t) {
16175 if(t < 0) t += 1;
16176 if(t > 1) t -= 1;
16177 if(t < 1/6) return p + (q - p) * 6 * t;
16178 if(t < 1/2) return q;
16179 if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
16180 return p;
16181 }
16182
16183 if(s === 0) {
16184 r = g = b = l; // achromatic
16185 }
16186 else {
16187 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
16188 var p = 2 * l - q;
16189 r = hue2rgb(p, q, h + 1/3);
16190 g = hue2rgb(p, q, h);
16191 b = hue2rgb(p, q, h - 1/3);
16192 }
16193
16194 return { r: r * 255, g: g * 255, b: b * 255 };
16195}
16196
16197// `rgbToHsv`
16198// Converts an RGB color value to HSV
16199// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
16200// *Returns:* { h, s, v } in [0,1]
16201function rgbToHsv(r, g, b) {
16202
16203 r = bound01(r, 255);
16204 g = bound01(g, 255);
16205 b = bound01(b, 255);
16206
16207 var max = mathMax(r, g, b), min = mathMin(r, g, b);
16208 var h, s, v = max;
16209
16210 var d = max - min;
16211 s = max === 0 ? 0 : d / max;
16212
16213 if(max == min) {
16214 h = 0; // achromatic
16215 }
16216 else {
16217 switch(max) {
16218 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
16219 case g: h = (b - r) / d + 2; break;
16220 case b: h = (r - g) / d + 4; break;
16221 }
16222 h /= 6;
16223 }
16224 return { h: h, s: s, v: v };
16225}
16226
16227// `hsvToRgb`
16228// Converts an HSV color value to RGB.
16229// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
16230// *Returns:* { r, g, b } in the set [0, 255]
16231 function hsvToRgb(h, s, v) {
16232
16233 h = bound01(h, 360) * 6;
16234 s = bound01(s, 100);
16235 v = bound01(v, 100);
16236
16237 var i = Math.floor(h),
16238 f = h - i,
16239 p = v * (1 - s),
16240 q = v * (1 - f * s),
16241 t = v * (1 - (1 - f) * s),
16242 mod = i % 6,
16243 r = [v, q, p, p, t, v][mod],
16244 g = [t, v, v, q, p, p][mod],
16245 b = [p, p, t, v, v, q][mod];
16246
16247 return { r: r * 255, g: g * 255, b: b * 255 };
16248}
16249
16250// `rgbToHex`
16251// Converts an RGB color to hex
16252// Assumes r, g, and b are contained in the set [0, 255]
16253// Returns a 3 or 6 character hex
16254function rgbToHex(r, g, b, allow3Char) {
16255
16256 var hex = [
16257 pad2(mathRound(r).toString(16)),
16258 pad2(mathRound(g).toString(16)),
16259 pad2(mathRound(b).toString(16))
16260 ];
16261
16262 // Return a 3 character hex if possible
16263 if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
16264 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
16265 }
16266
16267 return hex.join("");
16268}
16269
16270// `rgbaToHex`
16271// Converts an RGBA color plus alpha transparency to hex
16272// Assumes r, g, b are contained in the set [0, 255] and
16273// a in [0, 1]. Returns a 4 or 8 character rgba hex
16274function rgbaToHex(r, g, b, a, allow4Char) {
16275
16276 var hex = [
16277 pad2(mathRound(r).toString(16)),
16278 pad2(mathRound(g).toString(16)),
16279 pad2(mathRound(b).toString(16)),
16280 pad2(convertDecimalToHex(a))
16281 ];
16282
16283 // Return a 4 character hex if possible
16284 if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {
16285 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
16286 }
16287
16288 return hex.join("");
16289}
16290
16291// `rgbaToArgbHex`
16292// Converts an RGBA color to an ARGB Hex8 string
16293// Rarely used, but required for "toFilter()"
16294function rgbaToArgbHex(r, g, b, a) {
16295
16296 var hex = [
16297 pad2(convertDecimalToHex(a)),
16298 pad2(mathRound(r).toString(16)),
16299 pad2(mathRound(g).toString(16)),
16300 pad2(mathRound(b).toString(16))
16301 ];
16302
16303 return hex.join("");
16304}
16305
16306// `equals`
16307// Can be called with any tinycolor input
16308tinycolor.equals = function (color1, color2) {
16309 if (!color1 || !color2) { return false; }
16310 return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
16311};
16312
16313tinycolor.random = function() {
16314 return tinycolor.fromRatio({
16315 r: mathRandom(),
16316 g: mathRandom(),
16317 b: mathRandom()
16318 });
16319};
16320
16321
16322// Modification Functions
16323// ----------------------
16324// Thanks to less.js for some of the basics here
16325// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
16326
16327function desaturate(color, amount) {
16328 amount = (amount === 0) ? 0 : (amount || 10);
16329 var hsl = tinycolor(color).toHsl();
16330 hsl.s -= amount / 100;
16331 hsl.s = clamp01(hsl.s);
16332 return tinycolor(hsl);
16333}
16334
16335function saturate(color, amount) {
16336 amount = (amount === 0) ? 0 : (amount || 10);
16337 var hsl = tinycolor(color).toHsl();
16338 hsl.s += amount / 100;
16339 hsl.s = clamp01(hsl.s);
16340 return tinycolor(hsl);
16341}
16342
16343function greyscale(color) {
16344 return tinycolor(color).desaturate(100);
16345}
16346
16347function lighten (color, amount) {
16348 amount = (amount === 0) ? 0 : (amount || 10);
16349 var hsl = tinycolor(color).toHsl();
16350 hsl.l += amount / 100;
16351 hsl.l = clamp01(hsl.l);
16352 return tinycolor(hsl);
16353}
16354
16355function brighten(color, amount) {
16356 amount = (amount === 0) ? 0 : (amount || 10);
16357 var rgb = tinycolor(color).toRgb();
16358 rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
16359 rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
16360 rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
16361 return tinycolor(rgb);
16362}
16363
16364function darken (color, amount) {
16365 amount = (amount === 0) ? 0 : (amount || 10);
16366 var hsl = tinycolor(color).toHsl();
16367 hsl.l -= amount / 100;
16368 hsl.l = clamp01(hsl.l);
16369 return tinycolor(hsl);
16370}
16371
16372// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
16373// Values outside of this range will be wrapped into this range.
16374function spin(color, amount) {
16375 var hsl = tinycolor(color).toHsl();
16376 var hue = (hsl.h + amount) % 360;
16377 hsl.h = hue < 0 ? 360 + hue : hue;
16378 return tinycolor(hsl);
16379}
16380
16381// Combination Functions
16382// ---------------------
16383// Thanks to jQuery xColor for some of the ideas behind these
16384// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
16385
16386function complement(color) {
16387 var hsl = tinycolor(color).toHsl();
16388 hsl.h = (hsl.h + 180) % 360;
16389 return tinycolor(hsl);
16390}
16391
16392function triad(color) {
16393 var hsl = tinycolor(color).toHsl();
16394 var h = hsl.h;
16395 return [
16396 tinycolor(color),
16397 tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
16398 tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
16399 ];
16400}
16401
16402function tetrad(color) {
16403 var hsl = tinycolor(color).toHsl();
16404 var h = hsl.h;
16405 return [
16406 tinycolor(color),
16407 tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
16408 tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
16409 tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
16410 ];
16411}
16412
16413function splitcomplement(color) {
16414 var hsl = tinycolor(color).toHsl();
16415 var h = hsl.h;
16416 return [
16417 tinycolor(color),
16418 tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
16419 tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
16420 ];
16421}
16422
16423function analogous(color, results, slices) {
16424 results = results || 6;
16425 slices = slices || 30;
16426
16427 var hsl = tinycolor(color).toHsl();
16428 var part = 360 / slices;
16429 var ret = [tinycolor(color)];
16430
16431 for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
16432 hsl.h = (hsl.h + part) % 360;
16433 ret.push(tinycolor(hsl));
16434 }
16435 return ret;
16436}
16437
16438function monochromatic(color, results) {
16439 results = results || 6;
16440 var hsv = tinycolor(color).toHsv();
16441 var h = hsv.h, s = hsv.s, v = hsv.v;
16442 var ret = [];
16443 var modification = 1 / results;
16444
16445 while (results--) {
16446 ret.push(tinycolor({ h: h, s: s, v: v}));
16447 v = (v + modification) % 1;
16448 }
16449
16450 return ret;
16451}
16452
16453// Utility Functions
16454// ---------------------
16455
16456tinycolor.mix = function(color1, color2, amount) {
16457 amount = (amount === 0) ? 0 : (amount || 50);
16458
16459 var rgb1 = tinycolor(color1).toRgb();
16460 var rgb2 = tinycolor(color2).toRgb();
16461
16462 var p = amount / 100;
16463
16464 var rgba = {
16465 r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
16466 g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
16467 b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
16468 a: ((rgb2.a - rgb1.a) * p) + rgb1.a
16469 };
16470
16471 return tinycolor(rgba);
16472};
16473
16474
16475// Readability Functions
16476// ---------------------
16477// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
16478
16479// `contrast`
16480// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
16481tinycolor.readability = function(color1, color2) {
16482 var c1 = tinycolor(color1);
16483 var c2 = tinycolor(color2);
16484 return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
16485};
16486
16487// `isReadable`
16488// Ensure that foreground and background color combinations meet WCAG2 guidelines.
16489// The third argument is an optional Object.
16490// the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
16491// the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
16492// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
16493
16494// *Example*
16495// tinycolor.isReadable("#000", "#111") => false
16496// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
16497tinycolor.isReadable = function(color1, color2, wcag2) {
16498 var readability = tinycolor.readability(color1, color2);
16499 var wcag2Parms, out;
16500
16501 out = false;
16502
16503 wcag2Parms = validateWCAG2Parms(wcag2);
16504 switch (wcag2Parms.level + wcag2Parms.size) {
16505 case "AAsmall":
16506 case "AAAlarge":
16507 out = readability >= 4.5;
16508 break;
16509 case "AAlarge":
16510 out = readability >= 3;
16511 break;
16512 case "AAAsmall":
16513 out = readability >= 7;
16514 break;
16515 }
16516 return out;
16517
16518};
16519
16520// `mostReadable`
16521// Given a base color and a list of possible foreground or background
16522// colors for that base, returns the most readable color.
16523// Optionally returns Black or White if the most readable color is unreadable.
16524// *Example*
16525// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
16526// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
16527// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
16528// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
16529tinycolor.mostReadable = function(baseColor, colorList, args) {
16530 var bestColor = null;
16531 var bestScore = 0;
16532 var readability;
16533 var includeFallbackColors, level, size ;
16534 args = args || {};
16535 includeFallbackColors = args.includeFallbackColors ;
16536 level = args.level;
16537 size = args.size;
16538
16539 for (var i= 0; i < colorList.length ; i++) {
16540 readability = tinycolor.readability(baseColor, colorList[i]);
16541 if (readability > bestScore) {
16542 bestScore = readability;
16543 bestColor = tinycolor(colorList[i]);
16544 }
16545 }
16546
16547 if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
16548 return bestColor;
16549 }
16550 else {
16551 args.includeFallbackColors=false;
16552 return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
16553 }
16554};
16555
16556
16557// Big List of Colors
16558// ------------------
16559// <http://www.w3.org/TR/css3-color/#svg-color>
16560var names = tinycolor.names = {
16561 aliceblue: "f0f8ff",
16562 antiquewhite: "faebd7",
16563 aqua: "0ff",
16564 aquamarine: "7fffd4",
16565 azure: "f0ffff",
16566 beige: "f5f5dc",
16567 bisque: "ffe4c4",
16568 black: "000",
16569 blanchedalmond: "ffebcd",
16570 blue: "00f",
16571 blueviolet: "8a2be2",
16572 brown: "a52a2a",
16573 burlywood: "deb887",
16574 burntsienna: "ea7e5d",
16575 cadetblue: "5f9ea0",
16576 chartreuse: "7fff00",
16577 chocolate: "d2691e",
16578 coral: "ff7f50",
16579 cornflowerblue: "6495ed",
16580 cornsilk: "fff8dc",
16581 crimson: "dc143c",
16582 cyan: "0ff",
16583 darkblue: "00008b",
16584 darkcyan: "008b8b",
16585 darkgoldenrod: "b8860b",
16586 darkgray: "a9a9a9",
16587 darkgreen: "006400",
16588 darkgrey: "a9a9a9",
16589 darkkhaki: "bdb76b",
16590 darkmagenta: "8b008b",
16591 darkolivegreen: "556b2f",
16592 darkorange: "ff8c00",
16593 darkorchid: "9932cc",
16594 darkred: "8b0000",
16595 darksalmon: "e9967a",
16596 darkseagreen: "8fbc8f",
16597 darkslateblue: "483d8b",
16598 darkslategray: "2f4f4f",
16599 darkslategrey: "2f4f4f",
16600 darkturquoise: "00ced1",
16601 darkviolet: "9400d3",
16602 deeppink: "ff1493",
16603 deepskyblue: "00bfff",
16604 dimgray: "696969",
16605 dimgrey: "696969",
16606 dodgerblue: "1e90ff",
16607 firebrick: "b22222",
16608 floralwhite: "fffaf0",
16609 forestgreen: "228b22",
16610 fuchsia: "f0f",
16611 gainsboro: "dcdcdc",
16612 ghostwhite: "f8f8ff",
16613 gold: "ffd700",
16614 goldenrod: "daa520",
16615 gray: "808080",
16616 green: "008000",
16617 greenyellow: "adff2f",
16618 grey: "808080",
16619 honeydew: "f0fff0",
16620 hotpink: "ff69b4",
16621 indianred: "cd5c5c",
16622 indigo: "4b0082",
16623 ivory: "fffff0",
16624 khaki: "f0e68c",
16625 lavender: "e6e6fa",
16626 lavenderblush: "fff0f5",
16627 lawngreen: "7cfc00",
16628 lemonchiffon: "fffacd",
16629 lightblue: "add8e6",
16630 lightcoral: "f08080",
16631 lightcyan: "e0ffff",
16632 lightgoldenrodyellow: "fafad2",
16633 lightgray: "d3d3d3",
16634 lightgreen: "90ee90",
16635 lightgrey: "d3d3d3",
16636 lightpink: "ffb6c1",
16637 lightsalmon: "ffa07a",
16638 lightseagreen: "20b2aa",
16639 lightskyblue: "87cefa",
16640 lightslategray: "789",
16641 lightslategrey: "789",
16642 lightsteelblue: "b0c4de",
16643 lightyellow: "ffffe0",
16644 lime: "0f0",
16645 limegreen: "32cd32",
16646 linen: "faf0e6",
16647 magenta: "f0f",
16648 maroon: "800000",
16649 mediumaquamarine: "66cdaa",
16650 mediumblue: "0000cd",
16651 mediumorchid: "ba55d3",
16652 mediumpurple: "9370db",
16653 mediumseagreen: "3cb371",
16654 mediumslateblue: "7b68ee",
16655 mediumspringgreen: "00fa9a",
16656 mediumturquoise: "48d1cc",
16657 mediumvioletred: "c71585",
16658 midnightblue: "191970",
16659 mintcream: "f5fffa",
16660 mistyrose: "ffe4e1",
16661 moccasin: "ffe4b5",
16662 navajowhite: "ffdead",
16663 navy: "000080",
16664 oldlace: "fdf5e6",
16665 olive: "808000",
16666 olivedrab: "6b8e23",
16667 orange: "ffa500",
16668 orangered: "ff4500",
16669 orchid: "da70d6",
16670 palegoldenrod: "eee8aa",
16671 palegreen: "98fb98",
16672 paleturquoise: "afeeee",
16673 palevioletred: "db7093",
16674 papayawhip: "ffefd5",
16675 peachpuff: "ffdab9",
16676 peru: "cd853f",
16677 pink: "ffc0cb",
16678 plum: "dda0dd",
16679 powderblue: "b0e0e6",
16680 purple: "800080",
16681 rebeccapurple: "663399",
16682 red: "f00",
16683 rosybrown: "bc8f8f",
16684 royalblue: "4169e1",
16685 saddlebrown: "8b4513",
16686 salmon: "fa8072",
16687 sandybrown: "f4a460",
16688 seagreen: "2e8b57",
16689 seashell: "fff5ee",
16690 sienna: "a0522d",
16691 silver: "c0c0c0",
16692 skyblue: "87ceeb",
16693 slateblue: "6a5acd",
16694 slategray: "708090",
16695 slategrey: "708090",
16696 snow: "fffafa",
16697 springgreen: "00ff7f",
16698 steelblue: "4682b4",
16699 tan: "d2b48c",
16700 teal: "008080",
16701 thistle: "d8bfd8",
16702 tomato: "ff6347",
16703 turquoise: "40e0d0",
16704 violet: "ee82ee",
16705 wheat: "f5deb3",
16706 white: "fff",
16707 whitesmoke: "f5f5f5",
16708 yellow: "ff0",
16709 yellowgreen: "9acd32"
16710};
16711
16712// Make it easy to access colors via `hexNames[hex]`
16713var hexNames = tinycolor.hexNames = flip(names);
16714
16715
16716// Utilities
16717// ---------
16718
16719// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
16720function flip(o) {
16721 var flipped = { };
16722 for (var i in o) {
16723 if (o.hasOwnProperty(i)) {
16724 flipped[o[i]] = i;
16725 }
16726 }
16727 return flipped;
16728}
16729
16730// Return a valid alpha value [0,1] with all invalid values being set to 1
16731function boundAlpha(a) {
16732 a = parseFloat(a);
16733
16734 if (isNaN(a) || a < 0 || a > 1) {
16735 a = 1;
16736 }
16737
16738 return a;
16739}
16740
16741// Take input from [0, n] and return it as [0, 1]
16742function bound01(n, max) {
16743 if (isOnePointZero(n)) { n = "100%"; }
16744
16745 var processPercent = isPercentage(n);
16746 n = mathMin(max, mathMax(0, parseFloat(n)));
16747
16748 // Automatically convert percentage into number
16749 if (processPercent) {
16750 n = parseInt(n * max, 10) / 100;
16751 }
16752
16753 // Handle floating point rounding errors
16754 if ((Math.abs(n - max) < 0.000001)) {
16755 return 1;
16756 }
16757
16758 // Convert into [0, 1] range if it isn't already
16759 return (n % max) / parseFloat(max);
16760}
16761
16762// Force a number between 0 and 1
16763function clamp01(val) {
16764 return mathMin(1, mathMax(0, val));
16765}
16766
16767// Parse a base-16 hex value into a base-10 integer
16768function parseIntFromHex(val) {
16769 return parseInt(val, 16);
16770}
16771
16772// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
16773// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
16774function isOnePointZero(n) {
16775 return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
16776}
16777
16778// Check to see if string passed in is a percentage
16779function isPercentage(n) {
16780 return typeof n === "string" && n.indexOf('%') != -1;
16781}
16782
16783// Force a hex value to have 2 characters
16784function pad2(c) {
16785 return c.length == 1 ? '0' + c : '' + c;
16786}
16787
16788// Replace a decimal with it's percentage value
16789function convertToPercentage(n) {
16790 if (n <= 1) {
16791 n = (n * 100) + "%";
16792 }
16793
16794 return n;
16795}
16796
16797// Converts a decimal to a hex value
16798function convertDecimalToHex(d) {
16799 return Math.round(parseFloat(d) * 255).toString(16);
16800}
16801// Converts a hex value to a decimal
16802function convertHexToDecimal(h) {
16803 return (parseIntFromHex(h) / 255);
16804}
16805
16806var matchers = (function() {
16807
16808 // <http://www.w3.org/TR/css3-values/#integers>
16809 var CSS_INTEGER = "[-\\+]?\\d+%?";
16810
16811 // <http://www.w3.org/TR/css3-values/#number-value>
16812 var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
16813
16814 // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
16815 var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
16816
16817 // Actual matching.
16818 // Parentheses and commas are optional, but not required.
16819 // Whitespace can take the place of commas or opening paren
16820 var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
16821 var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
16822
16823 return {
16824 CSS_UNIT: new RegExp(CSS_UNIT),
16825 rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
16826 rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
16827 hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
16828 hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
16829 hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
16830 hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
16831 hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
16832 hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
16833 hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
16834 hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
16835 };
16836})();
16837
16838// `isValidCSSUnit`
16839// Take in a single string / number and check to see if it looks like a CSS unit
16840// (see `matchers` above for definition).
16841function isValidCSSUnit(color) {
16842 return !!matchers.CSS_UNIT.exec(color);
16843}
16844
16845// `stringInputToObject`
16846// Permissive string parsing. Take in a number of formats, and output an object
16847// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
16848function stringInputToObject(color) {
16849
16850 color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
16851 var named = false;
16852 if (names[color]) {
16853 color = names[color];
16854 named = true;
16855 }
16856 else if (color == 'transparent') {
16857 return { r: 0, g: 0, b: 0, a: 0, format: "name" };
16858 }
16859
16860 // Try to match string input using regular expressions.
16861 // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
16862 // Just return an object and let the conversion functions handle that.
16863 // This way the result will be the same whether the tinycolor is initialized with string or object.
16864 var match;
16865 if ((match = matchers.rgb.exec(color))) {
16866 return { r: match[1], g: match[2], b: match[3] };
16867 }
16868 if ((match = matchers.rgba.exec(color))) {
16869 return { r: match[1], g: match[2], b: match[3], a: match[4] };
16870 }
16871 if ((match = matchers.hsl.exec(color))) {
16872 return { h: match[1], s: match[2], l: match[3] };
16873 }
16874 if ((match = matchers.hsla.exec(color))) {
16875 return { h: match[1], s: match[2], l: match[3], a: match[4] };
16876 }
16877 if ((match = matchers.hsv.exec(color))) {
16878 return { h: match[1], s: match[2], v: match[3] };
16879 }
16880 if ((match = matchers.hsva.exec(color))) {
16881 return { h: match[1], s: match[2], v: match[3], a: match[4] };
16882 }
16883 if ((match = matchers.hex8.exec(color))) {
16884 return {
16885 r: parseIntFromHex(match[1]),
16886 g: parseIntFromHex(match[2]),
16887 b: parseIntFromHex(match[3]),
16888 a: convertHexToDecimal(match[4]),
16889 format: named ? "name" : "hex8"
16890 };
16891 }
16892 if ((match = matchers.hex6.exec(color))) {
16893 return {
16894 r: parseIntFromHex(match[1]),
16895 g: parseIntFromHex(match[2]),
16896 b: parseIntFromHex(match[3]),
16897 format: named ? "name" : "hex"
16898 };
16899 }
16900 if ((match = matchers.hex4.exec(color))) {
16901 return {
16902 r: parseIntFromHex(match[1] + '' + match[1]),
16903 g: parseIntFromHex(match[2] + '' + match[2]),
16904 b: parseIntFromHex(match[3] + '' + match[3]),
16905 a: convertHexToDecimal(match[4] + '' + match[4]),
16906 format: named ? "name" : "hex8"
16907 };
16908 }
16909 if ((match = matchers.hex3.exec(color))) {
16910 return {
16911 r: parseIntFromHex(match[1] + '' + match[1]),
16912 g: parseIntFromHex(match[2] + '' + match[2]),
16913 b: parseIntFromHex(match[3] + '' + match[3]),
16914 format: named ? "name" : "hex"
16915 };
16916 }
16917
16918 return false;
16919}
16920
16921function validateWCAG2Parms(parms) {
16922 // return valid WCAG2 parms for isReadable.
16923 // If input parms are invalid, return {"level":"AA", "size":"small"}
16924 var level, size;
16925 parms = parms || {"level":"AA", "size":"small"};
16926 level = (parms.level || "AA").toUpperCase();
16927 size = (parms.size || "small").toLowerCase();
16928 if (level !== "AA" && level !== "AAA") {
16929 level = "AA";
16930 }
16931 if (size !== "small" && size !== "large") {
16932 size = "small";
16933 }
16934 return {"level":level, "size":size};
16935}
16936
16937// Node: Export function
16938if (typeof module !== "undefined" && module.exports) {
16939 module.exports = tinycolor;
16940}
16941// AMD/requirejs: Define the module
16942else if (typeof define === 'function' && define.amd) {
16943 define(function () {return tinycolor;});
16944}
16945// Browser: Expose to window
16946else {
16947 window.tinycolor = tinycolor;
16948}
16949
16950})(Math);
16951
16952},{}],33:[function(_dereq_,module,exports){
16953// https://github.com/topojson/topojson-client v3.1.0 Copyright 2019 Mike Bostock
16954(function (global, factory) {
16955typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
16956typeof define === 'function' && define.amd ? define(['exports'], factory) :
16957(global = global || self, factory(global.topojson = global.topojson || {}));
16958}(this, function (exports) { 'use strict';
16959
16960function identity(x) {
16961 return x;
16962}
16963
16964function transform(transform) {
16965 if (transform == null) return identity;
16966 var x0,
16967 y0,
16968 kx = transform.scale[0],
16969 ky = transform.scale[1],
16970 dx = transform.translate[0],
16971 dy = transform.translate[1];
16972 return function(input, i) {
16973 if (!i) x0 = y0 = 0;
16974 var j = 2, n = input.length, output = new Array(n);
16975 output[0] = (x0 += input[0]) * kx + dx;
16976 output[1] = (y0 += input[1]) * ky + dy;
16977 while (j < n) output[j] = input[j], ++j;
16978 return output;
16979 };
16980}
16981
16982function bbox(topology) {
16983 var t = transform(topology.transform), key,
16984 x0 = Infinity, y0 = x0, x1 = -x0, y1 = -x0;
16985
16986 function bboxPoint(p) {
16987 p = t(p);
16988 if (p[0] < x0) x0 = p[0];
16989 if (p[0] > x1) x1 = p[0];
16990 if (p[1] < y0) y0 = p[1];
16991 if (p[1] > y1) y1 = p[1];
16992 }
16993
16994 function bboxGeometry(o) {
16995 switch (o.type) {
16996 case "GeometryCollection": o.geometries.forEach(bboxGeometry); break;
16997 case "Point": bboxPoint(o.coordinates); break;
16998 case "MultiPoint": o.coordinates.forEach(bboxPoint); break;
16999 }
17000 }
17001
17002 topology.arcs.forEach(function(arc) {
17003 var i = -1, n = arc.length, p;
17004 while (++i < n) {
17005 p = t(arc[i], i);
17006 if (p[0] < x0) x0 = p[0];
17007 if (p[0] > x1) x1 = p[0];
17008 if (p[1] < y0) y0 = p[1];
17009 if (p[1] > y1) y1 = p[1];
17010 }
17011 });
17012
17013 for (key in topology.objects) {
17014 bboxGeometry(topology.objects[key]);
17015 }
17016
17017 return [x0, y0, x1, y1];
17018}
17019
17020function reverse(array, n) {
17021 var t, j = array.length, i = j - n;
17022 while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
17023}
17024
17025function feature(topology, o) {
17026 if (typeof o === "string") o = topology.objects[o];
17027 return o.type === "GeometryCollection"
17028 ? {type: "FeatureCollection", features: o.geometries.map(function(o) { return feature$1(topology, o); })}
17029 : feature$1(topology, o);
17030}
17031
17032function feature$1(topology, o) {
17033 var id = o.id,
17034 bbox = o.bbox,
17035 properties = o.properties == null ? {} : o.properties,
17036 geometry = object(topology, o);
17037 return id == null && bbox == null ? {type: "Feature", properties: properties, geometry: geometry}
17038 : bbox == null ? {type: "Feature", id: id, properties: properties, geometry: geometry}
17039 : {type: "Feature", id: id, bbox: bbox, properties: properties, geometry: geometry};
17040}
17041
17042function object(topology, o) {
17043 var transformPoint = transform(topology.transform),
17044 arcs = topology.arcs;
17045
17046 function arc(i, points) {
17047 if (points.length) points.pop();
17048 for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) {
17049 points.push(transformPoint(a[k], k));
17050 }
17051 if (i < 0) reverse(points, n);
17052 }
17053
17054 function point(p) {
17055 return transformPoint(p);
17056 }
17057
17058 function line(arcs) {
17059 var points = [];
17060 for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
17061 if (points.length < 2) points.push(points[0]); // This should never happen per the specification.
17062 return points;
17063 }
17064
17065 function ring(arcs) {
17066 var points = line(arcs);
17067 while (points.length < 4) points.push(points[0]); // This may happen if an arc has only two points.
17068 return points;
17069 }
17070
17071 function polygon(arcs) {
17072 return arcs.map(ring);
17073 }
17074
17075 function geometry(o) {
17076 var type = o.type, coordinates;
17077 switch (type) {
17078 case "GeometryCollection": return {type: type, geometries: o.geometries.map(geometry)};
17079 case "Point": coordinates = point(o.coordinates); break;
17080 case "MultiPoint": coordinates = o.coordinates.map(point); break;
17081 case "LineString": coordinates = line(o.arcs); break;
17082 case "MultiLineString": coordinates = o.arcs.map(line); break;
17083 case "Polygon": coordinates = polygon(o.arcs); break;
17084 case "MultiPolygon": coordinates = o.arcs.map(polygon); break;
17085 default: return null;
17086 }
17087 return {type: type, coordinates: coordinates};
17088 }
17089
17090 return geometry(o);
17091}
17092
17093function stitch(topology, arcs) {
17094 var stitchedArcs = {},
17095 fragmentByStart = {},
17096 fragmentByEnd = {},
17097 fragments = [],
17098 emptyIndex = -1;
17099
17100 // Stitch empty arcs first, since they may be subsumed by other arcs.
17101 arcs.forEach(function(i, j) {
17102 var arc = topology.arcs[i < 0 ? ~i : i], t;
17103 if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
17104 t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
17105 }
17106 });
17107
17108 arcs.forEach(function(i) {
17109 var e = ends(i),
17110 start = e[0],
17111 end = e[1],
17112 f, g;
17113
17114 if (f = fragmentByEnd[start]) {
17115 delete fragmentByEnd[f.end];
17116 f.push(i);
17117 f.end = end;
17118 if (g = fragmentByStart[end]) {
17119 delete fragmentByStart[g.start];
17120 var fg = g === f ? f : f.concat(g);
17121 fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
17122 } else {
17123 fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
17124 }
17125 } else if (f = fragmentByStart[end]) {
17126 delete fragmentByStart[f.start];
17127 f.unshift(i);
17128 f.start = start;
17129 if (g = fragmentByEnd[start]) {
17130 delete fragmentByEnd[g.end];
17131 var gf = g === f ? f : g.concat(f);
17132 fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
17133 } else {
17134 fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
17135 }
17136 } else {
17137 f = [i];
17138 fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
17139 }
17140 });
17141
17142 function ends(i) {
17143 var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1;
17144 if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
17145 else p1 = arc[arc.length - 1];
17146 return i < 0 ? [p1, p0] : [p0, p1];
17147 }
17148
17149 function flush(fragmentByEnd, fragmentByStart) {
17150 for (var k in fragmentByEnd) {
17151 var f = fragmentByEnd[k];
17152 delete fragmentByStart[f.start];
17153 delete f.start;
17154 delete f.end;
17155 f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; });
17156 fragments.push(f);
17157 }
17158 }
17159
17160 flush(fragmentByEnd, fragmentByStart);
17161 flush(fragmentByStart, fragmentByEnd);
17162 arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); });
17163
17164 return fragments;
17165}
17166
17167function mesh(topology) {
17168 return object(topology, meshArcs.apply(this, arguments));
17169}
17170
17171function meshArcs(topology, object, filter) {
17172 var arcs, i, n;
17173 if (arguments.length > 1) arcs = extractArcs(topology, object, filter);
17174 else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i;
17175 return {type: "MultiLineString", arcs: stitch(topology, arcs)};
17176}
17177
17178function extractArcs(topology, object, filter) {
17179 var arcs = [],
17180 geomsByArc = [],
17181 geom;
17182
17183 function extract0(i) {
17184 var j = i < 0 ? ~i : i;
17185 (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
17186 }
17187
17188 function extract1(arcs) {
17189 arcs.forEach(extract0);
17190 }
17191
17192 function extract2(arcs) {
17193 arcs.forEach(extract1);
17194 }
17195
17196 function extract3(arcs) {
17197 arcs.forEach(extract2);
17198 }
17199
17200 function geometry(o) {
17201 switch (geom = o, o.type) {
17202 case "GeometryCollection": o.geometries.forEach(geometry); break;
17203 case "LineString": extract1(o.arcs); break;
17204 case "MultiLineString": case "Polygon": extract2(o.arcs); break;
17205 case "MultiPolygon": extract3(o.arcs); break;
17206 }
17207 }
17208
17209 geometry(object);
17210
17211 geomsByArc.forEach(filter == null
17212 ? function(geoms) { arcs.push(geoms[0].i); }
17213 : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });
17214
17215 return arcs;
17216}
17217
17218function planarRingArea(ring) {
17219 var i = -1, n = ring.length, a, b = ring[n - 1], area = 0;
17220 while (++i < n) a = b, b = ring[i], area += a[0] * b[1] - a[1] * b[0];
17221 return Math.abs(area); // Note: doubled area!
17222}
17223
17224function merge(topology) {
17225 return object(topology, mergeArcs.apply(this, arguments));
17226}
17227
17228function mergeArcs(topology, objects) {
17229 var polygonsByArc = {},
17230 polygons = [],
17231 groups = [];
17232
17233 objects.forEach(geometry);
17234
17235 function geometry(o) {
17236 switch (o.type) {
17237 case "GeometryCollection": o.geometries.forEach(geometry); break;
17238 case "Polygon": extract(o.arcs); break;
17239 case "MultiPolygon": o.arcs.forEach(extract); break;
17240 }
17241 }
17242
17243 function extract(polygon) {
17244 polygon.forEach(function(ring) {
17245 ring.forEach(function(arc) {
17246 (polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon);
17247 });
17248 });
17249 polygons.push(polygon);
17250 }
17251
17252 function area(ring) {
17253 return planarRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]);
17254 }
17255
17256 polygons.forEach(function(polygon) {
17257 if (!polygon._) {
17258 var group = [],
17259 neighbors = [polygon];
17260 polygon._ = 1;
17261 groups.push(group);
17262 while (polygon = neighbors.pop()) {
17263 group.push(polygon);
17264 polygon.forEach(function(ring) {
17265 ring.forEach(function(arc) {
17266 polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) {
17267 if (!polygon._) {
17268 polygon._ = 1;
17269 neighbors.push(polygon);
17270 }
17271 });
17272 });
17273 });
17274 }
17275 }
17276 });
17277
17278 polygons.forEach(function(polygon) {
17279 delete polygon._;
17280 });
17281
17282 return {
17283 type: "MultiPolygon",
17284 arcs: groups.map(function(polygons) {
17285 var arcs = [], n;
17286
17287 // Extract the exterior (unique) arcs.
17288 polygons.forEach(function(polygon) {
17289 polygon.forEach(function(ring) {
17290 ring.forEach(function(arc) {
17291 if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) {
17292 arcs.push(arc);
17293 }
17294 });
17295 });
17296 });
17297
17298 // Stitch the arcs into one or more rings.
17299 arcs = stitch(topology, arcs);
17300
17301 // If more than one ring is returned,
17302 // at most one of these rings can be the exterior;
17303 // choose the one with the greatest absolute area.
17304 if ((n = arcs.length) > 1) {
17305 for (var i = 1, k = area(arcs[0]), ki, t; i < n; ++i) {
17306 if ((ki = area(arcs[i])) > k) {
17307 t = arcs[0], arcs[0] = arcs[i], arcs[i] = t, k = ki;
17308 }
17309 }
17310 }
17311
17312 return arcs;
17313 }).filter(function(arcs) {
17314 return arcs.length > 0;
17315 })
17316 };
17317}
17318
17319function bisect(a, x) {
17320 var lo = 0, hi = a.length;
17321 while (lo < hi) {
17322 var mid = lo + hi >>> 1;
17323 if (a[mid] < x) lo = mid + 1;
17324 else hi = mid;
17325 }
17326 return lo;
17327}
17328
17329function neighbors(objects) {
17330 var indexesByArc = {}, // arc index -> array of object indexes
17331 neighbors = objects.map(function() { return []; });
17332
17333 function line(arcs, i) {
17334 arcs.forEach(function(a) {
17335 if (a < 0) a = ~a;
17336 var o = indexesByArc[a];
17337 if (o) o.push(i);
17338 else indexesByArc[a] = [i];
17339 });
17340 }
17341
17342 function polygon(arcs, i) {
17343 arcs.forEach(function(arc) { line(arc, i); });
17344 }
17345
17346 function geometry(o, i) {
17347 if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); });
17348 else if (o.type in geometryType) geometryType[o.type](o.arcs, i);
17349 }
17350
17351 var geometryType = {
17352 LineString: line,
17353 MultiLineString: polygon,
17354 Polygon: polygon,
17355 MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); }
17356 };
17357
17358 objects.forEach(geometry);
17359
17360 for (var i in indexesByArc) {
17361 for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) {
17362 for (var k = j + 1; k < m; ++k) {
17363 var ij = indexes[j], ik = indexes[k], n;
17364 if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik);
17365 if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij);
17366 }
17367 }
17368 }
17369
17370 return neighbors;
17371}
17372
17373function untransform(transform) {
17374 if (transform == null) return identity;
17375 var x0,
17376 y0,
17377 kx = transform.scale[0],
17378 ky = transform.scale[1],
17379 dx = transform.translate[0],
17380 dy = transform.translate[1];
17381 return function(input, i) {
17382 if (!i) x0 = y0 = 0;
17383 var j = 2,
17384 n = input.length,
17385 output = new Array(n),
17386 x1 = Math.round((input[0] - dx) / kx),
17387 y1 = Math.round((input[1] - dy) / ky);
17388 output[0] = x1 - x0, x0 = x1;
17389 output[1] = y1 - y0, y0 = y1;
17390 while (j < n) output[j] = input[j], ++j;
17391 return output;
17392 };
17393}
17394
17395function quantize(topology, transform) {
17396 if (topology.transform) throw new Error("already quantized");
17397
17398 if (!transform || !transform.scale) {
17399 if (!((n = Math.floor(transform)) >= 2)) throw new Error("n must be ≥2");
17400 box = topology.bbox || bbox(topology);
17401 var x0 = box[0], y0 = box[1], x1 = box[2], y1 = box[3], n;
17402 transform = {scale: [x1 - x0 ? (x1 - x0) / (n - 1) : 1, y1 - y0 ? (y1 - y0) / (n - 1) : 1], translate: [x0, y0]};
17403 } else {
17404 box = topology.bbox;
17405 }
17406
17407 var t = untransform(transform), box, key, inputs = topology.objects, outputs = {};
17408
17409 function quantizePoint(point) {
17410 return t(point);
17411 }
17412
17413 function quantizeGeometry(input) {
17414 var output;
17415 switch (input.type) {
17416 case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(quantizeGeometry)}; break;
17417 case "Point": output = {type: "Point", coordinates: quantizePoint(input.coordinates)}; break;
17418 case "MultiPoint": output = {type: "MultiPoint", coordinates: input.coordinates.map(quantizePoint)}; break;
17419 default: return input;
17420 }
17421 if (input.id != null) output.id = input.id;
17422 if (input.bbox != null) output.bbox = input.bbox;
17423 if (input.properties != null) output.properties = input.properties;
17424 return output;
17425 }
17426
17427 function quantizeArc(input) {
17428 var i = 0, j = 1, n = input.length, p, output = new Array(n); // pessimistic
17429 output[0] = t(input[0], 0);
17430 while (++i < n) if ((p = t(input[i], i))[0] || p[1]) output[j++] = p; // non-coincident points
17431 if (j === 1) output[j++] = [0, 0]; // an arc must have at least two points
17432 output.length = j;
17433 return output;
17434 }
17435
17436 for (key in inputs) outputs[key] = quantizeGeometry(inputs[key]);
17437
17438 return {
17439 type: "Topology",
17440 bbox: box,
17441 transform: transform,
17442 objects: outputs,
17443 arcs: topology.arcs.map(quantizeArc)
17444 };
17445}
17446
17447exports.bbox = bbox;
17448exports.feature = feature;
17449exports.merge = merge;
17450exports.mergeArcs = mergeArcs;
17451exports.mesh = mesh;
17452exports.meshArcs = meshArcs;
17453exports.neighbors = neighbors;
17454exports.quantize = quantize;
17455exports.transform = transform;
17456exports.untransform = untransform;
17457
17458Object.defineProperty(exports, '__esModule', { value: true });
17459
17460}));
17461
17462},{}],34:[function(_dereq_,module,exports){
17463/**
17464* Copyright 2012-2020, Plotly, Inc.
17465* All rights reserved.
17466*
17467* This source code is licensed under the MIT license found in the
17468* LICENSE file in the root directory of this source tree.
17469*/
17470
17471'use strict';
17472
17473/**
17474 * All paths are tuned for maximum scalability of the arrowhead,
17475 * ie throughout arrowwidth=0.3..3 the head is joined smoothly
17476 * to the line, with the line coming from the left and ending at (0, 0).
17477 *
17478 * `backoff` is the distance to move the arrowhead and the end of the line,
17479 * in order that the arrowhead points to the desired place, either at
17480 * the tip of the arrow or (in the case of circle or square)
17481 * the center of the symbol.
17482 *
17483 * `noRotate`, if truthy, says that this arrowhead should not rotate with the
17484 * arrow. That's the case for squares, which should always be straight, and
17485 * circles, for which it's irrelevant.
17486 */
17487
17488module.exports = [
17489 // no arrow
17490 {
17491 path: '',
17492 backoff: 0
17493 },
17494 // wide with flat back
17495 {
17496 path: 'M-2.4,-3V3L0.6,0Z',
17497 backoff: 0.6
17498 },
17499 // narrower with flat back
17500 {
17501 path: 'M-3.7,-2.5V2.5L1.3,0Z',
17502 backoff: 1.3
17503 },
17504 // barbed
17505 {
17506 path: 'M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z',
17507 backoff: 1.55
17508 },
17509 // wide line-drawn
17510 {
17511 path: 'M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z',
17512 backoff: 1.6
17513 },
17514 // narrower line-drawn
17515 {
17516 path: 'M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z',
17517 backoff: 2
17518 },
17519 // circle
17520 {
17521 path: 'M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z',
17522 backoff: 0,
17523 noRotate: true
17524 },
17525 // square
17526 {
17527 path: 'M2,2V-2H-2V2Z',
17528 backoff: 0,
17529 noRotate: true
17530 }
17531];
17532
17533},{}],35:[function(_dereq_,module,exports){
17534/**
17535* Copyright 2012-2020, Plotly, Inc.
17536* All rights reserved.
17537*
17538* This source code is licensed under the MIT license found in the
17539* LICENSE file in the root directory of this source tree.
17540*/
17541
17542'use strict';
17543
17544var ARROWPATHS = _dereq_('./arrow_paths');
17545var fontAttrs = _dereq_('../../plots/font_attributes');
17546var cartesianConstants = _dereq_('../../plots/cartesian/constants');
17547var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
17548
17549
17550module.exports = templatedArray('annotation', {
17551 visible: {
17552 valType: 'boolean',
17553
17554 dflt: true,
17555 editType: 'calc+arraydraw',
17556
17557 },
17558
17559 text: {
17560 valType: 'string',
17561
17562 editType: 'calc+arraydraw',
17563
17564 },
17565 textangle: {
17566 valType: 'angle',
17567 dflt: 0,
17568
17569 editType: 'calc+arraydraw',
17570
17571 },
17572 font: fontAttrs({
17573 editType: 'calc+arraydraw',
17574 colorEditType: 'arraydraw',
17575
17576 }),
17577 width: {
17578 valType: 'number',
17579 min: 1,
17580 dflt: null,
17581
17582 editType: 'calc+arraydraw',
17583
17584 },
17585 height: {
17586 valType: 'number',
17587 min: 1,
17588 dflt: null,
17589
17590 editType: 'calc+arraydraw',
17591
17592 },
17593 opacity: {
17594 valType: 'number',
17595 min: 0,
17596 max: 1,
17597 dflt: 1,
17598
17599 editType: 'arraydraw',
17600
17601 },
17602 align: {
17603 valType: 'enumerated',
17604 values: ['left', 'center', 'right'],
17605 dflt: 'center',
17606
17607 editType: 'arraydraw',
17608
17609 },
17610 valign: {
17611 valType: 'enumerated',
17612 values: ['top', 'middle', 'bottom'],
17613 dflt: 'middle',
17614
17615 editType: 'arraydraw',
17616
17617 },
17618 bgcolor: {
17619 valType: 'color',
17620 dflt: 'rgba(0,0,0,0)',
17621
17622 editType: 'arraydraw',
17623
17624 },
17625 bordercolor: {
17626 valType: 'color',
17627 dflt: 'rgba(0,0,0,0)',
17628
17629 editType: 'arraydraw',
17630
17631 },
17632 borderpad: {
17633 valType: 'number',
17634 min: 0,
17635 dflt: 1,
17636
17637 editType: 'calc+arraydraw',
17638
17639 },
17640 borderwidth: {
17641 valType: 'number',
17642 min: 0,
17643 dflt: 1,
17644
17645 editType: 'calc+arraydraw',
17646
17647 },
17648 // arrow
17649 showarrow: {
17650 valType: 'boolean',
17651 dflt: true,
17652
17653 editType: 'calc+arraydraw',
17654
17655 },
17656 arrowcolor: {
17657 valType: 'color',
17658
17659 editType: 'arraydraw',
17660
17661 },
17662 arrowhead: {
17663 valType: 'integer',
17664 min: 0,
17665 max: ARROWPATHS.length,
17666 dflt: 1,
17667
17668 editType: 'arraydraw',
17669
17670 },
17671 startarrowhead: {
17672 valType: 'integer',
17673 min: 0,
17674 max: ARROWPATHS.length,
17675 dflt: 1,
17676
17677 editType: 'arraydraw',
17678
17679 },
17680 arrowside: {
17681 valType: 'flaglist',
17682 flags: ['end', 'start'],
17683 extras: ['none'],
17684 dflt: 'end',
17685
17686 editType: 'arraydraw',
17687
17688 },
17689 arrowsize: {
17690 valType: 'number',
17691 min: 0.3,
17692 dflt: 1,
17693
17694 editType: 'calc+arraydraw',
17695
17696 },
17697 startarrowsize: {
17698 valType: 'number',
17699 min: 0.3,
17700 dflt: 1,
17701
17702 editType: 'calc+arraydraw',
17703
17704 },
17705 arrowwidth: {
17706 valType: 'number',
17707 min: 0.1,
17708
17709 editType: 'calc+arraydraw',
17710
17711 },
17712 standoff: {
17713 valType: 'number',
17714 min: 0,
17715 dflt: 0,
17716
17717 editType: 'calc+arraydraw',
17718
17719 },
17720 startstandoff: {
17721 valType: 'number',
17722 min: 0,
17723 dflt: 0,
17724
17725 editType: 'calc+arraydraw',
17726
17727 },
17728 ax: {
17729 valType: 'any',
17730
17731 editType: 'calc+arraydraw',
17732
17733 },
17734 ay: {
17735 valType: 'any',
17736
17737 editType: 'calc+arraydraw',
17738
17739 },
17740 axref: {
17741 valType: 'enumerated',
17742 dflt: 'pixel',
17743 values: [
17744 'pixel',
17745 cartesianConstants.idRegex.x.toString()
17746 ],
17747
17748 editType: 'calc',
17749
17750 },
17751 ayref: {
17752 valType: 'enumerated',
17753 dflt: 'pixel',
17754 values: [
17755 'pixel',
17756 cartesianConstants.idRegex.y.toString()
17757 ],
17758
17759 editType: 'calc',
17760
17761 },
17762 // positioning
17763 xref: {
17764 valType: 'enumerated',
17765 values: [
17766 'paper',
17767 cartesianConstants.idRegex.x.toString()
17768 ],
17769
17770 editType: 'calc',
17771
17772 },
17773 x: {
17774 valType: 'any',
17775
17776 editType: 'calc+arraydraw',
17777
17778 },
17779 xanchor: {
17780 valType: 'enumerated',
17781 values: ['auto', 'left', 'center', 'right'],
17782 dflt: 'auto',
17783
17784 editType: 'calc+arraydraw',
17785
17786 },
17787 xshift: {
17788 valType: 'number',
17789 dflt: 0,
17790
17791 editType: 'calc+arraydraw',
17792
17793 },
17794 yref: {
17795 valType: 'enumerated',
17796 values: [
17797 'paper',
17798 cartesianConstants.idRegex.y.toString()
17799 ],
17800
17801 editType: 'calc',
17802
17803 },
17804 y: {
17805 valType: 'any',
17806
17807 editType: 'calc+arraydraw',
17808
17809 },
17810 yanchor: {
17811 valType: 'enumerated',
17812 values: ['auto', 'top', 'middle', 'bottom'],
17813 dflt: 'auto',
17814
17815 editType: 'calc+arraydraw',
17816
17817 },
17818 yshift: {
17819 valType: 'number',
17820 dflt: 0,
17821
17822 editType: 'calc+arraydraw',
17823
17824 },
17825 clicktoshow: {
17826 valType: 'enumerated',
17827 values: [false, 'onoff', 'onout'],
17828 dflt: false,
17829
17830 editType: 'arraydraw',
17831
17832 },
17833 xclick: {
17834 valType: 'any',
17835
17836 editType: 'arraydraw',
17837
17838 },
17839 yclick: {
17840 valType: 'any',
17841
17842 editType: 'arraydraw',
17843
17844 },
17845 hovertext: {
17846 valType: 'string',
17847
17848 editType: 'arraydraw',
17849
17850 },
17851 hoverlabel: {
17852 bgcolor: {
17853 valType: 'color',
17854
17855 editType: 'arraydraw',
17856
17857 },
17858 bordercolor: {
17859 valType: 'color',
17860
17861 editType: 'arraydraw',
17862
17863 },
17864 font: fontAttrs({
17865 editType: 'arraydraw',
17866
17867 }),
17868 editType: 'arraydraw'
17869 },
17870 captureevents: {
17871 valType: 'boolean',
17872
17873 editType: 'arraydraw',
17874
17875 },
17876 editType: 'calc',
17877
17878 _deprecated: {
17879 ref: {
17880 valType: 'string',
17881
17882 editType: 'calc',
17883
17884 }
17885 }
17886});
17887
17888},{"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228,"../../plots/font_attributes":250,"./arrow_paths":34}],36:[function(_dereq_,module,exports){
17889/**
17890* Copyright 2012-2020, Plotly, Inc.
17891* All rights reserved.
17892*
17893* This source code is licensed under the MIT license found in the
17894* LICENSE file in the root directory of this source tree.
17895*/
17896
17897
17898'use strict';
17899
17900var Lib = _dereq_('../../lib');
17901var Axes = _dereq_('../../plots/cartesian/axes');
17902
17903var draw = _dereq_('./draw').draw;
17904
17905
17906module.exports = function calcAutorange(gd) {
17907 var fullLayout = gd._fullLayout;
17908 var annotationList = Lib.filterVisible(fullLayout.annotations);
17909
17910 if(annotationList.length && gd._fullData.length) {
17911 return Lib.syncOrAsync([draw, annAutorange], gd);
17912 }
17913};
17914
17915function annAutorange(gd) {
17916 var fullLayout = gd._fullLayout;
17917
17918 // find the bounding boxes for each of these annotations'
17919 // relative to their anchor points
17920 // use the arrow and the text bg rectangle,
17921 // as the whole anno may include hidden text in its bbox
17922 Lib.filterVisible(fullLayout.annotations).forEach(function(ann) {
17923 var xa = Axes.getFromId(gd, ann.xref);
17924 var ya = Axes.getFromId(gd, ann.yref);
17925
17926 ann._extremes = {};
17927 if(xa) calcAxisExpansion(ann, xa);
17928 if(ya) calcAxisExpansion(ann, ya);
17929 });
17930}
17931
17932function calcAxisExpansion(ann, ax) {
17933 var axId = ax._id;
17934 var letter = axId.charAt(0);
17935 var pos = ann[letter];
17936 var apos = ann['a' + letter];
17937 var ref = ann[letter + 'ref'];
17938 var aref = ann['a' + letter + 'ref'];
17939 var padplus = ann['_' + letter + 'padplus'];
17940 var padminus = ann['_' + letter + 'padminus'];
17941 var shift = {x: 1, y: -1}[letter] * ann[letter + 'shift'];
17942 var headSize = 3 * ann.arrowsize * ann.arrowwidth || 0;
17943 var headPlus = headSize + shift;
17944 var headMinus = headSize - shift;
17945 var startHeadSize = 3 * ann.startarrowsize * ann.arrowwidth || 0;
17946 var startHeadPlus = startHeadSize + shift;
17947 var startHeadMinus = startHeadSize - shift;
17948 var extremes;
17949
17950 if(aref === ref) {
17951 // expand for the arrowhead (padded by arrowhead)
17952 var extremeArrowHead = Axes.findExtremes(ax, [ax.r2c(pos)], {
17953 ppadplus: headPlus,
17954 ppadminus: headMinus
17955 });
17956 // again for the textbox (padded by textbox)
17957 var extremeText = Axes.findExtremes(ax, [ax.r2c(apos)], {
17958 ppadplus: Math.max(padplus, startHeadPlus),
17959 ppadminus: Math.max(padminus, startHeadMinus)
17960 });
17961 extremes = {
17962 min: [extremeArrowHead.min[0], extremeText.min[0]],
17963 max: [extremeArrowHead.max[0], extremeText.max[0]]
17964 };
17965 } else {
17966 startHeadPlus = apos ? startHeadPlus + apos : startHeadPlus;
17967 startHeadMinus = apos ? startHeadMinus - apos : startHeadMinus;
17968 extremes = Axes.findExtremes(ax, [ax.r2c(pos)], {
17969 ppadplus: Math.max(padplus, headPlus, startHeadPlus),
17970 ppadminus: Math.max(padminus, headMinus, startHeadMinus)
17971 });
17972 }
17973
17974 ann._extremes[axId] = extremes;
17975}
17976
17977},{"../../lib":177,"../../plots/cartesian/axes":222,"./draw":41}],37:[function(_dereq_,module,exports){
17978/**
17979* Copyright 2012-2020, Plotly, Inc.
17980* All rights reserved.
17981*
17982* This source code is licensed under the MIT license found in the
17983* LICENSE file in the root directory of this source tree.
17984*/
17985
17986'use strict';
17987
17988var Lib = _dereq_('../../lib');
17989var Registry = _dereq_('../../registry');
17990var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
17991
17992module.exports = {
17993 hasClickToShow: hasClickToShow,
17994 onClick: onClick
17995};
17996
17997/*
17998 * hasClickToShow: does the given hoverData have ANY annotations which will
17999 * turn ON if we click here? (used by hover events to set cursor)
18000 *
18001 * gd: graphDiv
18002 * hoverData: a hoverData array, as included with the *plotly_hover* or
18003 * *plotly_click* events in the `points` attribute
18004 *
18005 * returns: boolean
18006 */
18007function hasClickToShow(gd, hoverData) {
18008 var sets = getToggleSets(gd, hoverData);
18009 return sets.on.length > 0 || sets.explicitOff.length > 0;
18010}
18011
18012/*
18013 * onClick: perform the toggling (via Plotly.update) implied by clicking
18014 * at this hoverData
18015 *
18016 * gd: graphDiv
18017 * hoverData: a hoverData array, as included with the *plotly_hover* or
18018 * *plotly_click* events in the `points` attribute
18019 *
18020 * returns: Promise that the update is complete
18021 */
18022function onClick(gd, hoverData) {
18023 var toggleSets = getToggleSets(gd, hoverData);
18024 var onSet = toggleSets.on;
18025 var offSet = toggleSets.off.concat(toggleSets.explicitOff);
18026 var update = {};
18027 var annotationsOut = gd._fullLayout.annotations;
18028 var i, editHelpers;
18029
18030 if(!(onSet.length || offSet.length)) return;
18031
18032 for(i = 0; i < onSet.length; i++) {
18033 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[onSet[i]]);
18034 editHelpers.modifyItem('visible', true);
18035 Lib.extendFlat(update, editHelpers.getUpdateObj());
18036 }
18037
18038 for(i = 0; i < offSet.length; i++) {
18039 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[offSet[i]]);
18040 editHelpers.modifyItem('visible', false);
18041 Lib.extendFlat(update, editHelpers.getUpdateObj());
18042 }
18043
18044 return Registry.call('update', gd, {}, update);
18045}
18046
18047/*
18048 * getToggleSets: find the annotations which will turn on or off at this
18049 * hoverData
18050 *
18051 * gd: graphDiv
18052 * hoverData: a hoverData array, as included with the *plotly_hover* or
18053 * *plotly_click* events in the `points` attribute
18054 *
18055 * returns: {
18056 * on: Array (indices of annotations to turn on),
18057 * off: Array (indices to turn off because you're not hovering on them),
18058 * explicitOff: Array (indices to turn off because you *are* hovering on them)
18059 * }
18060 */
18061function getToggleSets(gd, hoverData) {
18062 var annotations = gd._fullLayout.annotations;
18063 var onSet = [];
18064 var offSet = [];
18065 var explicitOffSet = [];
18066 var hoverLen = (hoverData || []).length;
18067
18068 var i, j, anni, showMode, pointj, xa, ya, toggleType;
18069
18070 for(i = 0; i < annotations.length; i++) {
18071 anni = annotations[i];
18072 showMode = anni.clicktoshow;
18073
18074 if(showMode) {
18075 for(j = 0; j < hoverLen; j++) {
18076 pointj = hoverData[j];
18077 xa = pointj.xaxis;
18078 ya = pointj.yaxis;
18079
18080 if(xa._id === anni.xref &&
18081 ya._id === anni.yref &&
18082 xa.d2r(pointj.x) === clickData2r(anni._xclick, xa) &&
18083 ya.d2r(pointj.y) === clickData2r(anni._yclick, ya)
18084 ) {
18085 // match! toggle this annotation
18086 // regardless of its clicktoshow mode
18087 // but if it's onout mode, off is implicit
18088 if(anni.visible) {
18089 if(showMode === 'onout') toggleType = offSet;
18090 else toggleType = explicitOffSet;
18091 } else {
18092 toggleType = onSet;
18093 }
18094 toggleType.push(i);
18095 break;
18096 }
18097 }
18098
18099 if(j === hoverLen) {
18100 // no match - only turn this annotation OFF, and only if
18101 // showmode is 'onout'
18102 if(anni.visible && showMode === 'onout') offSet.push(i);
18103 }
18104 }
18105 }
18106
18107 return {on: onSet, off: offSet, explicitOff: explicitOffSet};
18108}
18109
18110// to handle log axes until v2
18111function clickData2r(d, ax) {
18112 return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d);
18113}
18114
18115},{"../../lib":177,"../../plot_api/plot_template":212,"../../registry":272}],38:[function(_dereq_,module,exports){
18116/**
18117* Copyright 2012-2020, Plotly, Inc.
18118* All rights reserved.
18119*
18120* This source code is licensed under the MIT license found in the
18121* LICENSE file in the root directory of this source tree.
18122*/
18123
18124'use strict';
18125
18126var Lib = _dereq_('../../lib');
18127var Color = _dereq_('../color');
18128
18129// defaults common to 'annotations' and 'annotations3d'
18130module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce) {
18131 coerce('opacity');
18132 var bgColor = coerce('bgcolor');
18133
18134 var borderColor = coerce('bordercolor');
18135 var borderOpacity = Color.opacity(borderColor);
18136
18137 coerce('borderpad');
18138
18139 var borderWidth = coerce('borderwidth');
18140 var showArrow = coerce('showarrow');
18141
18142 coerce('text', showArrow ? ' ' : fullLayout._dfltTitle.annotation);
18143 coerce('textangle');
18144 Lib.coerceFont(coerce, 'font', fullLayout.font);
18145
18146 coerce('width');
18147 coerce('align');
18148
18149 var h = coerce('height');
18150 if(h) coerce('valign');
18151
18152 if(showArrow) {
18153 var arrowside = coerce('arrowside');
18154 var arrowhead;
18155 var arrowsize;
18156
18157 if(arrowside.indexOf('end') !== -1) {
18158 arrowhead = coerce('arrowhead');
18159 arrowsize = coerce('arrowsize');
18160 }
18161
18162 if(arrowside.indexOf('start') !== -1) {
18163 coerce('startarrowhead', arrowhead);
18164 coerce('startarrowsize', arrowsize);
18165 }
18166 coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
18167 coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
18168 coerce('standoff');
18169 coerce('startstandoff');
18170 }
18171
18172 var hoverText = coerce('hovertext');
18173 var globalHoverLabel = fullLayout.hoverlabel || {};
18174
18175 if(hoverText) {
18176 var hoverBG = coerce('hoverlabel.bgcolor', globalHoverLabel.bgcolor ||
18177 (Color.opacity(bgColor) ? Color.rgb(bgColor) : Color.defaultLine)
18178 );
18179
18180 var hoverBorder = coerce('hoverlabel.bordercolor', globalHoverLabel.bordercolor ||
18181 Color.contrast(hoverBG)
18182 );
18183
18184 Lib.coerceFont(coerce, 'hoverlabel.font', {
18185 family: globalHoverLabel.font.family,
18186 size: globalHoverLabel.font.size,
18187 color: globalHoverLabel.font.color || hoverBorder
18188 });
18189 }
18190
18191 coerce('captureevents', !!hoverText);
18192};
18193
18194},{"../../lib":177,"../color":50}],39:[function(_dereq_,module,exports){
18195/**
18196* Copyright 2012-2020, Plotly, Inc.
18197* All rights reserved.
18198*
18199* This source code is licensed under the MIT license found in the
18200* LICENSE file in the root directory of this source tree.
18201*/
18202
18203
18204'use strict';
18205
18206var isNumeric = _dereq_('fast-isnumeric');
18207var toLogRange = _dereq_('../../lib/to_log_range');
18208
18209/*
18210 * convertCoords: when converting an axis between log and linear
18211 * you need to alter any annotations on that axis to keep them
18212 * pointing at the same data point.
18213 * In v2.0 this will become obsolete
18214 *
18215 * gd: the plot div
18216 * ax: the axis being changed
18217 * newType: the type it's getting
18218 * doExtra: function(attr, val) from inside relayout that sets the attribute.
18219 * Use this to make the changes as it's aware if any other changes in the
18220 * same relayout call should override this conversion.
18221 */
18222module.exports = function convertCoords(gd, ax, newType, doExtra) {
18223 ax = ax || {};
18224
18225 var toLog = (newType === 'log') && (ax.type === 'linear');
18226 var fromLog = (newType === 'linear') && (ax.type === 'log');
18227
18228 if(!(toLog || fromLog)) return;
18229
18230 var annotations = gd._fullLayout.annotations;
18231 var axLetter = ax._id.charAt(0);
18232 var ann;
18233 var attrPrefix;
18234
18235 function convert(attr) {
18236 var currentVal = ann[attr];
18237 var newVal = null;
18238
18239 if(toLog) newVal = toLogRange(currentVal, ax.range);
18240 else newVal = Math.pow(10, currentVal);
18241
18242 // if conversion failed, delete the value so it gets a default value
18243 if(!isNumeric(newVal)) newVal = null;
18244
18245 doExtra(attrPrefix + attr, newVal);
18246 }
18247
18248 for(var i = 0; i < annotations.length; i++) {
18249 ann = annotations[i];
18250 attrPrefix = 'annotations[' + i + '].';
18251
18252 if(ann[axLetter + 'ref'] === ax._id) convert(axLetter);
18253 if(ann['a' + axLetter + 'ref'] === ax._id) convert('a' + axLetter);
18254 }
18255};
18256
18257},{"../../lib/to_log_range":200,"fast-isnumeric":15}],40:[function(_dereq_,module,exports){
18258/**
18259* Copyright 2012-2020, Plotly, Inc.
18260* All rights reserved.
18261*
18262* This source code is licensed under the MIT license found in the
18263* LICENSE file in the root directory of this source tree.
18264*/
18265
18266
18267'use strict';
18268
18269var Lib = _dereq_('../../lib');
18270var Axes = _dereq_('../../plots/cartesian/axes');
18271var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
18272
18273var handleAnnotationCommonDefaults = _dereq_('./common_defaults');
18274var attributes = _dereq_('./attributes');
18275
18276
18277module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
18278 handleArrayContainerDefaults(layoutIn, layoutOut, {
18279 name: 'annotations',
18280 handleItemDefaults: handleAnnotationDefaults
18281 });
18282};
18283
18284function handleAnnotationDefaults(annIn, annOut, fullLayout) {
18285 function coerce(attr, dflt) {
18286 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
18287 }
18288
18289 var visible = coerce('visible');
18290 var clickToShow = coerce('clicktoshow');
18291
18292 if(!(visible || clickToShow)) return;
18293
18294 handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce);
18295
18296 var showArrow = annOut.showarrow;
18297
18298 // positioning
18299 var axLetters = ['x', 'y'];
18300 var arrowPosDflt = [-10, -30];
18301 var gdMock = {_fullLayout: fullLayout};
18302
18303 for(var i = 0; i < 2; i++) {
18304 var axLetter = axLetters[i];
18305
18306 // xref, yref
18307 var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
18308
18309 if(axRef !== 'paper') {
18310 var ax = Axes.getFromId(gdMock, axRef);
18311 ax._annIndices.push(annOut._index);
18312 }
18313
18314 // x, y
18315 Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
18316
18317 if(showArrow) {
18318 var arrowPosAttr = 'a' + axLetter;
18319 // axref, ayref
18320 var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
18321
18322 // for now the arrow can only be on the same axis or specified as pixels
18323 // TODO: sometime it might be interesting to allow it to be on *any* axis
18324 // but that would require updates to drawing & autorange code and maybe more
18325 if(aaxRef !== 'pixel' && aaxRef !== axRef) {
18326 aaxRef = annOut[arrowPosAttr] = 'pixel';
18327 }
18328
18329 // ax, ay
18330 var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4;
18331 Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt);
18332 }
18333
18334 // xanchor, yanchor
18335 coerce(axLetter + 'anchor');
18336
18337 // xshift, yshift
18338 coerce(axLetter + 'shift');
18339 }
18340
18341 // if you have one coordinate you should have both
18342 Lib.noneOrAll(annIn, annOut, ['x', 'y']);
18343
18344 // if you have one part of arrow length you should have both
18345 if(showArrow) {
18346 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
18347 }
18348
18349 if(clickToShow) {
18350 var xClick = coerce('xclick');
18351 var yClick = coerce('yclick');
18352
18353 // put the actual click data to bind to into private attributes
18354 // so we don't have to do this little bit of logic on every hover event
18355 annOut._xclick = (xClick === undefined) ?
18356 annOut.x :
18357 Axes.cleanPosition(xClick, gdMock, annOut.xref);
18358 annOut._yclick = (yClick === undefined) ?
18359 annOut.y :
18360 Axes.cleanPosition(yClick, gdMock, annOut.yref);
18361 }
18362}
18363
18364},{"../../lib":177,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":35,"./common_defaults":38}],41:[function(_dereq_,module,exports){
18365/**
18366* Copyright 2012-2020, Plotly, Inc.
18367* All rights reserved.
18368*
18369* This source code is licensed under the MIT license found in the
18370* LICENSE file in the root directory of this source tree.
18371*/
18372
18373'use strict';
18374
18375var d3 = _dereq_('d3');
18376
18377var Registry = _dereq_('../../registry');
18378var Plots = _dereq_('../../plots/plots');
18379var Lib = _dereq_('../../lib');
18380var Axes = _dereq_('../../plots/cartesian/axes');
18381var Color = _dereq_('../color');
18382var Drawing = _dereq_('../drawing');
18383var Fx = _dereq_('../fx');
18384var svgTextUtils = _dereq_('../../lib/svg_text_utils');
18385var setCursor = _dereq_('../../lib/setcursor');
18386var dragElement = _dereq_('../dragelement');
18387var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
18388
18389var drawArrowHead = _dereq_('./draw_arrow_head');
18390
18391// Annotations are stored in gd.layout.annotations, an array of objects
18392// index can point to one item in this array,
18393// or non-numeric to simply add a new one
18394// or -1 to modify all existing
18395// opt can be the full options object, or one key (to be set to value)
18396// or undefined to simply redraw
18397// if opt is blank, val can be 'add' or a full options object to add a new
18398// annotation at that point in the array, or 'remove' to delete this one
18399
18400module.exports = {
18401 draw: draw,
18402 drawOne: drawOne,
18403 drawRaw: drawRaw
18404};
18405
18406/*
18407 * draw: draw all annotations without any new modifications
18408 */
18409function draw(gd) {
18410 var fullLayout = gd._fullLayout;
18411
18412 fullLayout._infolayer.selectAll('.annotation').remove();
18413
18414 for(var i = 0; i < fullLayout.annotations.length; i++) {
18415 if(fullLayout.annotations[i].visible) {
18416 drawOne(gd, i);
18417 }
18418 }
18419
18420 return Plots.previousPromises(gd);
18421}
18422
18423/*
18424 * drawOne: draw a single cartesian or paper-ref annotation, potentially with modifications
18425 *
18426 * index (int): the annotation to draw
18427 */
18428function drawOne(gd, index) {
18429 var fullLayout = gd._fullLayout;
18430 var options = fullLayout.annotations[index] || {};
18431 var xa = Axes.getFromId(gd, options.xref);
18432 var ya = Axes.getFromId(gd, options.yref);
18433
18434 if(xa) xa.setScale();
18435 if(ya) ya.setScale();
18436
18437 drawRaw(gd, options, index, false, xa, ya);
18438}
18439
18440/**
18441 * drawRaw: draw a single annotation, potentially with modifications
18442 *
18443 * @param {DOM element} gd
18444 * @param {object} options : this annotation's fullLayout options
18445 * @param {integer} index : index in 'annotations' container of the annotation to draw
18446 * @param {string} subplotId : id of the annotation's subplot
18447 * - use false for 2d (i.e. cartesian or paper-ref) annotations
18448 * @param {object | undefined} xa : full x-axis object to compute subplot pos-to-px
18449 * @param {object | undefined} ya : ... y-axis
18450 */
18451function drawRaw(gd, options, index, subplotId, xa, ya) {
18452 var fullLayout = gd._fullLayout;
18453 var gs = gd._fullLayout._size;
18454 var edits = gd._context.edits;
18455
18456 var className, containerStr;
18457
18458 if(subplotId) {
18459 className = 'annotation-' + subplotId;
18460 containerStr = subplotId + '.annotations';
18461 } else {
18462 className = 'annotation';
18463 containerStr = 'annotations';
18464 }
18465
18466 var editHelpers = arrayEditor(gd.layout, containerStr, options);
18467 var modifyBase = editHelpers.modifyBase;
18468 var modifyItem = editHelpers.modifyItem;
18469 var getUpdateObj = editHelpers.getUpdateObj;
18470
18471 // remove the existing annotation if there is one
18472 fullLayout._infolayer
18473 .selectAll('.' + className + '[data-index="' + index + '"]')
18474 .remove();
18475
18476 var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
18477
18478 // this annotation is gone - quit now after deleting it
18479 // TODO: use d3 idioms instead of deleting and redrawing every time
18480 if(!options._input || options.visible === false) {
18481 d3.selectAll('#' + annClipID).remove();
18482 return;
18483 }
18484
18485 // calculated pixel positions
18486 // x & y each will get text, head, and tail as appropriate
18487 var annPosPx = {x: {}, y: {}};
18488 var textangle = +options.textangle || 0;
18489
18490 // create the components
18491 // made a single group to contain all, so opacity can work right
18492 // with border/arrow together this could handle a whole bunch of
18493 // cleanup at this point, but works for now
18494 var annGroup = fullLayout._infolayer.append('g')
18495 .classed(className, true)
18496 .attr('data-index', String(index))
18497 .style('opacity', options.opacity);
18498
18499 // another group for text+background so that they can rotate together
18500 var annTextGroup = annGroup.append('g')
18501 .classed('annotation-text-g', true);
18502
18503 var editTextPosition = edits[options.showarrow ? 'annotationTail' : 'annotationPosition'];
18504 var textEvents = options.captureevents || edits.annotationText || editTextPosition;
18505
18506 function makeEventData(initialEvent) {
18507 var eventData = {
18508 index: index,
18509 annotation: options._input,
18510 fullAnnotation: options,
18511 event: initialEvent
18512 };
18513 if(subplotId) {
18514 eventData.subplotId = subplotId;
18515 }
18516 return eventData;
18517 }
18518
18519 var annTextGroupInner = annTextGroup.append('g')
18520 .style('pointer-events', textEvents ? 'all' : null)
18521 .call(setCursor, 'pointer')
18522 .on('click', function() {
18523 gd._dragging = false;
18524 gd.emit('plotly_clickannotation', makeEventData(d3.event));
18525 });
18526
18527 if(options.hovertext) {
18528 annTextGroupInner
18529 .on('mouseover', function() {
18530 var hoverOptions = options.hoverlabel;
18531 var hoverFont = hoverOptions.font;
18532 var bBox = this.getBoundingClientRect();
18533 var bBoxRef = gd.getBoundingClientRect();
18534
18535 Fx.loneHover({
18536 x0: bBox.left - bBoxRef.left,
18537 x1: bBox.right - bBoxRef.left,
18538 y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
18539 text: options.hovertext,
18540 color: hoverOptions.bgcolor,
18541 borderColor: hoverOptions.bordercolor,
18542 fontFamily: hoverFont.family,
18543 fontSize: hoverFont.size,
18544 fontColor: hoverFont.color
18545 }, {
18546 container: fullLayout._hoverlayer.node(),
18547 outerContainer: fullLayout._paper.node(),
18548 gd: gd
18549 });
18550 })
18551 .on('mouseout', function() {
18552 Fx.loneUnhover(fullLayout._hoverlayer.node());
18553 });
18554 }
18555
18556 var borderwidth = options.borderwidth;
18557 var borderpad = options.borderpad;
18558 var borderfull = borderwidth + borderpad;
18559
18560 var annTextBG = annTextGroupInner.append('rect')
18561 .attr('class', 'bg')
18562 .style('stroke-width', borderwidth + 'px')
18563 .call(Color.stroke, options.bordercolor)
18564 .call(Color.fill, options.bgcolor);
18565
18566 var isSizeConstrained = options.width || options.height;
18567
18568 var annTextClip = fullLayout._topclips
18569 .selectAll('#' + annClipID)
18570 .data(isSizeConstrained ? [0] : []);
18571
18572 annTextClip.enter().append('clipPath')
18573 .classed('annclip', true)
18574 .attr('id', annClipID)
18575 .append('rect');
18576 annTextClip.exit().remove();
18577
18578 var font = options.font;
18579
18580 var text = fullLayout._meta ?
18581 Lib.templateString(options.text, fullLayout._meta) :
18582 options.text;
18583
18584 var annText = annTextGroupInner.append('text')
18585 .classed('annotation-text', true)
18586 .text(text);
18587
18588 function textLayout(s) {
18589 s.call(Drawing.font, font)
18590 .attr({
18591 'text-anchor': {
18592 left: 'start',
18593 right: 'end'
18594 }[options.align] || 'middle'
18595 });
18596
18597 svgTextUtils.convertToTspans(s, gd, drawGraphicalElements);
18598 return s;
18599 }
18600
18601 function drawGraphicalElements() {
18602 // if the text has *only* a link, make the whole box into a link
18603 var anchor3 = annText.selectAll('a');
18604 if(anchor3.size() === 1 && anchor3.text() === annText.text()) {
18605 var wholeLink = annTextGroupInner.insert('a', ':first-child').attr({
18606 'xlink:xlink:href': anchor3.attr('xlink:href'),
18607 'xlink:xlink:show': anchor3.attr('xlink:show')
18608 })
18609 .style({cursor: 'pointer'});
18610
18611 wholeLink.node().appendChild(annTextBG.node());
18612 }
18613
18614 var mathjaxGroup = annTextGroupInner.select('.annotation-text-math-group');
18615 var hasMathjax = !mathjaxGroup.empty();
18616 var anntextBB = Drawing.bBox(
18617 (hasMathjax ? mathjaxGroup : annText).node());
18618 var textWidth = anntextBB.width;
18619 var textHeight = anntextBB.height;
18620 var annWidth = options.width || textWidth;
18621 var annHeight = options.height || textHeight;
18622 var outerWidth = Math.round(annWidth + 2 * borderfull);
18623 var outerHeight = Math.round(annHeight + 2 * borderfull);
18624
18625 function shiftFraction(v, anchor) {
18626 if(anchor === 'auto') {
18627 if(v < 1 / 3) anchor = 'left';
18628 else if(v > 2 / 3) anchor = 'right';
18629 else anchor = 'center';
18630 }
18631 return {
18632 center: 0,
18633 middle: 0,
18634 left: 0.5,
18635 bottom: -0.5,
18636 right: -0.5,
18637 top: 0.5
18638 }[anchor];
18639 }
18640
18641 var annotationIsOffscreen = false;
18642 var letters = ['x', 'y'];
18643
18644 for(var i = 0; i < letters.length; i++) {
18645 var axLetter = letters[i];
18646 var axRef = options[axLetter + 'ref'] || axLetter;
18647 var tailRef = options['a' + axLetter + 'ref'];
18648 var ax = {x: xa, y: ya}[axLetter];
18649 var dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180;
18650 // note that these two can be either positive or negative
18651 var annSizeFromWidth = outerWidth * Math.cos(dimAngle);
18652 var annSizeFromHeight = outerHeight * Math.sin(dimAngle);
18653 // but this one is the positive total size
18654 var annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight);
18655 var anchor = options[axLetter + 'anchor'];
18656 var overallShift = options[axLetter + 'shift'] * (axLetter === 'x' ? 1 : -1);
18657 var posPx = annPosPx[axLetter];
18658 var basePx;
18659 var textPadShift;
18660 var alignPosition;
18661 var autoAlignFraction;
18662 var textShift;
18663
18664 /*
18665 * calculate the *primary* pixel position
18666 * which is the arrowhead if there is one,
18667 * otherwise the text anchor point
18668 */
18669 if(ax) {
18670 // check if annotation is off screen, to bypass DOM manipulations
18671 var posFraction = ax.r2fraction(options[axLetter]);
18672 if(posFraction < 0 || posFraction > 1) {
18673 if(tailRef === axRef) {
18674 posFraction = ax.r2fraction(options['a' + axLetter]);
18675 if(posFraction < 0 || posFraction > 1) {
18676 annotationIsOffscreen = true;
18677 }
18678 } else {
18679 annotationIsOffscreen = true;
18680 }
18681 }
18682 basePx = ax._offset + ax.r2p(options[axLetter]);
18683 autoAlignFraction = 0.5;
18684 } else {
18685 if(axLetter === 'x') {
18686 alignPosition = options[axLetter];
18687 basePx = gs.l + gs.w * alignPosition;
18688 } else {
18689 alignPosition = 1 - options[axLetter];
18690 basePx = gs.t + gs.h * alignPosition;
18691 }
18692 autoAlignFraction = options.showarrow ? 0.5 : alignPosition;
18693 }
18694
18695 // now translate this into pixel positions of head, tail, and text
18696 // as well as paddings for autorange
18697 if(options.showarrow) {
18698 posPx.head = basePx;
18699
18700 var arrowLength = options['a' + axLetter];
18701
18702 // with an arrow, the text rotates around the anchor point
18703 textShift = annSizeFromWidth * shiftFraction(0.5, options.xanchor) -
18704 annSizeFromHeight * shiftFraction(0.5, options.yanchor);
18705
18706 if(tailRef === axRef) {
18707 posPx.tail = ax._offset + ax.r2p(arrowLength);
18708 // tail is data-referenced: autorange pads the text in px from the tail
18709 textPadShift = textShift;
18710 } else {
18711 posPx.tail = basePx + arrowLength;
18712 // tail is specified in px from head, so autorange also pads vs head
18713 textPadShift = textShift + arrowLength;
18714 }
18715
18716 posPx.text = posPx.tail + textShift;
18717
18718 // constrain pixel/paper referenced so the draggers are at least
18719 // partially visible
18720 var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height'];
18721 if(axRef === 'paper') {
18722 posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1);
18723 }
18724 if(tailRef === 'pixel') {
18725 var shiftPlus = -Math.max(posPx.tail - 3, posPx.text);
18726 var shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx;
18727 if(shiftPlus > 0) {
18728 posPx.tail += shiftPlus;
18729 posPx.text += shiftPlus;
18730 } else if(shiftMinus > 0) {
18731 posPx.tail -= shiftMinus;
18732 posPx.text -= shiftMinus;
18733 }
18734 }
18735
18736 posPx.tail += overallShift;
18737 posPx.head += overallShift;
18738 } else {
18739 // with no arrow, the text rotates and *then* we put the anchor
18740 // relative to the new bounding box
18741 textShift = annSize * shiftFraction(autoAlignFraction, anchor);
18742 textPadShift = textShift;
18743 posPx.text = basePx + textShift;
18744 }
18745
18746 posPx.text += overallShift;
18747 textShift += overallShift;
18748 textPadShift += overallShift;
18749
18750 // padplus/minus are used by autorange
18751 options['_' + axLetter + 'padplus'] = (annSize / 2) + textPadShift;
18752 options['_' + axLetter + 'padminus'] = (annSize / 2) - textPadShift;
18753
18754 // size/shift are used during dragging
18755 options['_' + axLetter + 'size'] = annSize;
18756 options['_' + axLetter + 'shift'] = textShift;
18757 }
18758
18759 if(annotationIsOffscreen) {
18760 annTextGroupInner.remove();
18761 return;
18762 }
18763
18764 var xShift = 0;
18765 var yShift = 0;
18766
18767 if(options.align !== 'left') {
18768 xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
18769 }
18770 if(options.valign !== 'top') {
18771 yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
18772 }
18773
18774 if(hasMathjax) {
18775 mathjaxGroup.select('svg').attr({
18776 x: borderfull + xShift - 1,
18777 y: borderfull + yShift
18778 })
18779 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
18780 } else {
18781 var texty = borderfull + yShift - anntextBB.top;
18782 var textx = borderfull + xShift - anntextBB.left;
18783
18784 annText.call(svgTextUtils.positionText, textx, texty)
18785 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
18786 }
18787
18788 annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
18789 annWidth, annHeight);
18790
18791 annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
18792 outerWidth - borderwidth, outerHeight - borderwidth);
18793
18794 annTextGroupInner.call(Drawing.setTranslate,
18795 Math.round(annPosPx.x.text - outerWidth / 2),
18796 Math.round(annPosPx.y.text - outerHeight / 2));
18797
18798 /*
18799 * rotate text and background
18800 * we already calculated the text center position *as rotated*
18801 * because we needed that for autoranging anyway, so now whether
18802 * we have an arrow or not, we rotate about the text center.
18803 */
18804 annTextGroup.attr({transform: 'rotate(' + textangle + ',' +
18805 annPosPx.x.text + ',' + annPosPx.y.text + ')'});
18806
18807 /*
18808 * add the arrow
18809 * uses options[arrowwidth,arrowcolor,arrowhead] for styling
18810 * dx and dy are normally zero, but when you are dragging the textbox
18811 * while the head stays put, dx and dy are the pixel offsets
18812 */
18813 var drawArrow = function(dx, dy) {
18814 annGroup
18815 .selectAll('.annotation-arrow-g')
18816 .remove();
18817
18818 var headX = annPosPx.x.head;
18819 var headY = annPosPx.y.head;
18820 var tailX = annPosPx.x.tail + dx;
18821 var tailY = annPosPx.y.tail + dy;
18822 var textX = annPosPx.x.text + dx;
18823 var textY = annPosPx.y.text + dy;
18824
18825 // find the edge of the text box, where we'll start the arrow:
18826 // create transform matrix to rotate the text box corners
18827 var transform = Lib.rotationXYMatrix(textangle, textX, textY);
18828 var applyTransform = Lib.apply2DTransform(transform);
18829 var applyTransform2 = Lib.apply2DTransform2(transform);
18830
18831 // calculate and transform bounding box
18832 var width = +annTextBG.attr('width');
18833 var height = +annTextBG.attr('height');
18834 var xLeft = textX - 0.5 * width;
18835 var xRight = xLeft + width;
18836 var yTop = textY - 0.5 * height;
18837 var yBottom = yTop + height;
18838 var edges = [
18839 [xLeft, yTop, xLeft, yBottom],
18840 [xLeft, yBottom, xRight, yBottom],
18841 [xRight, yBottom, xRight, yTop],
18842 [xRight, yTop, xLeft, yTop]
18843 ].map(applyTransform2);
18844
18845 // Remove the line if it ends inside the box. Use ray
18846 // casting for rotated boxes: see which edges intersect a
18847 // line from the arrowhead to far away and reduce with xor
18848 // to get the parity of the number of intersections.
18849 if(edges.reduce(function(a, x) {
18850 return a ^
18851 !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6,
18852 x[0], x[1], x[2], x[3]);
18853 }, false)) {
18854 // no line or arrow - so quit drawArrow now
18855 return;
18856 }
18857
18858 edges.forEach(function(x) {
18859 var p = Lib.segmentsIntersect(tailX, tailY, headX, headY,
18860 x[0], x[1], x[2], x[3]);
18861 if(p) {
18862 tailX = p.x;
18863 tailY = p.y;
18864 }
18865 });
18866
18867 var strokewidth = options.arrowwidth;
18868 var arrowColor = options.arrowcolor;
18869 var arrowSide = options.arrowside;
18870
18871 var arrowGroup = annGroup.append('g')
18872 .style({opacity: Color.opacity(arrowColor)})
18873 .classed('annotation-arrow-g', true);
18874
18875 var arrow = arrowGroup.append('path')
18876 .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY)
18877 .style('stroke-width', strokewidth + 'px')
18878 .call(Color.stroke, Color.rgb(arrowColor));
18879
18880 drawArrowHead(arrow, arrowSide, options);
18881
18882 // the arrow dragger is a small square right at the head, then a line to the tail,
18883 // all expanded by a stroke width of 6px plus the arrow line width
18884 if(edits.annotationPosition && arrow.node().parentNode && !subplotId) {
18885 var arrowDragHeadX = headX;
18886 var arrowDragHeadY = headY;
18887 if(options.standoff) {
18888 var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2));
18889 arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength;
18890 arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength;
18891 }
18892 var arrowDrag = arrowGroup.append('path')
18893 .classed('annotation-arrow', true)
18894 .classed('anndrag', true)
18895 .classed('cursor-move', true)
18896 .attr({
18897 d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY),
18898 transform: 'translate(' + arrowDragHeadX + ',' + arrowDragHeadY + ')'
18899 })
18900 .style('stroke-width', (strokewidth + 6) + 'px')
18901 .call(Color.stroke, 'rgba(0,0,0,0)')
18902 .call(Color.fill, 'rgba(0,0,0,0)');
18903
18904 var annx0, anny0;
18905
18906 // dragger for the arrow & head: translates the whole thing
18907 // (head/tail/text) all together
18908 dragElement.init({
18909 element: arrowDrag.node(),
18910 gd: gd,
18911 prepFn: function() {
18912 var pos = Drawing.getTranslate(annTextGroupInner);
18913
18914 annx0 = pos.x;
18915 anny0 = pos.y;
18916 if(xa && xa.autorange) {
18917 modifyBase(xa._name + '.autorange', true);
18918 }
18919 if(ya && ya.autorange) {
18920 modifyBase(ya._name + '.autorange', true);
18921 }
18922 },
18923 moveFn: function(dx, dy) {
18924 var annxy0 = applyTransform(annx0, anny0);
18925 var xcenter = annxy0[0] + dx;
18926 var ycenter = annxy0[1] + dy;
18927 annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter);
18928
18929 modifyItem('x', xa ?
18930 xa.p2r(xa.r2p(options.x) + dx) :
18931 (options.x + (dx / gs.w)));
18932 modifyItem('y', ya ?
18933 ya.p2r(ya.r2p(options.y) + dy) :
18934 (options.y - (dy / gs.h)));
18935
18936 if(options.axref === options.xref) {
18937 modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
18938 }
18939
18940 if(options.ayref === options.yref) {
18941 modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
18942 }
18943
18944 arrowGroup.attr('transform', 'translate(' + dx + ',' + dy + ')');
18945 annTextGroup.attr({
18946 transform: 'rotate(' + textangle + ',' +
18947 xcenter + ',' + ycenter + ')'
18948 });
18949 },
18950 doneFn: function() {
18951 Registry.call('_guiRelayout', gd, getUpdateObj());
18952 var notesBox = document.querySelector('.js-notes-box-panel');
18953 if(notesBox) notesBox.redraw(notesBox.selectedObj);
18954 }
18955 });
18956 }
18957 };
18958
18959 if(options.showarrow) drawArrow(0, 0);
18960
18961 // user dragging the annotation (text, not arrow)
18962 if(editTextPosition) {
18963 var baseTextTransform;
18964
18965 // dragger for the textbox: if there's an arrow, just drag the
18966 // textbox and tail, leave the head untouched
18967 dragElement.init({
18968 element: annTextGroupInner.node(),
18969 gd: gd,
18970 prepFn: function() {
18971 baseTextTransform = annTextGroup.attr('transform');
18972 },
18973 moveFn: function(dx, dy) {
18974 var csr = 'pointer';
18975 if(options.showarrow) {
18976 if(options.axref === options.xref) {
18977 modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
18978 } else {
18979 modifyItem('ax', options.ax + dx);
18980 }
18981
18982 if(options.ayref === options.yref) {
18983 modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
18984 } else {
18985 modifyItem('ay', options.ay + dy);
18986 }
18987
18988 drawArrow(dx, dy);
18989 } else if(!subplotId) {
18990 var xUpdate, yUpdate;
18991 if(xa) {
18992 xUpdate = xa.p2r(xa.r2p(options.x) + dx);
18993 } else {
18994 var widthFraction = options._xsize / gs.w;
18995 var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2;
18996
18997 xUpdate = dragElement.align(xLeft + dx / gs.w,
18998 widthFraction, 0, 1, options.xanchor);
18999 }
19000
19001 if(ya) {
19002 yUpdate = ya.p2r(ya.r2p(options.y) + dy);
19003 } else {
19004 var heightFraction = options._ysize / gs.h;
19005 var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2;
19006
19007 yUpdate = dragElement.align(yBottom - dy / gs.h,
19008 heightFraction, 0, 1, options.yanchor);
19009 }
19010 modifyItem('x', xUpdate);
19011 modifyItem('y', yUpdate);
19012 if(!xa || !ya) {
19013 csr = dragElement.getCursor(
19014 xa ? 0.5 : xUpdate,
19015 ya ? 0.5 : yUpdate,
19016 options.xanchor, options.yanchor
19017 );
19018 }
19019 } else return;
19020
19021 annTextGroup.attr({
19022 transform: 'translate(' + dx + ',' + dy + ')' + baseTextTransform
19023 });
19024
19025 setCursor(annTextGroupInner, csr);
19026 },
19027 clickFn: function(_, initialEvent) {
19028 if(options.captureevents) {
19029 gd.emit('plotly_clickannotation', makeEventData(initialEvent));
19030 }
19031 },
19032 doneFn: function() {
19033 setCursor(annTextGroupInner);
19034 Registry.call('_guiRelayout', gd, getUpdateObj());
19035 var notesBox = document.querySelector('.js-notes-box-panel');
19036 if(notesBox) notesBox.redraw(notesBox.selectedObj);
19037 }
19038 });
19039 }
19040 }
19041
19042 if(edits.annotationText) {
19043 annText.call(svgTextUtils.makeEditable, {delegate: annTextGroupInner, gd: gd})
19044 .call(textLayout)
19045 .on('edit', function(_text) {
19046 options.text = _text;
19047
19048 this.call(textLayout);
19049
19050 modifyItem('text', _text);
19051
19052 if(xa && xa.autorange) {
19053 modifyBase(xa._name + '.autorange', true);
19054 }
19055 if(ya && ya.autorange) {
19056 modifyBase(ya._name + '.autorange', true);
19057 }
19058
19059 Registry.call('_guiRelayout', gd, getUpdateObj());
19060 });
19061 } else annText.call(textLayout);
19062}
19063
19064},{"../../lib":177,"../../lib/setcursor":196,"../../lib/svg_text_utils":198,"../../plot_api/plot_template":212,"../../plots/cartesian/axes":222,"../../plots/plots":263,"../../registry":272,"../color":50,"../dragelement":69,"../drawing":72,"../fx":90,"./draw_arrow_head":42,"d3":13}],42:[function(_dereq_,module,exports){
19065/**
19066* Copyright 2012-2020, Plotly, Inc.
19067* All rights reserved.
19068*
19069* This source code is licensed under the MIT license found in the
19070* LICENSE file in the root directory of this source tree.
19071*/
19072
19073
19074'use strict';
19075
19076var d3 = _dereq_('d3');
19077
19078var Color = _dereq_('../color');
19079
19080var ARROWPATHS = _dereq_('./arrow_paths');
19081
19082/**
19083 * Add arrowhead(s) to a path or line element
19084 *
19085 * @param {d3.selection} el3: a d3-selected line or path element
19086 *
19087 * @param {string} ends: 'none', 'start', 'end', or 'start+end' for which ends get arrowheads
19088 *
19089 * @param {object} options: style information. Must have all the following:
19090 * @param {number} options.arrowhead: end head style - see ./arrow_paths
19091 * @param {number} options.startarrowhead: start head style - see ./arrow_paths
19092 * @param {number} options.arrowsize: relative size of the end head vs line width
19093 * @param {number} options.startarrowsize: relative size of the start head vs line width
19094 * @param {number} options.standoff: distance in px to move the end arrow point from its target
19095 * @param {number} options.startstandoff: distance in px to move the start arrow point from its target
19096 * @param {number} options.arrowwidth: width of the arrow line
19097 * @param {string} options.arrowcolor: color of the arrow line, for the head to match
19098 * Note that the opacity of this color is ignored, as it's assumed the container
19099 * of both the line and head has opacity applied to it so there isn't greater opacity
19100 * where they overlap.
19101 */
19102module.exports = function drawArrowHead(el3, ends, options) {
19103 var el = el3.node();
19104 var headStyle = ARROWPATHS[options.arrowhead || 0];
19105 var startHeadStyle = ARROWPATHS[options.startarrowhead || 0];
19106 var scale = (options.arrowwidth || 1) * (options.arrowsize || 1);
19107 var startScale = (options.arrowwidth || 1) * (options.startarrowsize || 1);
19108 var doStart = ends.indexOf('start') >= 0;
19109 var doEnd = ends.indexOf('end') >= 0;
19110 var backOff = headStyle.backoff * scale + options.standoff;
19111 var startBackOff = startHeadStyle.backoff * startScale + options.startstandoff;
19112
19113 var start, end, startRot, endRot;
19114
19115 if(el.nodeName === 'line') {
19116 start = {x: +el3.attr('x1'), y: +el3.attr('y1')};
19117 end = {x: +el3.attr('x2'), y: +el3.attr('y2')};
19118
19119 var dx = start.x - end.x;
19120 var dy = start.y - end.y;
19121
19122 startRot = Math.atan2(dy, dx);
19123 endRot = startRot + Math.PI;
19124 if(backOff && startBackOff) {
19125 if(backOff + startBackOff > Math.sqrt(dx * dx + dy * dy)) {
19126 hideLine();
19127 return;
19128 }
19129 }
19130
19131 if(backOff) {
19132 if(backOff * backOff > dx * dx + dy * dy) {
19133 hideLine();
19134 return;
19135 }
19136 var backOffX = backOff * Math.cos(startRot);
19137 var backOffY = backOff * Math.sin(startRot);
19138
19139 end.x += backOffX;
19140 end.y += backOffY;
19141 el3.attr({x2: end.x, y2: end.y});
19142 }
19143
19144 if(startBackOff) {
19145 if(startBackOff * startBackOff > dx * dx + dy * dy) {
19146 hideLine();
19147 return;
19148 }
19149 var startBackOffX = startBackOff * Math.cos(startRot);
19150 var startbackOffY = startBackOff * Math.sin(startRot);
19151
19152 start.x -= startBackOffX;
19153 start.y -= startbackOffY;
19154 el3.attr({x1: start.x, y1: start.y});
19155 }
19156 } else if(el.nodeName === 'path') {
19157 var pathlen = el.getTotalLength();
19158 // using dash to hide the backOff region of the path.
19159 // if we ever allow dash for the arrow we'll have to
19160 // do better than this hack... maybe just manually
19161 // combine the two
19162 var dashArray = '';
19163
19164 if(pathlen < backOff + startBackOff) {
19165 hideLine();
19166 return;
19167 }
19168
19169
19170 var start0 = el.getPointAtLength(0);
19171 var dstart = el.getPointAtLength(0.1);
19172
19173 startRot = Math.atan2(start0.y - dstart.y, start0.x - dstart.x);
19174 start = el.getPointAtLength(Math.min(startBackOff, pathlen));
19175
19176 dashArray = '0px,' + startBackOff + 'px,';
19177
19178 var end0 = el.getPointAtLength(pathlen);
19179 var dend = el.getPointAtLength(pathlen - 0.1);
19180
19181 endRot = Math.atan2(end0.y - dend.y, end0.x - dend.x);
19182 end = el.getPointAtLength(Math.max(0, pathlen - backOff));
19183
19184 var shortening = dashArray ? startBackOff + backOff : backOff;
19185 dashArray += (pathlen - shortening) + 'px,' + pathlen + 'px';
19186
19187 el3.style('stroke-dasharray', dashArray);
19188 }
19189
19190 function hideLine() { el3.style('stroke-dasharray', '0px,100px'); }
19191
19192 function drawhead(arrowHeadStyle, p, rot, arrowScale) {
19193 if(!arrowHeadStyle.path) return;
19194 if(arrowHeadStyle.noRotate) rot = 0;
19195
19196 d3.select(el.parentNode).append('path')
19197 .attr({
19198 'class': el3.attr('class'),
19199 d: arrowHeadStyle.path,
19200 transform:
19201 'translate(' + p.x + ',' + p.y + ')' +
19202 (rot ? 'rotate(' + (rot * 180 / Math.PI) + ')' : '') +
19203 'scale(' + arrowScale + ')'
19204 })
19205 .style({
19206 fill: Color.rgb(options.arrowcolor),
19207 'stroke-width': 0
19208 });
19209 }
19210
19211 if(doStart) drawhead(startHeadStyle, start, startRot, startScale);
19212 if(doEnd) drawhead(headStyle, end, endRot, scale);
19213};
19214
19215},{"../color":50,"./arrow_paths":34,"d3":13}],43:[function(_dereq_,module,exports){
19216/**
19217* Copyright 2012-2020, Plotly, Inc.
19218* All rights reserved.
19219*
19220* This source code is licensed under the MIT license found in the
19221* LICENSE file in the root directory of this source tree.
19222*/
19223
19224
19225'use strict';
19226
19227var drawModule = _dereq_('./draw');
19228var clickModule = _dereq_('./click');
19229
19230module.exports = {
19231 moduleType: 'component',
19232 name: 'annotations',
19233
19234 layoutAttributes: _dereq_('./attributes'),
19235 supplyLayoutDefaults: _dereq_('./defaults'),
19236 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('annotations'),
19237
19238 calcAutorange: _dereq_('./calc_autorange'),
19239 draw: drawModule.draw,
19240 drawOne: drawModule.drawOne,
19241 drawRaw: drawModule.drawRaw,
19242
19243 hasClickToShow: clickModule.hasClickToShow,
19244 onClick: clickModule.onClick,
19245
19246 convertCoords: _dereq_('./convert_coords')
19247};
19248
19249},{"../../plots/cartesian/include_components":234,"./attributes":35,"./calc_autorange":36,"./click":37,"./convert_coords":39,"./defaults":40,"./draw":41}],44:[function(_dereq_,module,exports){
19250/**
19251* Copyright 2012-2020, Plotly, Inc.
19252* All rights reserved.
19253*
19254* This source code is licensed under the MIT license found in the
19255* LICENSE file in the root directory of this source tree.
19256*/
19257
19258
19259'use strict';
19260
19261var annAttrs = _dereq_('../annotations/attributes');
19262var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
19263var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
19264
19265module.exports = overrideAll(templatedArray('annotation', {
19266 visible: annAttrs.visible,
19267 x: {
19268 valType: 'any',
19269
19270
19271 },
19272 y: {
19273 valType: 'any',
19274
19275
19276 },
19277 z: {
19278 valType: 'any',
19279
19280
19281 },
19282 ax: {
19283 valType: 'number',
19284
19285
19286 },
19287 ay: {
19288 valType: 'number',
19289
19290
19291 },
19292
19293 xanchor: annAttrs.xanchor,
19294 xshift: annAttrs.xshift,
19295 yanchor: annAttrs.yanchor,
19296 yshift: annAttrs.yshift,
19297
19298 text: annAttrs.text,
19299 textangle: annAttrs.textangle,
19300 font: annAttrs.font,
19301 width: annAttrs.width,
19302 height: annAttrs.height,
19303 opacity: annAttrs.opacity,
19304 align: annAttrs.align,
19305 valign: annAttrs.valign,
19306 bgcolor: annAttrs.bgcolor,
19307 bordercolor: annAttrs.bordercolor,
19308 borderpad: annAttrs.borderpad,
19309 borderwidth: annAttrs.borderwidth,
19310 showarrow: annAttrs.showarrow,
19311 arrowcolor: annAttrs.arrowcolor,
19312 arrowhead: annAttrs.arrowhead,
19313 startarrowhead: annAttrs.startarrowhead,
19314 arrowside: annAttrs.arrowside,
19315 arrowsize: annAttrs.arrowsize,
19316 startarrowsize: annAttrs.startarrowsize,
19317 arrowwidth: annAttrs.arrowwidth,
19318 standoff: annAttrs.standoff,
19319 startstandoff: annAttrs.startstandoff,
19320 hovertext: annAttrs.hovertext,
19321 hoverlabel: annAttrs.hoverlabel,
19322 captureevents: annAttrs.captureevents,
19323
19324 // maybes later?
19325 // clicktoshow: annAttrs.clicktoshow,
19326 // xclick: annAttrs.xclick,
19327 // yclick: annAttrs.yclick,
19328
19329 // not needed!
19330 // axref: 'pixel'
19331 // ayref: 'pixel'
19332 // xref: 'x'
19333 // yref: 'y
19334 // zref: 'z'
19335}), 'calc', 'from-root');
19336
19337},{"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../annotations/attributes":35}],45:[function(_dereq_,module,exports){
19338/**
19339* Copyright 2012-2020, Plotly, Inc.
19340* All rights reserved.
19341*
19342* This source code is licensed under the MIT license found in the
19343* LICENSE file in the root directory of this source tree.
19344*/
19345
19346'use strict';
19347
19348var Lib = _dereq_('../../lib');
19349var Axes = _dereq_('../../plots/cartesian/axes');
19350
19351module.exports = function convert(scene) {
19352 var fullSceneLayout = scene.fullSceneLayout;
19353 var anns = fullSceneLayout.annotations;
19354
19355 for(var i = 0; i < anns.length; i++) {
19356 mockAnnAxes(anns[i], scene);
19357 }
19358
19359 scene.fullLayout._infolayer
19360 .selectAll('.annotation-' + scene.id)
19361 .remove();
19362};
19363
19364function mockAnnAxes(ann, scene) {
19365 var fullSceneLayout = scene.fullSceneLayout;
19366 var domain = fullSceneLayout.domain;
19367 var size = scene.fullLayout._size;
19368
19369 var base = {
19370 // this gets fill in on render
19371 pdata: null,
19372
19373 // to get setConvert to not execute cleanly
19374 type: 'linear',
19375
19376 // don't try to update them on `editable: true`
19377 autorange: false,
19378
19379 // set infinite range so that annotation draw routine
19380 // does not try to remove 'outside-range' annotations,
19381 // this case is handled in the render loop
19382 range: [-Infinity, Infinity]
19383 };
19384
19385 ann._xa = {};
19386 Lib.extendFlat(ann._xa, base);
19387 Axes.setConvert(ann._xa);
19388 ann._xa._offset = size.l + domain.x[0] * size.w;
19389 ann._xa.l2p = function() {
19390 return 0.5 * (1 + ann._pdata[0] / ann._pdata[3]) * size.w * (domain.x[1] - domain.x[0]);
19391 };
19392
19393 ann._ya = {};
19394 Lib.extendFlat(ann._ya, base);
19395 Axes.setConvert(ann._ya);
19396 ann._ya._offset = size.t + (1 - domain.y[1]) * size.h;
19397 ann._ya.l2p = function() {
19398 return 0.5 * (1 - ann._pdata[1] / ann._pdata[3]) * size.h * (domain.y[1] - domain.y[0]);
19399 };
19400}
19401
19402},{"../../lib":177,"../../plots/cartesian/axes":222}],46:[function(_dereq_,module,exports){
19403/**
19404* Copyright 2012-2020, Plotly, Inc.
19405* All rights reserved.
19406*
19407* This source code is licensed under the MIT license found in the
19408* LICENSE file in the root directory of this source tree.
19409*/
19410
19411'use strict';
19412
19413var Lib = _dereq_('../../lib');
19414var Axes = _dereq_('../../plots/cartesian/axes');
19415var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
19416var handleAnnotationCommonDefaults = _dereq_('../annotations/common_defaults');
19417var attributes = _dereq_('./attributes');
19418
19419module.exports = function handleDefaults(sceneLayoutIn, sceneLayoutOut, opts) {
19420 handleArrayContainerDefaults(sceneLayoutIn, sceneLayoutOut, {
19421 name: 'annotations',
19422 handleItemDefaults: handleAnnotationDefaults,
19423 fullLayout: opts.fullLayout
19424 });
19425};
19426
19427function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) {
19428 function coerce(attr, dflt) {
19429 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
19430 }
19431
19432 function coercePosition(axLetter) {
19433 var axName = axLetter + 'axis';
19434
19435 // mock in such way that getFromId grabs correct 3D axis
19436 var gdMock = { _fullLayout: {} };
19437 gdMock._fullLayout[axName] = sceneLayout[axName];
19438
19439 return Axes.coercePosition(annOut, gdMock, coerce, axLetter, axLetter, 0.5);
19440 }
19441
19442
19443 var visible = coerce('visible');
19444 if(!visible) return;
19445
19446 handleAnnotationCommonDefaults(annIn, annOut, opts.fullLayout, coerce);
19447
19448 coercePosition('x');
19449 coercePosition('y');
19450 coercePosition('z');
19451
19452 // if you have one coordinate you should all three
19453 Lib.noneOrAll(annIn, annOut, ['x', 'y', 'z']);
19454
19455 // hard-set here for completeness
19456 annOut.xref = 'x';
19457 annOut.yref = 'y';
19458 annOut.zref = 'z';
19459
19460 coerce('xanchor');
19461 coerce('yanchor');
19462 coerce('xshift');
19463 coerce('yshift');
19464
19465 if(annOut.showarrow) {
19466 annOut.axref = 'pixel';
19467 annOut.ayref = 'pixel';
19468
19469 // TODO maybe default values should be bigger than the 2D case?
19470 coerce('ax', -10);
19471 coerce('ay', -30);
19472
19473 // if you have one part of arrow length you should have both
19474 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
19475 }
19476}
19477
19478},{"../../lib":177,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"../annotations/common_defaults":38,"./attributes":44}],47:[function(_dereq_,module,exports){
19479/**
19480* Copyright 2012-2020, Plotly, Inc.
19481* All rights reserved.
19482*
19483* This source code is licensed under the MIT license found in the
19484* LICENSE file in the root directory of this source tree.
19485*/
19486
19487'use strict';
19488
19489var drawRaw = _dereq_('../annotations/draw').drawRaw;
19490var project = _dereq_('../../plots/gl3d/project');
19491var axLetters = ['x', 'y', 'z'];
19492
19493module.exports = function draw(scene) {
19494 var fullSceneLayout = scene.fullSceneLayout;
19495 var dataScale = scene.dataScale;
19496 var anns = fullSceneLayout.annotations;
19497
19498 for(var i = 0; i < anns.length; i++) {
19499 var ann = anns[i];
19500 var annotationIsOffscreen = false;
19501
19502 for(var j = 0; j < 3; j++) {
19503 var axLetter = axLetters[j];
19504 var pos = ann[axLetter];
19505 var ax = fullSceneLayout[axLetter + 'axis'];
19506 var posFraction = ax.r2fraction(pos);
19507
19508 if(posFraction < 0 || posFraction > 1) {
19509 annotationIsOffscreen = true;
19510 break;
19511 }
19512 }
19513
19514 if(annotationIsOffscreen) {
19515 scene.fullLayout._infolayer
19516 .select('.annotation-' + scene.id + '[data-index="' + i + '"]')
19517 .remove();
19518 } else {
19519 ann._pdata = project(scene.glplot.cameraParams, [
19520 fullSceneLayout.xaxis.r2l(ann.x) * dataScale[0],
19521 fullSceneLayout.yaxis.r2l(ann.y) * dataScale[1],
19522 fullSceneLayout.zaxis.r2l(ann.z) * dataScale[2]
19523 ]);
19524
19525 drawRaw(scene.graphDiv, ann, i, scene.id, ann._xa, ann._ya);
19526 }
19527 }
19528};
19529
19530},{"../../plots/gl3d/project":260,"../annotations/draw":41}],48:[function(_dereq_,module,exports){
19531/**
19532* Copyright 2012-2020, Plotly, Inc.
19533* All rights reserved.
19534*
19535* This source code is licensed under the MIT license found in the
19536* LICENSE file in the root directory of this source tree.
19537*/
19538
19539'use strict';
19540
19541var Registry = _dereq_('../../registry');
19542var Lib = _dereq_('../../lib');
19543
19544module.exports = {
19545 moduleType: 'component',
19546 name: 'annotations3d',
19547
19548 schema: {
19549 subplots: {
19550 scene: {annotations: _dereq_('./attributes')}
19551 }
19552 },
19553
19554 layoutAttributes: _dereq_('./attributes'),
19555 handleDefaults: _dereq_('./defaults'),
19556 includeBasePlot: includeGL3D,
19557
19558 convert: _dereq_('./convert'),
19559 draw: _dereq_('./draw')
19560};
19561
19562function includeGL3D(layoutIn, layoutOut) {
19563 var GL3D = Registry.subplotsRegistry.gl3d;
19564 if(!GL3D) return;
19565
19566 var attrRegex = GL3D.attrRegex;
19567
19568 var keys = Object.keys(layoutIn);
19569 for(var i = 0; i < keys.length; i++) {
19570 var k = keys[i];
19571 if(attrRegex.test(k) && (layoutIn[k].annotations || []).length) {
19572 Lib.pushUnique(layoutOut._basePlotModules, GL3D);
19573 Lib.pushUnique(layoutOut._subplots.gl3d, k);
19574 }
19575 }
19576}
19577
19578},{"../../lib":177,"../../registry":272,"./attributes":44,"./convert":45,"./defaults":46,"./draw":47}],49:[function(_dereq_,module,exports){
19579/**
19580* Copyright 2012-2020, Plotly, Inc.
19581* All rights reserved.
19582*
19583* This source code is licensed under the MIT license found in the
19584* LICENSE file in the root directory of this source tree.
19585*/
19586
19587'use strict';
19588
19589
19590// IMPORTANT - default colors should be in hex for compatibility
19591exports.defaults = [
19592 '#1f77b4', // muted blue
19593 '#ff7f0e', // safety orange
19594 '#2ca02c', // cooked asparagus green
19595 '#d62728', // brick red
19596 '#9467bd', // muted purple
19597 '#8c564b', // chestnut brown
19598 '#e377c2', // raspberry yogurt pink
19599 '#7f7f7f', // middle gray
19600 '#bcbd22', // curry yellow-green
19601 '#17becf' // blue-teal
19602];
19603
19604exports.defaultLine = '#444';
19605
19606exports.lightLine = '#eee';
19607
19608exports.background = '#fff';
19609
19610exports.borderLine = '#BEC8D9';
19611
19612// with axis.color and Color.interp we aren't using lightLine
19613// itself anymore, instead interpolating between axis.color
19614// and the background color using tinycolor.mix. lightFraction
19615// gives back exactly lightLine if the other colors are defaults.
19616exports.lightFraction = 100 * (0xe - 0x4) / (0xf - 0x4);
19617
19618},{}],50:[function(_dereq_,module,exports){
19619/**
19620* Copyright 2012-2020, Plotly, Inc.
19621* All rights reserved.
19622*
19623* This source code is licensed under the MIT license found in the
19624* LICENSE file in the root directory of this source tree.
19625*/
19626
19627
19628'use strict';
19629
19630var tinycolor = _dereq_('tinycolor2');
19631var isNumeric = _dereq_('fast-isnumeric');
19632
19633var color = module.exports = {};
19634
19635var colorAttrs = _dereq_('./attributes');
19636color.defaults = colorAttrs.defaults;
19637var defaultLine = color.defaultLine = colorAttrs.defaultLine;
19638color.lightLine = colorAttrs.lightLine;
19639var background = color.background = colorAttrs.background;
19640
19641/*
19642 * tinyRGB: turn a tinycolor into an rgb string, but
19643 * unlike the built-in tinycolor.toRgbString this never includes alpha
19644 */
19645color.tinyRGB = function(tc) {
19646 var c = tc.toRgb();
19647 return 'rgb(' + Math.round(c.r) + ', ' +
19648 Math.round(c.g) + ', ' + Math.round(c.b) + ')';
19649};
19650
19651color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); };
19652
19653color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; };
19654
19655color.addOpacity = function(cstr, op) {
19656 var c = tinycolor(cstr).toRgb();
19657 return 'rgba(' + Math.round(c.r) + ', ' +
19658 Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')';
19659};
19660
19661// combine two colors into one apparent color
19662// if back has transparency or is missing,
19663// color.background is assumed behind it
19664color.combine = function(front, back) {
19665 var fc = tinycolor(front).toRgb();
19666 if(fc.a === 1) return tinycolor(front).toRgbString();
19667
19668 var bc = tinycolor(back || background).toRgb();
19669 var bcflat = bc.a === 1 ? bc : {
19670 r: 255 * (1 - bc.a) + bc.r * bc.a,
19671 g: 255 * (1 - bc.a) + bc.g * bc.a,
19672 b: 255 * (1 - bc.a) + bc.b * bc.a
19673 };
19674 var fcflat = {
19675 r: bcflat.r * (1 - fc.a) + fc.r * fc.a,
19676 g: bcflat.g * (1 - fc.a) + fc.g * fc.a,
19677 b: bcflat.b * (1 - fc.a) + fc.b * fc.a
19678 };
19679 return tinycolor(fcflat).toRgbString();
19680};
19681
19682/*
19683 * Create a color that contrasts with cstr.
19684 *
19685 * If cstr is a dark color, we lighten it; if it's light, we darken.
19686 *
19687 * If lightAmount / darkAmount are used, we adjust by these percentages,
19688 * otherwise we go all the way to white or black.
19689 */
19690color.contrast = function(cstr, lightAmount, darkAmount) {
19691 var tc = tinycolor(cstr);
19692
19693 if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background));
19694
19695 var newColor = tc.isDark() ?
19696 (lightAmount ? tc.lighten(lightAmount) : background) :
19697 (darkAmount ? tc.darken(darkAmount) : defaultLine);
19698
19699 return newColor.toString();
19700};
19701
19702color.stroke = function(s, c) {
19703 var tc = tinycolor(c);
19704 s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()});
19705};
19706
19707color.fill = function(s, c) {
19708 var tc = tinycolor(c);
19709 s.style({
19710 'fill': color.tinyRGB(tc),
19711 'fill-opacity': tc.getAlpha()
19712 });
19713};
19714
19715// search container for colors with the deprecated rgb(fractions) format
19716// and convert them to rgb(0-255 values)
19717color.clean = function(container) {
19718 if(!container || typeof container !== 'object') return;
19719
19720 var keys = Object.keys(container);
19721 var i, j, key, val;
19722
19723 for(i = 0; i < keys.length; i++) {
19724 key = keys[i];
19725 val = container[key];
19726
19727 if(key.substr(key.length - 5) === 'color') {
19728 // only sanitize keys that end in "color" or "colorscale"
19729
19730 if(Array.isArray(val)) {
19731 for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]);
19732 } else container[key] = cleanOne(val);
19733 } else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) {
19734 // colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]]
19735
19736 for(j = 0; j < val.length; j++) {
19737 if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]);
19738 }
19739 } else if(Array.isArray(val)) {
19740 // recurse into arrays of objects, and plain objects
19741
19742 var el0 = val[0];
19743 if(!Array.isArray(el0) && el0 && typeof el0 === 'object') {
19744 for(j = 0; j < val.length; j++) color.clean(val[j]);
19745 }
19746 } else if(val && typeof val === 'object') color.clean(val);
19747 }
19748};
19749
19750function cleanOne(val) {
19751 if(isNumeric(val) || typeof val !== 'string') return val;
19752
19753 var valTrim = val.trim();
19754 if(valTrim.substr(0, 3) !== 'rgb') return val;
19755
19756 var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/);
19757 if(!match) return val;
19758
19759 var parts = match[1].trim().split(/\s*[\s,]\s*/);
19760 var rgba = valTrim.charAt(3) === 'a' && parts.length === 4;
19761 if(!rgba && parts.length !== 3) return val;
19762
19763 for(var i = 0; i < parts.length; i++) {
19764 if(!parts[i].length) return val;
19765 parts[i] = Number(parts[i]);
19766
19767 if(!(parts[i] >= 0)) {
19768 // all parts must be non-negative numbers
19769
19770 return val;
19771 }
19772
19773 if(i === 3) {
19774 // alpha>1 gets clipped to 1
19775
19776 if(parts[i] > 1) parts[i] = 1;
19777 } else if(parts[i] >= 1) {
19778 // r, g, b must be < 1 (ie 1 itself is not allowed)
19779
19780 return val;
19781 }
19782 }
19783
19784 var rgbStr = Math.round(parts[0] * 255) + ', ' +
19785 Math.round(parts[1] * 255) + ', ' +
19786 Math.round(parts[2] * 255);
19787
19788 if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')';
19789 return 'rgb(' + rgbStr + ')';
19790}
19791
19792},{"./attributes":49,"fast-isnumeric":15,"tinycolor2":32}],51:[function(_dereq_,module,exports){
19793/**
19794* Copyright 2012-2020, Plotly, Inc.
19795* All rights reserved.
19796*
19797* This source code is licensed under the MIT license found in the
19798* LICENSE file in the root directory of this source tree.
19799*/
19800
19801'use strict';
19802
19803var axesAttrs = _dereq_('../../plots/cartesian/layout_attributes');
19804var fontAttrs = _dereq_('../../plots/font_attributes');
19805var extendFlat = _dereq_('../../lib/extend').extendFlat;
19806var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
19807
19808
19809module.exports = overrideAll({
19810// TODO: only right is supported currently
19811// orient: {
19812// valType: 'enumerated',
19813//
19814// values: ['left', 'right', 'top', 'bottom'],
19815// dflt: 'right',
19816//
19817// },
19818 thicknessmode: {
19819 valType: 'enumerated',
19820 values: ['fraction', 'pixels'],
19821
19822 dflt: 'pixels',
19823
19824 },
19825 thickness: {
19826 valType: 'number',
19827
19828 min: 0,
19829 dflt: 30,
19830
19831 },
19832 lenmode: {
19833 valType: 'enumerated',
19834 values: ['fraction', 'pixels'],
19835
19836 dflt: 'fraction',
19837
19838 },
19839 len: {
19840 valType: 'number',
19841 min: 0,
19842 dflt: 1,
19843
19844
19845 },
19846 x: {
19847 valType: 'number',
19848 dflt: 1.02,
19849 min: -2,
19850 max: 3,
19851
19852
19853 },
19854 xanchor: {
19855 valType: 'enumerated',
19856 values: ['left', 'center', 'right'],
19857 dflt: 'left',
19858
19859
19860 },
19861 xpad: {
19862 valType: 'number',
19863
19864 min: 0,
19865 dflt: 10,
19866
19867 },
19868 y: {
19869 valType: 'number',
19870
19871 dflt: 0.5,
19872 min: -2,
19873 max: 3,
19874
19875 },
19876 yanchor: {
19877 valType: 'enumerated',
19878 values: ['top', 'middle', 'bottom'],
19879
19880 dflt: 'middle',
19881
19882 },
19883 ypad: {
19884 valType: 'number',
19885
19886 min: 0,
19887 dflt: 10,
19888
19889 },
19890 // a possible line around the bar itself
19891 outlinecolor: axesAttrs.linecolor,
19892 outlinewidth: axesAttrs.linewidth,
19893 // Should outlinewidth have {dflt: 0} ?
19894 // another possible line outside the padding and tick labels
19895 bordercolor: axesAttrs.linecolor,
19896 borderwidth: {
19897 valType: 'number',
19898
19899 min: 0,
19900 dflt: 0,
19901
19902 },
19903 bgcolor: {
19904 valType: 'color',
19905
19906 dflt: 'rgba(0,0,0,0)',
19907
19908 },
19909 // tick and title properties named and function exactly as in axes
19910 tickmode: axesAttrs.tickmode,
19911 nticks: axesAttrs.nticks,
19912 tick0: axesAttrs.tick0,
19913 dtick: axesAttrs.dtick,
19914 tickvals: axesAttrs.tickvals,
19915 ticktext: axesAttrs.ticktext,
19916 ticks: extendFlat({}, axesAttrs.ticks, {dflt: ''}),
19917 ticklen: axesAttrs.ticklen,
19918 tickwidth: axesAttrs.tickwidth,
19919 tickcolor: axesAttrs.tickcolor,
19920 showticklabels: axesAttrs.showticklabels,
19921 tickfont: fontAttrs({
19922
19923 }),
19924 tickangle: axesAttrs.tickangle,
19925 tickformat: axesAttrs.tickformat,
19926 tickformatstops: axesAttrs.tickformatstops,
19927 tickprefix: axesAttrs.tickprefix,
19928 showtickprefix: axesAttrs.showtickprefix,
19929 ticksuffix: axesAttrs.ticksuffix,
19930 showticksuffix: axesAttrs.showticksuffix,
19931 separatethousands: axesAttrs.separatethousands,
19932 exponentformat: axesAttrs.exponentformat,
19933 showexponent: axesAttrs.showexponent,
19934 title: {
19935 text: {
19936 valType: 'string',
19937
19938
19939 },
19940 font: fontAttrs({
19941
19942 }),
19943 side: {
19944 valType: 'enumerated',
19945 values: ['right', 'top', 'bottom'],
19946
19947 dflt: 'top',
19948
19949 }
19950 },
19951
19952 _deprecated: {
19953 title: {
19954 valType: 'string',
19955
19956
19957 },
19958 titlefont: fontAttrs({
19959
19960 }),
19961 titleside: {
19962 valType: 'enumerated',
19963 values: ['right', 'top', 'bottom'],
19964
19965 dflt: 'top',
19966
19967 }
19968 }
19969}, 'colorbars', 'from-root');
19970
19971},{"../../lib/extend":170,"../../plot_api/edit_types":205,"../../plots/cartesian/layout_attributes":236,"../../plots/font_attributes":250}],52:[function(_dereq_,module,exports){
19972/**
19973* Copyright 2012-2020, Plotly, Inc.
19974* All rights reserved.
19975*
19976* This source code is licensed under the MIT license found in the
19977* LICENSE file in the root directory of this source tree.
19978*/
19979
19980'use strict';
19981
19982module.exports = {
19983 cn: {
19984 colorbar: 'colorbar',
19985 cbbg: 'cbbg',
19986 cbfill: 'cbfill',
19987 cbfills: 'cbfills',
19988 cbline: 'cbline',
19989 cblines: 'cblines',
19990 cbaxis: 'cbaxis',
19991 cbtitleunshift: 'cbtitleunshift',
19992 cbtitle: 'cbtitle',
19993 cboutline: 'cboutline',
19994 crisp: 'crisp',
19995 jsPlaceholder: 'js-placeholder'
19996 }
19997};
19998
19999},{}],53:[function(_dereq_,module,exports){
20000/**
20001* Copyright 2012-2020, Plotly, Inc.
20002* All rights reserved.
20003*
20004* This source code is licensed under the MIT license found in the
20005* LICENSE file in the root directory of this source tree.
20006*/
20007
20008
20009'use strict';
20010
20011var Lib = _dereq_('../../lib');
20012var Template = _dereq_('../../plot_api/plot_template');
20013
20014var handleTickValueDefaults = _dereq_('../../plots/cartesian/tick_value_defaults');
20015var handleTickMarkDefaults = _dereq_('../../plots/cartesian/tick_mark_defaults');
20016var handleTickLabelDefaults = _dereq_('../../plots/cartesian/tick_label_defaults');
20017
20018var attributes = _dereq_('./attributes');
20019
20020module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
20021 var colorbarOut = Template.newContainer(containerOut, 'colorbar');
20022 var colorbarIn = containerIn.colorbar || {};
20023
20024 function coerce(attr, dflt) {
20025 return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt);
20026 }
20027
20028 var thicknessmode = coerce('thicknessmode');
20029 coerce('thickness', (thicknessmode === 'fraction') ?
20030 30 / (layout.width - layout.margin.l - layout.margin.r) :
20031 30
20032 );
20033
20034 var lenmode = coerce('lenmode');
20035 coerce('len', (lenmode === 'fraction') ?
20036 1 :
20037 layout.height - layout.margin.t - layout.margin.b
20038 );
20039
20040 coerce('x');
20041 coerce('xanchor');
20042 coerce('xpad');
20043 coerce('y');
20044 coerce('yanchor');
20045 coerce('ypad');
20046 Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']);
20047
20048 coerce('outlinecolor');
20049 coerce('outlinewidth');
20050 coerce('bordercolor');
20051 coerce('borderwidth');
20052 coerce('bgcolor');
20053
20054 handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');
20055
20056 var opts = {outerTicks: false, font: layout.font};
20057 handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
20058 handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
20059
20060 coerce('title.text', layout._dfltTitle.colorbar);
20061 Lib.coerceFont(coerce, 'title.font', layout.font);
20062 coerce('title.side');
20063};
20064
20065},{"../../lib":177,"../../plot_api/plot_template":212,"../../plots/cartesian/tick_label_defaults":243,"../../plots/cartesian/tick_mark_defaults":244,"../../plots/cartesian/tick_value_defaults":245,"./attributes":51}],54:[function(_dereq_,module,exports){
20066/**
20067* Copyright 2012-2020, Plotly, Inc.
20068* All rights reserved.
20069*
20070* This source code is licensed under the MIT license found in the
20071* LICENSE file in the root directory of this source tree.
20072*/
20073
20074'use strict';
20075
20076var d3 = _dereq_('d3');
20077var tinycolor = _dereq_('tinycolor2');
20078
20079var Plots = _dereq_('../../plots/plots');
20080var Registry = _dereq_('../../registry');
20081var Axes = _dereq_('../../plots/cartesian/axes');
20082var dragElement = _dereq_('../dragelement');
20083var Lib = _dereq_('../../lib');
20084var extendFlat = _dereq_('../../lib/extend').extendFlat;
20085var setCursor = _dereq_('../../lib/setcursor');
20086var Drawing = _dereq_('../drawing');
20087var Color = _dereq_('../color');
20088var Titles = _dereq_('../titles');
20089var svgTextUtils = _dereq_('../../lib/svg_text_utils');
20090var flipScale = _dereq_('../colorscale/helpers').flipScale;
20091
20092var handleAxisDefaults = _dereq_('../../plots/cartesian/axis_defaults');
20093var handleAxisPositionDefaults = _dereq_('../../plots/cartesian/position_defaults');
20094var axisLayoutAttrs = _dereq_('../../plots/cartesian/layout_attributes');
20095
20096var alignmentConstants = _dereq_('../../constants/alignment');
20097var LINE_SPACING = alignmentConstants.LINE_SPACING;
20098var FROM_TL = alignmentConstants.FROM_TL;
20099var FROM_BR = alignmentConstants.FROM_BR;
20100
20101var cn = _dereq_('./constants').cn;
20102
20103function draw(gd) {
20104 var fullLayout = gd._fullLayout;
20105
20106 var colorBars = fullLayout._infolayer
20107 .selectAll('g.' + cn.colorbar)
20108 .data(makeColorBarData(gd), function(opts) { return opts._id; });
20109
20110 colorBars.enter().append('g')
20111 .attr('class', function(opts) { return opts._id; })
20112 .classed(cn.colorbar, true);
20113
20114 colorBars.each(function(opts) {
20115 var g = d3.select(this);
20116
20117 Lib.ensureSingle(g, 'rect', cn.cbbg);
20118 Lib.ensureSingle(g, 'g', cn.cbfills);
20119 Lib.ensureSingle(g, 'g', cn.cblines);
20120 Lib.ensureSingle(g, 'g', cn.cbaxis, function(s) { s.classed(cn.crisp, true); });
20121 Lib.ensureSingle(g, 'g', cn.cbtitleunshift, function(s) { s.append('g').classed(cn.cbtitle, true); });
20122 Lib.ensureSingle(g, 'rect', cn.cboutline);
20123
20124 var done = drawColorBar(g, opts, gd);
20125 if(done && done.then) (gd._promises || []).push(done);
20126
20127 if(gd._context.edits.colorbarPosition) {
20128 makeEditable(g, opts, gd);
20129 }
20130 });
20131
20132 colorBars.exit()
20133 .each(function(opts) { Plots.autoMargin(gd, opts._id); })
20134 .remove();
20135
20136 colorBars.order();
20137}
20138
20139function makeColorBarData(gd) {
20140 var fullLayout = gd._fullLayout;
20141 var calcdata = gd.calcdata;
20142 var out = [];
20143
20144 // single out item
20145 var opts;
20146 // colorbar attr parent container
20147 var cont;
20148 // trace attr container
20149 var trace;
20150 // colorbar options
20151 var cbOpt;
20152
20153 function initOpts(opts) {
20154 return extendFlat(opts, {
20155 // fillcolor can be a d3 scale, domain is z values, range is colors
20156 // or leave it out for no fill,
20157 // or set to a string constant for single-color fill
20158 _fillcolor: null,
20159 // line.color has the same options as fillcolor
20160 _line: {color: null, width: null, dash: null},
20161 // levels of lines to draw.
20162 // note that this DOES NOT determine the extent of the bar
20163 // that's given by the domain of fillcolor
20164 // (or line.color if no fillcolor domain)
20165 _levels: {start: null, end: null, size: null},
20166 // separate fill levels (for example, heatmap coloring of a
20167 // contour map) if this is omitted, fillcolors will be
20168 // evaluated halfway between levels
20169 _filllevels: null,
20170 // for continuous colorscales: fill with a gradient instead of explicit levels
20171 // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
20172 _fillgradient: null,
20173 // when using a gradient, we need the data range specified separately
20174 _zrange: null
20175 });
20176 }
20177
20178 function calcOpts() {
20179 if(typeof cbOpt.calc === 'function') {
20180 cbOpt.calc(gd, trace, opts);
20181 } else {
20182 opts._fillgradient = cont.reversescale ?
20183 flipScale(cont.colorscale) :
20184 cont.colorscale;
20185 opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]];
20186 }
20187 }
20188
20189 for(var i = 0; i < calcdata.length; i++) {
20190 var cd = calcdata[i];
20191 trace = cd[0].trace;
20192 var moduleOpts = trace._module.colorbar;
20193
20194 if(trace.visible === true && moduleOpts) {
20195 var allowsMultiplotCbs = Array.isArray(moduleOpts);
20196 var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts];
20197
20198 for(var j = 0; j < cbOpts.length; j++) {
20199 cbOpt = cbOpts[j];
20200 var contName = cbOpt.container;
20201 cont = contName ? trace[contName] : trace;
20202
20203 if(cont && cont.showscale) {
20204 opts = initOpts(cont.colorbar);
20205 opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : '');
20206 opts._traceIndex = trace.index;
20207 opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.';
20208 opts._meta = trace._meta;
20209 calcOpts();
20210 out.push(opts);
20211 }
20212 }
20213 }
20214 }
20215
20216 for(var k in fullLayout._colorAxes) {
20217 cont = fullLayout[k];
20218
20219 if(cont.showscale) {
20220 var colorAxOpts = fullLayout._colorAxes[k];
20221
20222 opts = initOpts(cont.colorbar);
20223 opts._id = 'cb' + k;
20224 opts._propPrefix = k + '.colorbar.';
20225 opts._meta = fullLayout._meta;
20226
20227 cbOpt = {min: 'cmin', max: 'cmax'};
20228 if(colorAxOpts[0] !== 'heatmap') {
20229 trace = colorAxOpts[1];
20230 cbOpt.calc = trace._module.colorbar.calc;
20231 }
20232
20233 calcOpts();
20234 out.push(opts);
20235 }
20236 }
20237
20238 return out;
20239}
20240
20241function drawColorBar(g, opts, gd) {
20242 var fullLayout = gd._fullLayout;
20243 var gs = fullLayout._size;
20244
20245 var fillColor = opts._fillcolor;
20246 var line = opts._line;
20247 var title = opts.title;
20248 var titleSide = title.side;
20249
20250 var zrange = opts._zrange ||
20251 d3.extent((typeof fillColor === 'function' ? fillColor : line.color).domain());
20252
20253 var lineColormap = typeof line.color === 'function' ?
20254 line.color :
20255 function() { return line.color; };
20256 var fillColormap = typeof fillColor === 'function' ?
20257 fillColor :
20258 function() { return fillColor; };
20259
20260 var levelsIn = opts._levels;
20261 var levelsOut = calcLevels(gd, opts, zrange);
20262 var fillLevels = levelsOut.fill;
20263 var lineLevels = levelsOut.line;
20264
20265 // we calculate pixel sizes based on the specified graph size,
20266 // not the actual (in case something pushed the margins around)
20267 // which is a little odd but avoids an odd iterative effect
20268 // when the colorbar itself is pushing the margins.
20269 // but then the fractional size is calculated based on the
20270 // actual graph size, so that the axes will size correctly.
20271 var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? gs.w : 1));
20272 var thickFrac = thickPx / gs.w;
20273 var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? gs.h : 1));
20274 var lenFrac = lenPx / gs.h;
20275 var xpadFrac = opts.xpad / gs.w;
20276 var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2;
20277 var ypadFrac = opts.ypad / gs.h;
20278
20279 // x positioning: do it initially just for left anchor,
20280 // then fix at the end (since we don't know the width yet)
20281 var xLeft = Math.round(opts.x * gs.w + opts.xpad);
20282 // for dragging... this is getting a little muddled...
20283 var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor] || 0);
20284
20285 // y positioning we can do correctly from the start
20286 var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5);
20287 var yBottomPx = Math.round(gs.h * (1 - yBottomFrac));
20288 var yTopPx = yBottomPx - lenPx;
20289
20290 // stash a few things for makeEditable
20291 opts._lenFrac = lenFrac;
20292 opts._thickFrac = thickFrac;
20293 opts._xLeftFrac = xLeftFrac;
20294 opts._yBottomFrac = yBottomFrac;
20295
20296 // stash mocked axis for contour label formatting
20297 var ax = opts._axis = mockColorBarAxis(gd, opts, zrange);
20298
20299 // position can't go in through supplyDefaults
20300 // because that restricts it to [0,1]
20301 ax.position = opts.x + xpadFrac + thickFrac;
20302
20303 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
20304 ax.title.side = titleSide;
20305 ax.titlex = opts.x + xpadFrac;
20306 ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac);
20307 }
20308
20309 if(line.color && opts.tickmode === 'auto') {
20310 ax.tickmode = 'linear';
20311 ax.tick0 = levelsIn.start;
20312 var dtick = levelsIn.size;
20313 // expand if too many contours, so we don't get too many ticks
20314 var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1;
20315 var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick);
20316 if(dtFactor > 1) {
20317 var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10));
20318 dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]);
20319 // if the contours are at round multiples, reset tick0
20320 // so they're still at round multiples. Otherwise,
20321 // keep the first label on the first contour level
20322 if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) {
20323 ax.tick0 = 0;
20324 }
20325 }
20326 ax.dtick = dtick;
20327 }
20328
20329 // set domain after init, because we may want to
20330 // allow it outside [0,1]
20331 ax.domain = [
20332 yBottomFrac + ypadFrac,
20333 yBottomFrac + lenFrac - ypadFrac
20334 ];
20335
20336 ax.setScale();
20337
20338 g.attr('transform', 'translate(' + Math.round(gs.l) + ',' + Math.round(gs.t) + ')');
20339
20340 var titleCont = g.select('.' + cn.cbtitleunshift)
20341 .attr('transform', 'translate(-' + Math.round(gs.l) + ',-' + Math.round(gs.t) + ')');
20342
20343 var axLayer = g.select('.' + cn.cbaxis);
20344 var titleEl;
20345 var titleHeight = 0;
20346
20347 function drawTitle(titleClass, titleOpts) {
20348 var dfltTitleOpts = {
20349 propContainer: ax,
20350 propName: opts._propPrefix + 'title',
20351 traceIndex: opts._traceIndex,
20352 _meta: opts._meta,
20353 placeholder: fullLayout._dfltTitle.colorbar,
20354 containerGroup: g.select('.' + cn.cbtitle)
20355 };
20356
20357 // this class-to-rotate thing with convertToTspans is
20358 // getting hackier and hackier... delete groups with the
20359 // wrong class (in case earlier the colorbar was drawn on
20360 // a different side, I think?)
20361 var otherClass = titleClass.charAt(0) === 'h' ?
20362 titleClass.substr(1) :
20363 'h' + titleClass;
20364 g.selectAll('.' + otherClass + ',.' + otherClass + '-math-group').remove();
20365
20366 Titles.draw(gd, titleClass, extendFlat(dfltTitleOpts, titleOpts || {}));
20367 }
20368
20369 function drawDummyTitle() {
20370 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
20371 // draw the title so we know how much room it needs
20372 // when we squish the axis. This one only applies to
20373 // top or bottom titles, not right side.
20374 var x = gs.l + (opts.x + xpadFrac) * gs.w;
20375 var fontSize = ax.title.font.size;
20376 var y;
20377
20378 if(titleSide === 'top') {
20379 y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h +
20380 gs.t + 3 + fontSize * 0.75;
20381 } else {
20382 y = (1 - (yBottomFrac + ypadFrac)) * gs.h +
20383 gs.t - 3 - fontSize * 0.25;
20384 }
20385 drawTitle(ax._id + 'title', {
20386 attributes: {x: x, y: y, 'text-anchor': 'start'}
20387 });
20388 }
20389 }
20390
20391 function drawCbTitle() {
20392 if(['top', 'bottom'].indexOf(titleSide) === -1) {
20393 var fontSize = ax.title.font.size;
20394 var y = ax._offset + ax._length / 2;
20395 var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ?
20396 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) :
20397 -10 - fontSize * ((ax.showticklabels ? 0.5 : 0)));
20398
20399 // the 'h' + is a hack to get around the fact that
20400 // convertToTspans rotates any 'y...' class by 90 degrees.
20401 // TODO: find a better way to control this.
20402 drawTitle('h' + ax._id + 'title', {
20403 avoid: {
20404 selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'),
20405 side: titleSide,
20406 offsetLeft: gs.l,
20407 offsetTop: 0,
20408 maxShift: fullLayout.width
20409 },
20410 attributes: {x: x, y: y, 'text-anchor': 'middle'},
20411 transform: {rotate: '-90', offset: 0}
20412 });
20413 }
20414 }
20415
20416 function drawAxis() {
20417 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
20418 // squish the axis top to make room for the title
20419 var titleGroup = g.select('.' + cn.cbtitle);
20420 var titleText = titleGroup.select('text');
20421 var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2];
20422 var mathJaxNode = titleGroup
20423 .select('.h' + ax._id + 'title-math-group')
20424 .node();
20425 var lineSize = 15.6;
20426 if(titleText.node()) {
20427 lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING;
20428 }
20429 if(mathJaxNode) {
20430 titleHeight = Drawing.bBox(mathJaxNode).height;
20431 if(titleHeight > lineSize) {
20432 // not entirely sure how mathjax is doing
20433 // vertical alignment, but this seems to work.
20434 titleTrans[1] -= (titleHeight - lineSize) / 2;
20435 }
20436 } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) {
20437 titleHeight = Drawing.bBox(titleText.node()).height;
20438 }
20439 if(titleHeight) {
20440 // buffer btwn colorbar and title
20441 // TODO: configurable
20442 titleHeight += 5;
20443
20444 if(titleSide === 'top') {
20445 ax.domain[1] -= titleHeight / gs.h;
20446 titleTrans[1] *= -1;
20447 } else {
20448 ax.domain[0] += titleHeight / gs.h;
20449 var nlines = svgTextUtils.lineCount(titleText);
20450 titleTrans[1] += (1 - nlines) * lineSize;
20451 }
20452
20453 titleGroup.attr('transform', 'translate(' + titleTrans + ')');
20454 ax.setScale();
20455 }
20456 }
20457
20458 g.selectAll('.' + cn.cbfills + ',.' + cn.cblines)
20459 .attr('transform', 'translate(0,' + Math.round(gs.h * (1 - ax.domain[1])) + ')');
20460
20461 axLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')');
20462
20463 var fills = g.select('.' + cn.cbfills)
20464 .selectAll('rect.' + cn.cbfill)
20465 .data(fillLevels);
20466 fills.enter().append('rect')
20467 .classed(cn.cbfill, true)
20468 .style('stroke', 'none');
20469 fills.exit().remove();
20470
20471 var zBounds = zrange
20472 .map(ax.c2p)
20473 .map(Math.round)
20474 .sort(function(a, b) { return a - b; });
20475
20476 fills.each(function(d, i) {
20477 var z = [
20478 (i === 0) ? zrange[0] : (fillLevels[i] + fillLevels[i - 1]) / 2,
20479 (i === fillLevels.length - 1) ? zrange[1] : (fillLevels[i] + fillLevels[i + 1]) / 2
20480 ]
20481 .map(ax.c2p)
20482 .map(Math.round);
20483
20484 // offset the side adjoining the next rectangle so they
20485 // overlap, to prevent antialiasing gaps
20486 z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
20487
20488
20489 // Colorbar cannot currently support opacities so we
20490 // use an opaque fill even when alpha channels present
20491 var fillEl = d3.select(this).attr({
20492 x: xLeft,
20493 width: Math.max(thickPx, 2),
20494 y: d3.min(z),
20495 height: Math.max(d3.max(z) - d3.min(z), 2),
20496 });
20497
20498 if(opts._fillgradient) {
20499 Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill');
20500 } else {
20501 // tinycolor can't handle exponents and
20502 // at this scale, removing it makes no difference.
20503 var colorString = fillColormap(d).replace('e-', '');
20504 fillEl.attr('fill', tinycolor(colorString).toHexString());
20505 }
20506 });
20507
20508 var lines = g.select('.' + cn.cblines)
20509 .selectAll('path.' + cn.cbline)
20510 .data(line.color && line.width ? lineLevels : []);
20511 lines.enter().append('path')
20512 .classed(cn.cbline, true);
20513 lines.exit().remove();
20514 lines.each(function(d) {
20515 d3.select(this)
20516 .attr('d', 'M' + xLeft + ',' +
20517 (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx)
20518 .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash);
20519 });
20520
20521 // force full redraw of labels and ticks
20522 axLayer.selectAll('g.' + ax._id + 'tick,path').remove();
20523
20524 var shift = xLeft + thickPx +
20525 (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0);
20526
20527 var vals = Axes.calcTicks(ax);
20528 var transFn = Axes.makeTransFn(ax);
20529 var tickSign = Axes.getTickSigns(ax)[2];
20530
20531 Axes.drawTicks(gd, ax, {
20532 vals: ax.ticks === 'inside' ? Axes.clipEnds(ax, vals) : vals,
20533 layer: axLayer,
20534 path: Axes.makeTickPath(ax, shift, tickSign),
20535 transFn: transFn
20536 });
20537
20538 return Axes.drawLabels(gd, ax, {
20539 vals: vals,
20540 layer: axLayer,
20541 transFn: transFn,
20542 labelFns: Axes.makeLabelFns(ax, shift)
20543 });
20544 }
20545
20546 // wait for the axis & title to finish rendering before
20547 // continuing positioning
20548 // TODO: why are we redrawing multiple times now with this?
20549 // I guess autoMargin doesn't like being post-promise?
20550 function positionCB() {
20551 var innerWidth = thickPx + opts.outlinewidth / 2 + Drawing.bBox(axLayer.node()).width;
20552 titleEl = titleCont.select('text');
20553
20554 if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) {
20555 var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node();
20556 var titleWidth;
20557 if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) {
20558 titleWidth = Drawing.bBox(mathJaxNode).width;
20559 } else {
20560 // note: the formula below works for all title sides,
20561 // (except for top/bottom mathjax, above)
20562 // but the weird gs.l is because the titleunshift
20563 // transform gets removed by Drawing.bBox
20564 titleWidth = Drawing.bBox(titleCont.node()).right - xLeft - gs.l;
20565 }
20566 innerWidth = Math.max(innerWidth, titleWidth);
20567 }
20568
20569 var outerwidth = 2 * opts.xpad + innerWidth + opts.borderwidth + opts.outlinewidth / 2;
20570 var outerheight = yBottomPx - yTopPx;
20571
20572 g.select('.' + cn.cbbg).attr({
20573 x: xLeft - opts.xpad - (opts.borderwidth + opts.outlinewidth) / 2,
20574 y: yTopPx - yExtraPx,
20575 width: Math.max(outerwidth, 2),
20576 height: Math.max(outerheight + 2 * yExtraPx, 2)
20577 })
20578 .call(Color.fill, opts.bgcolor)
20579 .call(Color.stroke, opts.bordercolor)
20580 .style('stroke-width', opts.borderwidth);
20581
20582 g.selectAll('.' + cn.cboutline).attr({
20583 x: xLeft,
20584 y: yTopPx + opts.ypad + (titleSide === 'top' ? titleHeight : 0),
20585 width: Math.max(thickPx, 2),
20586 height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2)
20587 })
20588 .call(Color.stroke, opts.outlinecolor)
20589 .style({
20590 fill: 'none',
20591 'stroke-width': opts.outlinewidth
20592 });
20593
20594 // fix positioning for xanchor!='left'
20595 var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * outerwidth;
20596 g.attr('transform', 'translate(' + (gs.l - xoffset) + ',' + gs.t + ')');
20597
20598 // auto margin adjustment
20599 var marginOpts = {};
20600 var tFrac = FROM_TL[opts.yanchor];
20601 var bFrac = FROM_BR[opts.yanchor];
20602 if(opts.lenmode === 'pixels') {
20603 marginOpts.y = opts.y;
20604 marginOpts.t = outerheight * tFrac;
20605 marginOpts.b = outerheight * bFrac;
20606 } else {
20607 marginOpts.t = marginOpts.b = 0;
20608 marginOpts.yt = opts.y + opts.len * tFrac;
20609 marginOpts.yb = opts.y - opts.len * bFrac;
20610 }
20611
20612 var lFrac = FROM_TL[opts.xanchor];
20613 var rFrac = FROM_BR[opts.xanchor];
20614 if(opts.thicknessmode === 'pixels') {
20615 marginOpts.x = opts.x;
20616 marginOpts.l = outerwidth * lFrac;
20617 marginOpts.r = outerwidth * rFrac;
20618 } else {
20619 var extraThickness = outerwidth - thickPx;
20620 marginOpts.l = extraThickness * lFrac;
20621 marginOpts.r = extraThickness * rFrac;
20622 marginOpts.xl = opts.x - opts.thickness * lFrac;
20623 marginOpts.xr = opts.x + opts.thickness * rFrac;
20624 }
20625
20626 Plots.autoMargin(gd, opts._id, marginOpts);
20627 }
20628
20629 return Lib.syncOrAsync([
20630 Plots.previousPromises,
20631 drawDummyTitle,
20632 drawAxis,
20633 drawCbTitle,
20634 Plots.previousPromises,
20635 positionCB
20636 ], gd);
20637}
20638
20639function makeEditable(g, opts, gd) {
20640 var fullLayout = gd._fullLayout;
20641 var gs = fullLayout._size;
20642 var t0, xf, yf;
20643
20644 dragElement.init({
20645 element: g.node(),
20646 gd: gd,
20647 prepFn: function() {
20648 t0 = g.attr('transform');
20649 setCursor(g);
20650 },
20651 moveFn: function(dx, dy) {
20652 g.attr('transform', t0 + ' ' + 'translate(' + dx + ',' + dy + ')');
20653
20654 xf = dragElement.align(opts._xLeftFrac + (dx / gs.w), opts._thickFrac,
20655 0, 1, opts.xanchor);
20656 yf = dragElement.align(opts._yBottomFrac - (dy / gs.h), opts._lenFrac,
20657 0, 1, opts.yanchor);
20658
20659 var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor);
20660 setCursor(g, csr);
20661 },
20662 doneFn: function() {
20663 setCursor(g);
20664
20665 if(xf !== undefined && yf !== undefined) {
20666 var update = {};
20667 update[opts._propPrefix + 'x'] = xf;
20668 update[opts._propPrefix + 'y'] = yf;
20669 if(opts._traceIndex !== undefined) {
20670 Registry.call('_guiRestyle', gd, update, opts._traceIndex);
20671 } else {
20672 Registry.call('_guiRelayout', gd, update);
20673 }
20674 }
20675 }
20676 });
20677}
20678
20679function calcLevels(gd, opts, zrange) {
20680 var levelsIn = opts._levels;
20681 var lineLevels = [];
20682 var fillLevels = [];
20683 var l;
20684 var i;
20685
20686 var l0 = levelsIn.end + levelsIn.size / 100;
20687 var ls = levelsIn.size;
20688 var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]);
20689 var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]);
20690
20691 for(i = 0; i < 1e5; i++) {
20692 l = levelsIn.start + i * ls;
20693 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
20694 if(l > zr0 && l < zr1) lineLevels.push(l);
20695 }
20696
20697 if(opts._fillgradient) {
20698 fillLevels = [0];
20699 } else if(typeof opts._fillcolor === 'function') {
20700 var fillLevelsIn = opts._filllevels;
20701
20702 if(fillLevelsIn) {
20703 l0 = fillLevelsIn.end + fillLevelsIn.size / 100;
20704 ls = fillLevelsIn.size;
20705 for(i = 0; i < 1e5; i++) {
20706 l = fillLevelsIn.start + i * ls;
20707 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
20708 if(l > zrange[0] && l < zrange[1]) fillLevels.push(l);
20709 }
20710 } else {
20711 fillLevels = lineLevels.map(function(v) {
20712 return v - levelsIn.size / 2;
20713 });
20714 fillLevels.push(fillLevels[fillLevels.length - 1] + levelsIn.size);
20715 }
20716 } else if(opts._fillcolor && typeof opts._fillcolor === 'string') {
20717 // doesn't matter what this value is, with a single value
20718 // we'll make a single fill rect covering the whole bar
20719 fillLevels = [0];
20720 }
20721
20722 if(levelsIn.size < 0) {
20723 lineLevels.reverse();
20724 fillLevels.reverse();
20725 }
20726
20727 return {line: lineLevels, fill: fillLevels};
20728}
20729
20730function mockColorBarAxis(gd, opts, zrange) {
20731 var fullLayout = gd._fullLayout;
20732
20733 var cbAxisIn = {
20734 type: 'linear',
20735 range: zrange,
20736 tickmode: opts.tickmode,
20737 nticks: opts.nticks,
20738 tick0: opts.tick0,
20739 dtick: opts.dtick,
20740 tickvals: opts.tickvals,
20741 ticktext: opts.ticktext,
20742 ticks: opts.ticks,
20743 ticklen: opts.ticklen,
20744 tickwidth: opts.tickwidth,
20745 tickcolor: opts.tickcolor,
20746 showticklabels: opts.showticklabels,
20747 tickfont: opts.tickfont,
20748 tickangle: opts.tickangle,
20749 tickformat: opts.tickformat,
20750 exponentformat: opts.exponentformat,
20751 separatethousands: opts.separatethousands,
20752 showexponent: opts.showexponent,
20753 showtickprefix: opts.showtickprefix,
20754 tickprefix: opts.tickprefix,
20755 showticksuffix: opts.showticksuffix,
20756 ticksuffix: opts.ticksuffix,
20757 title: opts.title,
20758 showline: true,
20759 anchor: 'free',
20760 side: 'right',
20761 position: 1
20762 };
20763
20764 var cbAxisOut = {
20765 type: 'linear',
20766 _id: 'y' + opts._id
20767 };
20768
20769 var axisOptions = {
20770 letter: 'y',
20771 font: fullLayout.font,
20772 noHover: true,
20773 noTickson: true,
20774 calendar: fullLayout.calendar // not really necessary (yet?)
20775 };
20776
20777 function coerce(attr, dflt) {
20778 return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt);
20779 }
20780
20781 handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout);
20782 handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions);
20783
20784 return cbAxisOut;
20785}
20786
20787module.exports = {
20788 draw: draw
20789};
20790
20791},{"../../constants/alignment":152,"../../lib":177,"../../lib/extend":170,"../../lib/setcursor":196,"../../lib/svg_text_utils":198,"../../plots/cartesian/axes":222,"../../plots/cartesian/axis_defaults":224,"../../plots/cartesian/layout_attributes":236,"../../plots/cartesian/position_defaults":239,"../../plots/plots":263,"../../registry":272,"../color":50,"../colorscale/helpers":61,"../dragelement":69,"../drawing":72,"../titles":145,"./constants":52,"d3":13,"tinycolor2":32}],55:[function(_dereq_,module,exports){
20792/**
20793* Copyright 2012-2020, Plotly, Inc.
20794* All rights reserved.
20795*
20796* This source code is licensed under the MIT license found in the
20797* LICENSE file in the root directory of this source tree.
20798*/
20799
20800
20801'use strict';
20802
20803var Lib = _dereq_('../../lib');
20804
20805
20806module.exports = function hasColorbar(container) {
20807 return Lib.isPlainObject(container.colorbar);
20808};
20809
20810},{"../../lib":177}],56:[function(_dereq_,module,exports){
20811/**
20812* Copyright 2012-2020, Plotly, Inc.
20813* All rights reserved.
20814*
20815* This source code is licensed under the MIT license found in the
20816* LICENSE file in the root directory of this source tree.
20817*/
20818
20819'use strict';
20820
20821module.exports = {
20822 moduleType: 'component',
20823 name: 'colorbar',
20824
20825 attributes: _dereq_('./attributes'),
20826 supplyDefaults: _dereq_('./defaults'),
20827
20828 draw: _dereq_('./draw').draw,
20829 hasColorbar: _dereq_('./has_colorbar')
20830};
20831
20832},{"./attributes":51,"./defaults":53,"./draw":54,"./has_colorbar":55}],57:[function(_dereq_,module,exports){
20833/**
20834* Copyright 2012-2020, Plotly, Inc.
20835* All rights reserved.
20836*
20837* This source code is licensed under the MIT license found in the
20838* LICENSE file in the root directory of this source tree.
20839*/
20840
20841'use strict';
20842
20843var colorbarAttrs = _dereq_('../colorbar/attributes');
20844var counterRegex = _dereq_('../../lib/regex').counter;
20845
20846var palettes = _dereq_('./scales.js').scales;
20847var paletteStr = Object.keys(palettes);
20848
20849function code(s) {
20850 return '`' + s + '`';
20851}
20852
20853/**
20854 * Make colorscale attribute declarations for
20855 *
20856 * - colorscale,
20857 * - (c|z)auto, (c|z)min, (c|z)max,
20858 * - autocolorscale, reversescale,
20859 * - showscale (optionally)
20860 * - color (optionally)
20861 *
20862 * @param {string} context (dflt: '', i.e. from trace root):
20863 * the container this is in ('', *marker*, *marker.line* etc)
20864 *
20865 * @param {object} opts:
20866 * - cLetter {string} (dflt: 'c'):
20867 * leading letter for 'min', 'max and 'auto' attribute (either 'z' or 'c')
20868 *
20869 * - colorAttr {string} (dflt: 'z' if `cLetter: 'z'`, 'color' if `cLetter: 'c'`):
20870 * (for descriptions) sets the name of the color attribute that maps to the colorscale.
20871 *
20872 * N.B. if `colorAttr: 'color'`, we include the `color` declaration here.
20873 *
20874 * - onlyIfNumerical {string} (dflt: false' if `cLetter: 'z'`, true if `cLetter: 'c'`):
20875 * (for descriptions) set to true if colorscale attribute only
20876 *
20877 * - colorscaleDflt {string}:
20878 * overrides the colorscale dflt
20879 *
20880 * - autoColorDflt {boolean} (dflt true):
20881 * normally autocolorscale.dflt is `true`, but pass `false` to override
20882 *
20883 * - noScale {boolean} (dflt: true if `context: 'marker.line'`, false otherwise):
20884 * set to `false` to not include showscale attribute (e.g. for 'marker.line')
20885 *
20886 * - showScaleDflt {boolean} (dflt: true if `cLetter: 'z'`, false otherwise)
20887 *
20888 * - editTypeOverride {boolean} (dflt: ''):
20889 * most of these attributes already require a recalc, but the ones that do not
20890 * have editType *style* or *plot* unless you override (presumably with *calc*)
20891 *
20892 * - anim {boolean) (dflt: undefined): is 'color' animatable?
20893 *
20894 * @return {object}
20895 */
20896module.exports = function colorScaleAttrs(context, opts) {
20897 context = context || '';
20898 opts = opts || {};
20899
20900 var cLetter = opts.cLetter || 'c';
20901 var onlyIfNumerical = ('onlyIfNumerical' in opts) ? opts.onlyIfNumerical : Boolean(context);
20902 var noScale = ('noScale' in opts) ? opts.noScale : context === 'marker.line';
20903 var showScaleDflt = ('showScaleDflt' in opts) ? opts.showScaleDflt : cLetter === 'z';
20904 var colorscaleDflt = typeof opts.colorscaleDflt === 'string' ? palettes[opts.colorscaleDflt] : null;
20905 var editTypeOverride = opts.editTypeOverride || '';
20906 var contextHead = context ? (context + '.') : '';
20907
20908 var colorAttr, colorAttrFull;
20909
20910 if('colorAttr' in opts) {
20911 colorAttr = opts.colorAttr;
20912 colorAttrFull = opts.colorAttr;
20913 } else {
20914 colorAttr = {z: 'z', c: 'color'}[cLetter];
20915 colorAttrFull = 'in ' + code(contextHead + colorAttr);
20916 }
20917
20918 var effectDesc = onlyIfNumerical ?
20919 ' Has an effect only if ' + colorAttrFull + 'is set to a numerical array.' :
20920 '';
20921
20922 var auto = cLetter + 'auto';
20923 var min = cLetter + 'min';
20924 var max = cLetter + 'max';
20925 var mid = cLetter + 'mid';
20926 var autoFull = code(contextHead + auto);
20927 var minFull = code(contextHead + min);
20928 var maxFull = code(contextHead + max);
20929 var minmaxFull = minFull + ' and ' + maxFull;
20930 var autoImpliedEdits = {};
20931 autoImpliedEdits[min] = autoImpliedEdits[max] = undefined;
20932 var minmaxImpliedEdits = {};
20933 minmaxImpliedEdits[auto] = false;
20934
20935 var attrs = {};
20936
20937 if(colorAttr === 'color') {
20938 attrs.color = {
20939 valType: 'color',
20940 arrayOk: true,
20941
20942 editType: editTypeOverride || 'style',
20943
20944 };
20945
20946 if(opts.anim) {
20947 attrs.color.anim = true;
20948 }
20949 }
20950
20951 attrs[auto] = {
20952 valType: 'boolean',
20953
20954 dflt: true,
20955 editType: 'calc',
20956 impliedEdits: autoImpliedEdits,
20957
20958 };
20959
20960 attrs[min] = {
20961 valType: 'number',
20962
20963 dflt: null,
20964 editType: editTypeOverride || 'plot',
20965 impliedEdits: minmaxImpliedEdits,
20966
20967 };
20968
20969 attrs[max] = {
20970 valType: 'number',
20971
20972 dflt: null,
20973 editType: editTypeOverride || 'plot',
20974 impliedEdits: minmaxImpliedEdits,
20975
20976 };
20977
20978 attrs[mid] = {
20979 valType: 'number',
20980
20981 dflt: null,
20982 editType: 'calc',
20983 impliedEdits: autoImpliedEdits,
20984
20985 };
20986
20987 attrs.colorscale = {
20988 valType: 'colorscale',
20989
20990 editType: 'calc',
20991 dflt: colorscaleDflt,
20992 impliedEdits: {autocolorscale: false},
20993
20994 };
20995
20996 attrs.autocolorscale = {
20997 valType: 'boolean',
20998
20999 // gets overrode in 'heatmap' & 'surface' for backwards comp.
21000 dflt: opts.autoColorDflt === false ? false : true,
21001 editType: 'calc',
21002 impliedEdits: {colorscale: undefined},
21003
21004 };
21005
21006 attrs.reversescale = {
21007 valType: 'boolean',
21008
21009 dflt: false,
21010 editType: 'plot',
21011
21012 };
21013
21014 if(!noScale) {
21015 attrs.showscale = {
21016 valType: 'boolean',
21017
21018 dflt: showScaleDflt,
21019 editType: 'calc',
21020
21021 };
21022
21023 attrs.colorbar = colorbarAttrs;
21024 }
21025
21026 if(!opts.noColorAxis) {
21027 attrs.coloraxis = {
21028 valType: 'subplotid',
21029
21030 regex: counterRegex('coloraxis'),
21031 dflt: null,
21032 editType: 'calc',
21033
21034 };
21035 }
21036
21037 return attrs;
21038};
21039
21040},{"../../lib/regex":192,"../colorbar/attributes":51,"./scales.js":65}],58:[function(_dereq_,module,exports){
21041/**
21042* Copyright 2012-2020, Plotly, Inc.
21043* All rights reserved.
21044*
21045* This source code is licensed under the MIT license found in the
21046* LICENSE file in the root directory of this source tree.
21047*/
21048
21049'use strict';
21050
21051var isNumeric = _dereq_('fast-isnumeric');
21052
21053var Lib = _dereq_('../../lib');
21054var extractOpts = _dereq_('./helpers').extractOpts;
21055
21056module.exports = function calc(gd, trace, opts) {
21057 var fullLayout = gd._fullLayout;
21058 var vals = opts.vals;
21059 var containerStr = opts.containerStr;
21060
21061 var container = containerStr ?
21062 Lib.nestedProperty(trace, containerStr).get() :
21063 trace;
21064
21065 var cOpts = extractOpts(container);
21066 var auto = cOpts.auto !== false;
21067 var min = cOpts.min;
21068 var max = cOpts.max;
21069 var mid = cOpts.mid;
21070
21071 var minVal = function() { return Lib.aggNums(Math.min, null, vals); };
21072 var maxVal = function() { return Lib.aggNums(Math.max, null, vals); };
21073
21074 if(min === undefined) {
21075 min = minVal();
21076 } else if(auto) {
21077 if(container._colorAx && isNumeric(min)) {
21078 min = Math.min(min, minVal());
21079 } else {
21080 min = minVal();
21081 }
21082 }
21083
21084 if(max === undefined) {
21085 max = maxVal();
21086 } else if(auto) {
21087 if(container._colorAx && isNumeric(max)) {
21088 max = Math.max(max, maxVal());
21089 } else {
21090 max = maxVal();
21091 }
21092 }
21093
21094 if(auto && mid !== undefined) {
21095 if(max - mid > mid - min) {
21096 min = mid - (max - mid);
21097 } else if(max - mid < mid - min) {
21098 max = mid + (mid - min);
21099 }
21100 }
21101
21102 if(min === max) {
21103 min -= 0.5;
21104 max += 0.5;
21105 }
21106
21107 cOpts._sync('min', min);
21108 cOpts._sync('max', max);
21109
21110 if(cOpts.autocolorscale) {
21111 var scl;
21112 if(min * max < 0) scl = fullLayout.colorscale.diverging;
21113 else if(min >= 0) scl = fullLayout.colorscale.sequential;
21114 else scl = fullLayout.colorscale.sequentialminus;
21115 cOpts._sync('colorscale', scl);
21116 }
21117};
21118
21119},{"../../lib":177,"./helpers":61,"fast-isnumeric":15}],59:[function(_dereq_,module,exports){
21120/**
21121* Copyright 2012-2020, Plotly, Inc.
21122* All rights reserved.
21123*
21124* This source code is licensed under the MIT license found in the
21125* LICENSE file in the root directory of this source tree.
21126*/
21127
21128'use strict';
21129
21130var Lib = _dereq_('../../lib');
21131var hasColorscale = _dereq_('./helpers').hasColorscale;
21132var extractOpts = _dereq_('./helpers').extractOpts;
21133
21134module.exports = function crossTraceDefaults(fullData, fullLayout) {
21135 function replace(cont, k) {
21136 var val = cont['_' + k];
21137 if(val !== undefined) {
21138 cont[k] = val;
21139 }
21140 }
21141
21142 function relinkColorAttrs(outerCont, cbOpt) {
21143 var cont = cbOpt.container ?
21144 Lib.nestedProperty(outerCont, cbOpt.container).get() :
21145 outerCont;
21146
21147 if(cont) {
21148 if(cont.coloraxis) {
21149 // stash ref to color axis
21150 cont._colorAx = fullLayout[cont.coloraxis];
21151 } else {
21152 var cOpts = extractOpts(cont);
21153 var isAuto = cOpts.auto;
21154
21155 if(isAuto || cOpts.min === undefined) {
21156 replace(cont, cbOpt.min);
21157 }
21158 if(isAuto || cOpts.max === undefined) {
21159 replace(cont, cbOpt.max);
21160 }
21161 if(cOpts.autocolorscale) {
21162 replace(cont, 'colorscale');
21163 }
21164 }
21165 }
21166 }
21167
21168 for(var i = 0; i < fullData.length; i++) {
21169 var trace = fullData[i];
21170 var cbOpts = trace._module.colorbar;
21171
21172 if(cbOpts) {
21173 if(Array.isArray(cbOpts)) {
21174 for(var j = 0; j < cbOpts.length; j++) {
21175 relinkColorAttrs(trace, cbOpts[j]);
21176 }
21177 } else {
21178 relinkColorAttrs(trace, cbOpts);
21179 }
21180 }
21181
21182 if(hasColorscale(trace, 'marker.line')) {
21183 relinkColorAttrs(trace, {
21184 container: 'marker.line',
21185 min: 'cmin',
21186 max: 'cmax'
21187 });
21188 }
21189 }
21190
21191 for(var k in fullLayout._colorAxes) {
21192 relinkColorAttrs(fullLayout[k], {min: 'cmin', max: 'cmax'});
21193 }
21194};
21195
21196},{"../../lib":177,"./helpers":61}],60:[function(_dereq_,module,exports){
21197/**
21198* Copyright 2012-2020, Plotly, Inc.
21199* All rights reserved.
21200*
21201* This source code is licensed under the MIT license found in the
21202* LICENSE file in the root directory of this source tree.
21203*/
21204
21205'use strict';
21206
21207var isNumeric = _dereq_('fast-isnumeric');
21208
21209var Lib = _dereq_('../../lib');
21210var hasColorbar = _dereq_('../colorbar/has_colorbar');
21211var colorbarDefaults = _dereq_('../colorbar/defaults');
21212
21213var isValidScale = _dereq_('./scales').isValid;
21214var traceIs = _dereq_('../../registry').traceIs;
21215
21216function npMaybe(parentCont, prefix) {
21217 var containerStr = prefix.slice(0, prefix.length - 1);
21218 return prefix ?
21219 Lib.nestedProperty(parentCont, containerStr).get() || {} :
21220 parentCont;
21221}
21222
21223/**
21224 * Colorscale / colorbar default handler
21225 *
21226 * @param {object} parentContIn : user (input) parent container (e.g. trace or layout coloraxis object)
21227 * @param {object} parentContOut : full parent container
21228 * @param {object} layout : (full) layout object
21229 * @param {fn} coerce : Lib.coerce wrapper
21230 * @param {object} opts :
21231 * - prefix {string} : attr string prefix to colorscale container from parent root
21232 * - cLetter {string} : 'c or 'z' color letter
21233 */
21234module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts) {
21235 var prefix = opts.prefix;
21236 var cLetter = opts.cLetter;
21237 var inTrace = '_module' in parentContOut;
21238 var containerIn = npMaybe(parentContIn, prefix);
21239 var containerOut = npMaybe(parentContOut, prefix);
21240 var template = npMaybe(parentContOut._template || {}, prefix) || {};
21241
21242 // colorScaleDefaults wrapper called if-ever we need to reset the colorscale
21243 // attributes for containers that were linked to invalid color axes
21244 var thisFn = function() {
21245 delete parentContIn.coloraxis;
21246 delete parentContOut.coloraxis;
21247 return colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts);
21248 };
21249
21250 if(inTrace) {
21251 var colorAxes = layout._colorAxes || {};
21252 var colorAx = coerce(prefix + 'coloraxis');
21253
21254 if(colorAx) {
21255 var colorbarVisuals = (
21256 traceIs(parentContOut, 'contour') &&
21257 Lib.nestedProperty(parentContOut, 'contours.coloring').get()
21258 ) || 'heatmap';
21259
21260 var stash = colorAxes[colorAx];
21261
21262 if(stash) {
21263 stash[2].push(thisFn);
21264
21265 if(stash[0] !== colorbarVisuals) {
21266 stash[0] = false;
21267 Lib.warn([
21268 'Ignoring coloraxis:', colorAx, 'setting',
21269 'as it is linked to incompatible colorscales.'
21270 ].join(' '));
21271 }
21272 } else {
21273 // stash:
21274 // - colorbar visual 'type'
21275 // - colorbar options to help in Colorbar.draw
21276 // - list of colorScaleDefaults wrapper functions
21277 colorAxes[colorAx] = [colorbarVisuals, parentContOut, [thisFn]];
21278 }
21279 return;
21280 }
21281 }
21282
21283 var minIn = containerIn[cLetter + 'min'];
21284 var maxIn = containerIn[cLetter + 'max'];
21285 var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn);
21286 var auto = coerce(prefix + cLetter + 'auto', !validMinMax);
21287
21288 if(auto) {
21289 coerce(prefix + cLetter + 'mid');
21290 } else {
21291 coerce(prefix + cLetter + 'min');
21292 coerce(prefix + cLetter + 'max');
21293 }
21294
21295 // handles both the trace case (autocolorscale is false by default) and
21296 // the marker and marker.line case (autocolorscale is true by default)
21297 var sclIn = containerIn.colorscale;
21298 var sclTemplate = template.colorscale;
21299 var autoColorscaleDflt;
21300 if(sclIn !== undefined) autoColorscaleDflt = !isValidScale(sclIn);
21301 if(sclTemplate !== undefined) autoColorscaleDflt = !isValidScale(sclTemplate);
21302 coerce(prefix + 'autocolorscale', autoColorscaleDflt);
21303
21304 coerce(prefix + 'colorscale');
21305 coerce(prefix + 'reversescale');
21306
21307 if(prefix !== 'marker.line.') {
21308 // handles both the trace case where the dflt is listed in attributes and
21309 // the marker case where the dflt is determined by hasColorbar
21310 var showScaleDflt;
21311 if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn);
21312
21313 var showScale = coerce(prefix + 'showscale', showScaleDflt);
21314 if(showScale) {
21315 if(prefix && template) containerOut._template = template;
21316 colorbarDefaults(containerIn, containerOut, layout);
21317 }
21318 }
21319};
21320
21321},{"../../lib":177,"../../registry":272,"../colorbar/defaults":53,"../colorbar/has_colorbar":55,"./scales":65,"fast-isnumeric":15}],61:[function(_dereq_,module,exports){
21322/**
21323* Copyright 2012-2020, Plotly, Inc.
21324* All rights reserved.
21325*
21326* This source code is licensed under the MIT license found in the
21327* LICENSE file in the root directory of this source tree.
21328*/
21329
21330'use strict';
21331
21332var d3 = _dereq_('d3');
21333var tinycolor = _dereq_('tinycolor2');
21334var isNumeric = _dereq_('fast-isnumeric');
21335
21336var Lib = _dereq_('../../lib');
21337var Color = _dereq_('../color');
21338
21339var isValidScale = _dereq_('./scales').isValid;
21340
21341function hasColorscale(trace, containerStr, colorKey) {
21342 var container = containerStr ?
21343 Lib.nestedProperty(trace, containerStr).get() || {} :
21344 trace;
21345 var color = container[colorKey || 'color'];
21346
21347 var isArrayWithOneNumber = false;
21348 if(Lib.isArrayOrTypedArray(color)) {
21349 for(var i = 0; i < color.length; i++) {
21350 if(isNumeric(color[i])) {
21351 isArrayWithOneNumber = true;
21352 break;
21353 }
21354 }
21355 }
21356
21357 return (
21358 Lib.isPlainObject(container) && (
21359 isArrayWithOneNumber ||
21360 container.showscale === true ||
21361 (isNumeric(container.cmin) && isNumeric(container.cmax)) ||
21362 isValidScale(container.colorscale) ||
21363 Lib.isPlainObject(container.colorbar)
21364 )
21365 );
21366}
21367
21368var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar'];
21369var letterAttrs = ['min', 'max', 'mid', 'auto'];
21370
21371/**
21372 * Extract 'c' / 'z', trace / color axis colorscale options
21373 *
21374 * Note that it would be nice to replace all z* with c* equivalents in v2
21375 *
21376 * @param {object} cont : attribute container
21377 * @return {object}:
21378 * - min: cmin or zmin
21379 * - max: cmax or zmax
21380 * - mid: cmid or zmid
21381 * - auto: cauto or zauto
21382 * - *scale: *scale attrs
21383 * - colorbar: colorbar
21384 * - _sync: function syncing attr and underscore dual (useful when calc'ing min/max)
21385 */
21386function extractOpts(cont) {
21387 var colorAx = cont._colorAx;
21388 var cont2 = colorAx ? colorAx : cont;
21389 var out = {};
21390 var cLetter;
21391 var i, k;
21392
21393 for(i = 0; i < constantAttrs.length; i++) {
21394 k = constantAttrs[i];
21395 out[k] = cont2[k];
21396 }
21397
21398 if(colorAx) {
21399 cLetter = 'c';
21400 for(i = 0; i < letterAttrs.length; i++) {
21401 k = letterAttrs[i];
21402 out[k] = cont2['c' + k];
21403 }
21404 } else {
21405 var k2;
21406 for(i = 0; i < letterAttrs.length; i++) {
21407 k = letterAttrs[i];
21408 k2 = 'c' + k;
21409 if(k2 in cont2) {
21410 out[k] = cont2[k2];
21411 continue;
21412 }
21413 k2 = 'z' + k;
21414 if(k2 in cont2) {
21415 out[k] = cont2[k2];
21416 }
21417 }
21418 cLetter = k2.charAt(0);
21419 }
21420
21421 out._sync = function(k, v) {
21422 var k2 = letterAttrs.indexOf(k) !== -1 ? cLetter + k : k;
21423 cont2[k2] = cont2['_' + k2] = v;
21424 };
21425
21426 return out;
21427}
21428
21429/**
21430 * Extract colorscale into numeric domain and color range.
21431 *
21432 * @param {object} cont colorscale container (e.g. trace, marker)
21433 * - colorscale {array of arrays}
21434 * - cmin/zmin {number}
21435 * - cmax/zmax {number}
21436 * - reversescale {boolean}
21437 *
21438 * @return {object}
21439 * - domain {array}
21440 * - range {array}
21441 */
21442function extractScale(cont) {
21443 var cOpts = extractOpts(cont);
21444 var cmin = cOpts.min;
21445 var cmax = cOpts.max;
21446
21447 var scl = cOpts.reversescale ?
21448 flipScale(cOpts.colorscale) :
21449 cOpts.colorscale;
21450
21451 var N = scl.length;
21452 var domain = new Array(N);
21453 var range = new Array(N);
21454
21455 for(var i = 0; i < N; i++) {
21456 var si = scl[i];
21457 domain[i] = cmin + si[0] * (cmax - cmin);
21458 range[i] = si[1];
21459 }
21460
21461 return {domain: domain, range: range};
21462}
21463
21464function flipScale(scl) {
21465 var N = scl.length;
21466 var sclNew = new Array(N);
21467
21468 for(var i = N - 1, j = 0; i >= 0; i--, j++) {
21469 var si = scl[i];
21470 sclNew[j] = [1 - si[0], si[1]];
21471 }
21472 return sclNew;
21473}
21474
21475/**
21476 * General colorscale function generator.
21477 *
21478 * @param {object} specs output of Colorscale.extractScale or precomputed domain, range.
21479 * - domain {array}
21480 * - range {array}
21481 *
21482 * @param {object} opts
21483 * - noNumericCheck {boolean} if true, scale func bypasses numeric checks
21484 * - returnArray {boolean} if true, scale func return 4-item array instead of color strings
21485 *
21486 * @return {function}
21487 */
21488function makeColorScaleFunc(specs, opts) {
21489 opts = opts || {};
21490
21491 var domain = specs.domain;
21492 var range = specs.range;
21493 var N = range.length;
21494 var _range = new Array(N);
21495
21496 for(var i = 0; i < N; i++) {
21497 var rgba = tinycolor(range[i]).toRgb();
21498 _range[i] = [rgba.r, rgba.g, rgba.b, rgba.a];
21499 }
21500
21501 var _sclFunc = d3.scale.linear()
21502 .domain(domain)
21503 .range(_range)
21504 .clamp(true);
21505
21506 var noNumericCheck = opts.noNumericCheck;
21507 var returnArray = opts.returnArray;
21508 var sclFunc;
21509
21510 if(noNumericCheck && returnArray) {
21511 sclFunc = _sclFunc;
21512 } else if(noNumericCheck) {
21513 sclFunc = function(v) {
21514 return colorArray2rbga(_sclFunc(v));
21515 };
21516 } else if(returnArray) {
21517 sclFunc = function(v) {
21518 if(isNumeric(v)) return _sclFunc(v);
21519 else if(tinycolor(v).isValid()) return v;
21520 else return Color.defaultLine;
21521 };
21522 } else {
21523 sclFunc = function(v) {
21524 if(isNumeric(v)) return colorArray2rbga(_sclFunc(v));
21525 else if(tinycolor(v).isValid()) return v;
21526 else return Color.defaultLine;
21527 };
21528 }
21529
21530 // colorbar draw looks into the d3 scale closure for domain and range
21531 sclFunc.domain = _sclFunc.domain;
21532 sclFunc.range = function() { return range; };
21533
21534 return sclFunc;
21535}
21536
21537function makeColorScaleFuncFromTrace(trace, opts) {
21538 return makeColorScaleFunc(extractScale(trace), opts);
21539}
21540
21541function colorArray2rbga(colorArray) {
21542 var colorObj = {
21543 r: colorArray[0],
21544 g: colorArray[1],
21545 b: colorArray[2],
21546 a: colorArray[3]
21547 };
21548
21549 return tinycolor(colorObj).toRgbString();
21550}
21551
21552module.exports = {
21553 hasColorscale: hasColorscale,
21554 extractOpts: extractOpts,
21555 extractScale: extractScale,
21556 flipScale: flipScale,
21557 makeColorScaleFunc: makeColorScaleFunc,
21558 makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace
21559};
21560
21561},{"../../lib":177,"../color":50,"./scales":65,"d3":13,"fast-isnumeric":15,"tinycolor2":32}],62:[function(_dereq_,module,exports){
21562/**
21563* Copyright 2012-2020, Plotly, Inc.
21564* All rights reserved.
21565*
21566* This source code is licensed under the MIT license found in the
21567* LICENSE file in the root directory of this source tree.
21568*/
21569
21570'use strict';
21571
21572var scales = _dereq_('./scales');
21573var helpers = _dereq_('./helpers');
21574
21575module.exports = {
21576 moduleType: 'component',
21577 name: 'colorscale',
21578
21579 attributes: _dereq_('./attributes'),
21580 layoutAttributes: _dereq_('./layout_attributes'),
21581
21582 supplyLayoutDefaults: _dereq_('./layout_defaults'),
21583 handleDefaults: _dereq_('./defaults'),
21584 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
21585
21586 calc: _dereq_('./calc'),
21587
21588 // ./scales.js is required in lib/coerce.js ;
21589 // it needs to be a seperate module to avoid circular a dependency
21590 scales: scales.scales,
21591 defaultScale: scales.defaultScale,
21592 getScale: scales.get,
21593 isValidScale: scales.isValid,
21594
21595 hasColorscale: helpers.hasColorscale,
21596 extractOpts: helpers.extractOpts,
21597 extractScale: helpers.extractScale,
21598 flipScale: helpers.flipScale,
21599 makeColorScaleFunc: helpers.makeColorScaleFunc,
21600 makeColorScaleFuncFromTrace: helpers.makeColorScaleFuncFromTrace
21601};
21602
21603},{"./attributes":57,"./calc":58,"./cross_trace_defaults":59,"./defaults":60,"./helpers":61,"./layout_attributes":63,"./layout_defaults":64,"./scales":65}],63:[function(_dereq_,module,exports){
21604/**
21605* Copyright 2012-2020, Plotly, Inc.
21606* All rights reserved.
21607*
21608* This source code is licensed under the MIT license found in the
21609* LICENSE file in the root directory of this source tree.
21610*/
21611
21612'use strict';
21613
21614var extendFlat = _dereq_('../../lib/extend').extendFlat;
21615
21616var colorScaleAttrs = _dereq_('./attributes');
21617var scales = _dereq_('./scales').scales;
21618
21619var msg = 'Note that `autocolorscale` must be true for this attribute to work.';
21620
21621module.exports = {
21622 editType: 'calc',
21623
21624 colorscale: {
21625 editType: 'calc',
21626
21627 sequential: {
21628 valType: 'colorscale',
21629 dflt: scales.Reds,
21630
21631 editType: 'calc',
21632
21633 },
21634 sequentialminus: {
21635 valType: 'colorscale',
21636 dflt: scales.Blues,
21637
21638 editType: 'calc',
21639
21640 },
21641 diverging: {
21642 valType: 'colorscale',
21643 dflt: scales.RdBu,
21644
21645 editType: 'calc',
21646
21647 }
21648 },
21649
21650 coloraxis: extendFlat({
21651 // not really a 'subplot' attribute container,
21652 // but this is the flag we use to denote attributes that
21653 // support yaxis, yaxis2, yaxis3, ... counters
21654 _isSubplotObj: true,
21655 editType: 'calc',
21656
21657 }, colorScaleAttrs('', {
21658 colorAttr: 'corresponding trace color array(s)',
21659 noColorAxis: true,
21660 showScaleDflt: true
21661 }))
21662};
21663
21664},{"../../lib/extend":170,"./attributes":57,"./scales":65}],64:[function(_dereq_,module,exports){
21665/**
21666* Copyright 2012-2020, Plotly, Inc.
21667* All rights reserved.
21668*
21669* This source code is licensed under the MIT license found in the
21670* LICENSE file in the root directory of this source tree.
21671*/
21672
21673'use strict';
21674
21675var Lib = _dereq_('../../lib');
21676var Template = _dereq_('../../plot_api/plot_template');
21677
21678var colorScaleAttrs = _dereq_('./layout_attributes');
21679var colorScaleDefaults = _dereq_('./defaults');
21680
21681module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
21682 function coerce(attr, dflt) {
21683 return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt);
21684 }
21685
21686 coerce('colorscale.sequential');
21687 coerce('colorscale.sequentialminus');
21688 coerce('colorscale.diverging');
21689
21690 var colorAxes = layoutOut._colorAxes;
21691 var colorAxIn, colorAxOut;
21692
21693 function coerceAx(attr, dflt) {
21694 return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt);
21695 }
21696
21697 for(var k in colorAxes) {
21698 var stash = colorAxes[k];
21699
21700 if(stash[0]) {
21701 colorAxIn = layoutIn[k] || {};
21702 colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis');
21703 colorAxOut._name = k;
21704 colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'});
21705 } else {
21706 // re-coerce colorscale attributes w/o coloraxis
21707 for(var i = 0; i < stash[2].length; i++) {
21708 stash[2][i]();
21709 }
21710 delete layoutOut._colorAxes[k];
21711 }
21712 }
21713};
21714
21715},{"../../lib":177,"../../plot_api/plot_template":212,"./defaults":60,"./layout_attributes":63}],65:[function(_dereq_,module,exports){
21716/**
21717* Copyright 2012-2020, Plotly, Inc.
21718* All rights reserved.
21719*
21720* This source code is licensed under the MIT license found in the
21721* LICENSE file in the root directory of this source tree.
21722*/
21723
21724'use strict';
21725
21726var tinycolor = _dereq_('tinycolor2');
21727
21728var scales = {
21729 'Greys': [
21730 [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']
21731 ],
21732
21733 'YlGnBu': [
21734 [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'],
21735 [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'],
21736 [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'],
21737 [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'],
21738 [1, 'rgb(255,255,217)']
21739 ],
21740
21741 'Greens': [
21742 [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'],
21743 [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'],
21744 [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'],
21745 [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'],
21746 [1, 'rgb(247,252,245)']
21747 ],
21748
21749 'YlOrRd': [
21750 [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'],
21751 [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'],
21752 [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'],
21753 [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'],
21754 [1, 'rgb(255,255,204)']
21755 ],
21756
21757 'Bluered': [
21758 [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']
21759 ],
21760
21761 // modified RdBu based on
21762 // http://www.kennethmoreland.com/color-maps/
21763 'RdBu': [
21764 [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'],
21765 [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'],
21766 [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)']
21767 ],
21768
21769 // Scale for non-negative numeric values
21770 'Reds': [
21771 [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'],
21772 [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)']
21773 ],
21774
21775 // Scale for non-positive numeric values
21776 'Blues': [
21777 [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'],
21778 [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'],
21779 [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)']
21780 ],
21781
21782 'Picnic': [
21783 [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'],
21784 [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'],
21785 [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'],
21786 [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'],
21787 [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'],
21788 [1, 'rgb(255,0,0)']
21789 ],
21790
21791 'Rainbow': [
21792 [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'],
21793 [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'],
21794 [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'],
21795 [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'],
21796 [1, 'rgb(255,0,0)']
21797 ],
21798
21799 'Portland': [
21800 [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'],
21801 [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'],
21802 [1, 'rgb(217,30,30)']
21803 ],
21804
21805 'Jet': [
21806 [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'],
21807 [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'],
21808 [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)']
21809 ],
21810
21811 'Hot': [
21812 [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'],
21813 [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)']
21814 ],
21815
21816 'Blackbody': [
21817 [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'],
21818 [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'],
21819 [1, 'rgb(160,200,255)']
21820 ],
21821
21822 'Earth': [
21823 [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'],
21824 [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'],
21825 [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)']
21826 ],
21827
21828 'Electric': [
21829 [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'],
21830 [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'],
21831 [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)']
21832 ],
21833
21834 'Viridis': [
21835 [0, '#440154'], [0.06274509803921569, '#48186a'],
21836 [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'],
21837 [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'],
21838 [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'],
21839 [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'],
21840 [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'],
21841 [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'],
21842 [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'],
21843 [1, '#fde725']
21844 ],
21845
21846 'Cividis': [
21847 [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'],
21848 [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'],
21849 [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'],
21850 [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'],
21851 [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'],
21852 [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'],
21853 [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'],
21854 [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'],
21855 [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)']
21856 ]
21857};
21858
21859var defaultScale = scales.RdBu;
21860
21861function getScale(scl, dflt) {
21862 if(!dflt) dflt = defaultScale;
21863 if(!scl) return dflt;
21864
21865 function parseScale() {
21866 try {
21867 scl = scales[scl] || JSON.parse(scl);
21868 } catch(e) {
21869 scl = dflt;
21870 }
21871 }
21872
21873 if(typeof scl === 'string') {
21874 parseScale();
21875 // occasionally scl is double-JSON encoded...
21876 if(typeof scl === 'string') parseScale();
21877 }
21878
21879 if(!isValidScaleArray(scl)) return dflt;
21880 return scl;
21881}
21882
21883
21884function isValidScaleArray(scl) {
21885 var highestVal = 0;
21886
21887 if(!Array.isArray(scl) || scl.length < 2) return false;
21888
21889 if(!scl[0] || !scl[scl.length - 1]) return false;
21890
21891 if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false;
21892
21893 for(var i = 0; i < scl.length; i++) {
21894 var si = scl[i];
21895
21896 if(si.length !== 2 || +si[0] < highestVal || !tinycolor(si[1]).isValid()) {
21897 return false;
21898 }
21899
21900 highestVal = +si[0];
21901 }
21902
21903 return true;
21904}
21905
21906function isValidScale(scl) {
21907 if(scales[scl] !== undefined) return true;
21908 else return isValidScaleArray(scl);
21909}
21910
21911module.exports = {
21912 scales: scales,
21913 defaultScale: defaultScale,
21914
21915 get: getScale,
21916 isValid: isValidScale
21917};
21918
21919},{"tinycolor2":32}],66:[function(_dereq_,module,exports){
21920/**
21921* Copyright 2012-2020, Plotly, Inc.
21922* All rights reserved.
21923*
21924* This source code is licensed under the MIT license found in the
21925* LICENSE file in the root directory of this source tree.
21926*/
21927
21928
21929'use strict';
21930
21931
21932// for automatic alignment on dragging, <1/3 means left align,
21933// >2/3 means right, and between is center. Pick the right fraction
21934// based on where you are, and return the fraction corresponding to
21935// that position on the object
21936module.exports = function align(v, dv, v0, v1, anchor) {
21937 var vmin = (v - v0) / (v1 - v0);
21938 var vmax = vmin + dv / (v1 - v0);
21939 var vc = (vmin + vmax) / 2;
21940
21941 // explicitly specified anchor
21942 if(anchor === 'left' || anchor === 'bottom') return vmin;
21943 if(anchor === 'center' || anchor === 'middle') return vc;
21944 if(anchor === 'right' || anchor === 'top') return vmax;
21945
21946 // automatic based on position
21947 if(vmin < (2 / 3) - vc) return vmin;
21948 if(vmax > (4 / 3) - vc) return vmax;
21949 return vc;
21950};
21951
21952},{}],67:[function(_dereq_,module,exports){
21953/**
21954* Copyright 2012-2020, Plotly, Inc.
21955* All rights reserved.
21956*
21957* This source code is licensed under the MIT license found in the
21958* LICENSE file in the root directory of this source tree.
21959*/
21960
21961
21962'use strict';
21963
21964var Lib = _dereq_('../../lib');
21965
21966
21967// set cursors pointing toward the closest corner/side,
21968// to indicate alignment
21969// x and y are 0-1, fractions of the plot area
21970var cursorset = [
21971 ['sw-resize', 's-resize', 'se-resize'],
21972 ['w-resize', 'move', 'e-resize'],
21973 ['nw-resize', 'n-resize', 'ne-resize']
21974];
21975
21976module.exports = function getCursor(x, y, xanchor, yanchor) {
21977 if(xanchor === 'left') x = 0;
21978 else if(xanchor === 'center') x = 1;
21979 else if(xanchor === 'right') x = 2;
21980 else x = Lib.constrain(Math.floor(x * 3), 0, 2);
21981
21982 if(yanchor === 'bottom') y = 0;
21983 else if(yanchor === 'middle') y = 1;
21984 else if(yanchor === 'top') y = 2;
21985 else y = Lib.constrain(Math.floor(y * 3), 0, 2);
21986
21987 return cursorset[y][x];
21988};
21989
21990},{"../../lib":177}],68:[function(_dereq_,module,exports){
21991/**
21992* Copyright 2012-2020, Plotly, Inc.
21993* All rights reserved.
21994*
21995* This source code is licensed under the MIT license found in the
21996* LICENSE file in the root directory of this source tree.
21997*/
21998
21999'use strict';
22000
22001exports.selectMode = function(dragmode) {
22002 return (
22003 dragmode === 'lasso' ||
22004 dragmode === 'select'
22005 );
22006};
22007
22008exports.drawMode = function(dragmode) {
22009 return (
22010 dragmode === 'drawclosedpath' ||
22011 dragmode === 'drawopenpath' ||
22012 dragmode === 'drawline' ||
22013 dragmode === 'drawrect' ||
22014 dragmode === 'drawcircle'
22015 );
22016};
22017
22018exports.openMode = function(dragmode) {
22019 return (
22020 dragmode === 'drawline' ||
22021 dragmode === 'drawopenpath'
22022 );
22023};
22024
22025exports.rectMode = function(dragmode) {
22026 return (
22027 dragmode === 'select' ||
22028 dragmode === 'drawline' ||
22029 dragmode === 'drawrect' ||
22030 dragmode === 'drawcircle'
22031 );
22032};
22033
22034exports.freeMode = function(dragmode) {
22035 return (
22036 dragmode === 'lasso' ||
22037 dragmode === 'drawclosedpath' ||
22038 dragmode === 'drawopenpath'
22039 );
22040};
22041
22042exports.selectingOrDrawing = function(dragmode) {
22043 return (
22044 exports.freeMode(dragmode) ||
22045 exports.rectMode(dragmode)
22046 );
22047};
22048
22049},{}],69:[function(_dereq_,module,exports){
22050/**
22051* Copyright 2012-2020, Plotly, Inc.
22052* All rights reserved.
22053*
22054* This source code is licensed under the MIT license found in the
22055* LICENSE file in the root directory of this source tree.
22056*/
22057
22058'use strict';
22059
22060var mouseOffset = _dereq_('mouse-event-offset');
22061var hasHover = _dereq_('has-hover');
22062var supportsPassive = _dereq_('has-passive-events');
22063
22064var removeElement = _dereq_('../../lib').removeElement;
22065var constants = _dereq_('../../plots/cartesian/constants');
22066
22067var dragElement = module.exports = {};
22068
22069dragElement.align = _dereq_('./align');
22070dragElement.getCursor = _dereq_('./cursor');
22071
22072var unhover = _dereq_('./unhover');
22073dragElement.unhover = unhover.wrapped;
22074dragElement.unhoverRaw = unhover.raw;
22075
22076/**
22077 * Abstracts click & drag interactions
22078 *
22079 * During the interaction, a "coverSlip" element - a transparent
22080 * div covering the whole page - is created, which has two key effects:
22081 * - Lets you drag beyond the boundaries of the plot itself without
22082 * dropping (but if you drag all the way out of the browser window the
22083 * interaction will end)
22084 * - Freezes the cursor: whatever mouse cursor the drag element had when the
22085 * interaction started gets copied to the coverSlip for use until mouseup
22086 *
22087 * If the user executes a drag bigger than MINDRAG, callbacks will fire as:
22088 * prepFn, moveFn (1 or more times), doneFn
22089 * If the user does not drag enough, prepFn and clickFn will fire.
22090 *
22091 * Note: If you cancel contextmenu, clickFn will fire even with a right click
22092 * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
22093 * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
22094 * TODO: we should probably turn this into a `config` parameter, so we can fix it
22095 * such that if you *don't* cancel contextmenu, we can prevent partial drags, which
22096 * put you in a weird state.
22097 *
22098 * If the user clicks multiple times quickly, clickFn will fire each time
22099 * but numClicks will increase to help you recognize doubleclicks.
22100 *
22101 * @param {object} options with keys:
22102 * element (required) the DOM element to drag
22103 * prepFn (optional) function(event, startX, startY)
22104 * executed on mousedown
22105 * startX and startY are the clientX and clientY pixel position
22106 * of the mousedown event
22107 * moveFn (optional) function(dx, dy)
22108 * executed on move, ONLY after we've exceeded MINDRAG
22109 * (we keep executing moveFn if you move back to where you started)
22110 * dx and dy are the net pixel offset of the drag,
22111 * dragged is true/false, has the mouse moved enough to
22112 * constitute a drag
22113 * doneFn (optional) function(e)
22114 * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
22115 * sure that moveFn has been called at least once)
22116 * numClicks is how many clicks we've registered within
22117 * a doubleclick time
22118 * e is the original mouseup event
22119 * clickFn (optional) function(numClicks, e)
22120 * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
22121 * has not been called at all)
22122 * numClicks is how many clicks we've registered within
22123 * a doubleclick time
22124 * e is the original mousedown event
22125 * clampFn (optional, function(dx, dy) return [dx2, dy2])
22126 * Provide custom clamping function for small displacements.
22127 * By default, clamping is done using `minDrag` to x and y displacements
22128 * independently.
22129 */
22130dragElement.init = function init(options) {
22131 var gd = options.gd;
22132 var numClicks = 1;
22133 var doubleClickDelay = gd._context.doubleClickDelay;
22134 var element = options.element;
22135
22136 var startX,
22137 startY,
22138 newMouseDownTime,
22139 cursor,
22140 dragCover,
22141 initialEvent,
22142 initialTarget,
22143 rightClick;
22144
22145 if(!gd._mouseDownTime) gd._mouseDownTime = 0;
22146
22147 element.style.pointerEvents = 'all';
22148
22149 element.onmousedown = onStart;
22150
22151 if(!supportsPassive) {
22152 element.ontouchstart = onStart;
22153 } else {
22154 if(element._ontouchstart) {
22155 element.removeEventListener('touchstart', element._ontouchstart);
22156 }
22157 element._ontouchstart = onStart;
22158 element.addEventListener('touchstart', onStart, {passive: false});
22159 }
22160
22161 function _clampFn(dx, dy, minDrag) {
22162 if(Math.abs(dx) < minDrag) dx = 0;
22163 if(Math.abs(dy) < minDrag) dy = 0;
22164 return [dx, dy];
22165 }
22166
22167 var clampFn = options.clampFn || _clampFn;
22168
22169 function onStart(e) {
22170 // make dragging and dragged into properties of gd
22171 // so that others can look at and modify them
22172 gd._dragged = false;
22173 gd._dragging = true;
22174 var offset = pointerOffset(e);
22175 startX = offset[0];
22176 startY = offset[1];
22177 initialTarget = e.target;
22178 initialEvent = e;
22179 rightClick = e.buttons === 2 || e.ctrlKey;
22180
22181 // fix Fx.hover for touch events
22182 if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') {
22183 e.clientX = startX;
22184 e.clientY = startY;
22185 }
22186
22187 newMouseDownTime = (new Date()).getTime();
22188 if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) {
22189 // in a click train
22190 numClicks += 1;
22191 } else {
22192 // new click train
22193 numClicks = 1;
22194 gd._mouseDownTime = newMouseDownTime;
22195 }
22196
22197 if(options.prepFn) options.prepFn(e, startX, startY);
22198
22199 if(hasHover && !rightClick) {
22200 dragCover = coverSlip();
22201 dragCover.style.cursor = window.getComputedStyle(element).cursor;
22202 } else if(!hasHover) {
22203 // document acts as a dragcover for mobile, bc we can't create dragcover dynamically
22204 dragCover = document;
22205 cursor = window.getComputedStyle(document.documentElement).cursor;
22206 document.documentElement.style.cursor = window.getComputedStyle(element).cursor;
22207 }
22208
22209 document.addEventListener('mouseup', onDone);
22210 document.addEventListener('touchend', onDone);
22211
22212 if(options.dragmode !== false) {
22213 e.preventDefault();
22214 document.addEventListener('mousemove', onMove);
22215 document.addEventListener('touchmove', onMove, {passive: false});
22216 }
22217
22218 return;
22219 }
22220
22221 function onMove(e) {
22222 e.preventDefault();
22223
22224 var offset = pointerOffset(e);
22225 var minDrag = options.minDrag || constants.MINDRAG;
22226 var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag);
22227 var dx = dxdy[0];
22228 var dy = dxdy[1];
22229
22230 if(dx || dy) {
22231 gd._dragged = true;
22232 dragElement.unhover(gd);
22233 }
22234
22235 if(gd._dragged && options.moveFn && !rightClick) {
22236 gd._dragdata = {
22237 element: element,
22238 dx: dx,
22239 dy: dy
22240 };
22241 options.moveFn(dx, dy);
22242 }
22243
22244 return;
22245 }
22246
22247 function onDone(e) {
22248 delete gd._dragdata;
22249
22250 if(options.dragmode !== false) {
22251 e.preventDefault();
22252 document.removeEventListener('mousemove', onMove);
22253 document.removeEventListener('touchmove', onMove);
22254 }
22255
22256 document.removeEventListener('mouseup', onDone);
22257 document.removeEventListener('touchend', onDone);
22258
22259 if(hasHover) {
22260 removeElement(dragCover);
22261 } else if(cursor) {
22262 dragCover.documentElement.style.cursor = cursor;
22263 cursor = null;
22264 }
22265
22266 if(!gd._dragging) {
22267 gd._dragged = false;
22268 return;
22269 }
22270 gd._dragging = false;
22271
22272 // don't count as a dblClick unless the mouseUp is also within
22273 // the dblclick delay
22274 if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) {
22275 numClicks = Math.max(numClicks - 1, 1);
22276 }
22277
22278 if(gd._dragged) {
22279 if(options.doneFn) options.doneFn();
22280 } else {
22281 if(options.clickFn) options.clickFn(numClicks, initialEvent);
22282
22283 // If we haven't dragged, this should be a click. But because of the
22284 // coverSlip changing the element, the natural system might not generate one,
22285 // so we need to make our own. But right clicks don't normally generate
22286 // click events, only contextmenu events, which happen on mousedown.
22287 if(!rightClick) {
22288 var e2;
22289
22290 try {
22291 e2 = new MouseEvent('click', e);
22292 } catch(err) {
22293 var offset = pointerOffset(e);
22294 e2 = document.createEvent('MouseEvents');
22295 e2.initMouseEvent('click',
22296 e.bubbles, e.cancelable,
22297 e.view, e.detail,
22298 e.screenX, e.screenY,
22299 offset[0], offset[1],
22300 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
22301 e.button, e.relatedTarget);
22302 }
22303
22304 initialTarget.dispatchEvent(e2);
22305 }
22306 }
22307
22308 gd._dragging = false;
22309 gd._dragged = false;
22310 return;
22311 }
22312};
22313
22314function coverSlip() {
22315 var cover = document.createElement('div');
22316
22317 cover.className = 'dragcover';
22318 var cStyle = cover.style;
22319 cStyle.position = 'fixed';
22320 cStyle.left = 0;
22321 cStyle.right = 0;
22322 cStyle.top = 0;
22323 cStyle.bottom = 0;
22324 cStyle.zIndex = 999999999;
22325 cStyle.background = 'none';
22326
22327 document.body.appendChild(cover);
22328
22329 return cover;
22330}
22331
22332dragElement.coverSlip = coverSlip;
22333
22334function pointerOffset(e) {
22335 return mouseOffset(
22336 e.changedTouches ? e.changedTouches[0] : e,
22337 document.body
22338 );
22339}
22340
22341},{"../../lib":177,"../../plots/cartesian/constants":228,"./align":66,"./cursor":67,"./unhover":70,"has-hover":17,"has-passive-events":18,"mouse-event-offset":21}],70:[function(_dereq_,module,exports){
22342/**
22343* Copyright 2012-2020, Plotly, Inc.
22344* All rights reserved.
22345*
22346* This source code is licensed under the MIT license found in the
22347* LICENSE file in the root directory of this source tree.
22348*/
22349
22350'use strict';
22351
22352var Events = _dereq_('../../lib/events');
22353var throttle = _dereq_('../../lib/throttle');
22354var getGraphDiv = _dereq_('../../lib/dom').getGraphDiv;
22355
22356var hoverConstants = _dereq_('../fx/constants');
22357
22358var unhover = module.exports = {};
22359
22360unhover.wrapped = function(gd, evt, subplot) {
22361 gd = getGraphDiv(gd);
22362
22363 // Important, clear any queued hovers
22364 if(gd._fullLayout) {
22365 throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID);
22366 }
22367
22368 unhover.raw(gd, evt, subplot);
22369};
22370
22371
22372// remove hover effects on mouse out, and emit unhover event
22373unhover.raw = function raw(gd, evt) {
22374 var fullLayout = gd._fullLayout;
22375 var oldhoverdata = gd._hoverdata;
22376
22377 if(!evt) evt = {};
22378 if(evt.target &&
22379 Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
22380 return;
22381 }
22382
22383 fullLayout._hoverlayer.selectAll('g').remove();
22384 fullLayout._hoverlayer.selectAll('line').remove();
22385 fullLayout._hoverlayer.selectAll('circle').remove();
22386 gd._hoverdata = undefined;
22387
22388 if(evt.target && oldhoverdata) {
22389 gd.emit('plotly_unhover', {
22390 event: evt,
22391 points: oldhoverdata
22392 });
22393 }
22394};
22395
22396},{"../../lib/dom":168,"../../lib/events":169,"../../lib/throttle":199,"../fx/constants":84}],71:[function(_dereq_,module,exports){
22397/**
22398* Copyright 2012-2020, Plotly, Inc.
22399* All rights reserved.
22400*
22401* This source code is licensed under the MIT license found in the
22402* LICENSE file in the root directory of this source tree.
22403*/
22404
22405
22406'use strict';
22407
22408exports.dash = {
22409 valType: 'string',
22410 // string type usually doesn't take values... this one should really be
22411 // a special type or at least a special coercion function, from the GUI
22412 // you only get these values but elsewhere the user can supply a list of
22413 // dash lengths in px, and it will be honored
22414 values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'],
22415 dflt: 'solid',
22416
22417 editType: 'style',
22418
22419};
22420
22421},{}],72:[function(_dereq_,module,exports){
22422/**
22423* Copyright 2012-2020, Plotly, Inc.
22424* All rights reserved.
22425*
22426* This source code is licensed under the MIT license found in the
22427* LICENSE file in the root directory of this source tree.
22428*/
22429
22430
22431'use strict';
22432
22433var d3 = _dereq_('d3');
22434var isNumeric = _dereq_('fast-isnumeric');
22435var tinycolor = _dereq_('tinycolor2');
22436
22437var Registry = _dereq_('../../registry');
22438var Color = _dereq_('../color');
22439var Colorscale = _dereq_('../colorscale');
22440var Lib = _dereq_('../../lib');
22441var svgTextUtils = _dereq_('../../lib/svg_text_utils');
22442
22443var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
22444var alignment = _dereq_('../../constants/alignment');
22445var LINE_SPACING = alignment.LINE_SPACING;
22446var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM;
22447
22448var subTypes = _dereq_('../../traces/scatter/subtypes');
22449var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func');
22450var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue;
22451
22452var drawing = module.exports = {};
22453
22454// -----------------------------------------------------
22455// styling functions for plot elements
22456// -----------------------------------------------------
22457
22458drawing.font = function(s, family, size, color) {
22459 // also allow the form font(s, {family, size, color})
22460 if(Lib.isPlainObject(family)) {
22461 color = family.color;
22462 size = family.size;
22463 family = family.family;
22464 }
22465 if(family) s.style('font-family', family);
22466 if(size + 1) s.style('font-size', size + 'px');
22467 if(color) s.call(Color.fill, color);
22468};
22469
22470/*
22471 * Positioning helpers
22472 * Note: do not use `setPosition` with <text> nodes modified by
22473 * `svgTextUtils.convertToTspans`. Use `svgTextUtils.positionText`
22474 * instead, so that <tspan.line> elements get updated to match.
22475 */
22476drawing.setPosition = function(s, x, y) { s.attr('x', x).attr('y', y); };
22477drawing.setSize = function(s, w, h) { s.attr('width', w).attr('height', h); };
22478drawing.setRect = function(s, x, y, w, h) {
22479 s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
22480};
22481
22482/** Translate node
22483 *
22484 * @param {object} d : calcdata point item
22485 * @param {sel} sel : d3 selction of node to translate
22486 * @param {object} xa : corresponding full xaxis object
22487 * @param {object} ya : corresponding full yaxis object
22488 *
22489 * @return {boolean} :
22490 * true if selection got translated
22491 * false if selection could not get translated
22492 */
22493drawing.translatePoint = function(d, sel, xa, ya) {
22494 var x = xa.c2p(d.x);
22495 var y = ya.c2p(d.y);
22496
22497 if(isNumeric(x) && isNumeric(y) && sel.node()) {
22498 // for multiline text this works better
22499 if(sel.node().nodeName === 'text') {
22500 sel.attr('x', x).attr('y', y);
22501 } else {
22502 sel.attr('transform', 'translate(' + x + ',' + y + ')');
22503 }
22504 } else {
22505 return false;
22506 }
22507
22508 return true;
22509};
22510
22511drawing.translatePoints = function(s, xa, ya) {
22512 s.each(function(d) {
22513 var sel = d3.select(this);
22514 drawing.translatePoint(d, sel, xa, ya);
22515 });
22516};
22517
22518drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
22519 sel.attr(
22520 'display',
22521 (xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none'
22522 );
22523};
22524
22525drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
22526 if(!subplot._hasClipOnAxisFalse) return;
22527
22528 var xa = subplot.xaxis;
22529 var ya = subplot.yaxis;
22530
22531 traceGroups.each(function(d) {
22532 var trace = d[0].trace;
22533 var xcalendar = trace.xcalendar;
22534 var ycalendar = trace.ycalendar;
22535 var selector = Registry.traceIs(trace, 'bar-like') ? '.bartext' : '.point,.textpoint';
22536
22537 traceGroups.selectAll(selector).each(function(d) {
22538 drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
22539 });
22540 });
22541};
22542
22543drawing.crispRound = function(gd, lineWidth, dflt) {
22544 // for lines that disable antialiasing we want to
22545 // make sure the width is an integer, and at least 1 if it's nonzero
22546
22547 if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0;
22548
22549 // but not for static plots - these don't get antialiased anyway.
22550 if(gd._context.staticPlot) return lineWidth;
22551
22552 if(lineWidth < 1) return 1;
22553 return Math.round(lineWidth);
22554};
22555
22556drawing.singleLineStyle = function(d, s, lw, lc, ld) {
22557 s.style('fill', 'none');
22558 var line = (((d || [])[0] || {}).trace || {}).line || {};
22559 var lw1 = lw || line.width || 0;
22560 var dash = ld || line.dash || '';
22561
22562 Color.stroke(s, lc || line.color);
22563 drawing.dashLine(s, dash, lw1);
22564};
22565
22566drawing.lineGroupStyle = function(s, lw, lc, ld) {
22567 s.style('fill', 'none')
22568 .each(function(d) {
22569 var line = (((d || [])[0] || {}).trace || {}).line || {};
22570 var lw1 = lw || line.width || 0;
22571 var dash = ld || line.dash || '';
22572
22573 d3.select(this)
22574 .call(Color.stroke, lc || line.color)
22575 .call(drawing.dashLine, dash, lw1);
22576 });
22577};
22578
22579drawing.dashLine = function(s, dash, lineWidth) {
22580 lineWidth = +lineWidth || 0;
22581
22582 dash = drawing.dashStyle(dash, lineWidth);
22583
22584 s.style({
22585 'stroke-dasharray': dash,
22586 'stroke-width': lineWidth + 'px'
22587 });
22588};
22589
22590drawing.dashStyle = function(dash, lineWidth) {
22591 lineWidth = +lineWidth || 1;
22592 var dlw = Math.max(lineWidth, 3);
22593
22594 if(dash === 'solid') dash = '';
22595 else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
22596 else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
22597 else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
22598 else if(dash === 'dashdot') {
22599 dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
22600 } else if(dash === 'longdashdot') {
22601 dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
22602 }
22603 // otherwise user wrote the dasharray themselves - leave it be
22604
22605 return dash;
22606};
22607
22608// Same as fillGroupStyle, except in this case the selection may be a transition
22609drawing.singleFillStyle = function(sel) {
22610 var node = d3.select(sel.node());
22611 var data = node.data();
22612 var fillcolor = (((data[0] || [])[0] || {}).trace || {}).fillcolor;
22613 if(fillcolor) {
22614 sel.call(Color.fill, fillcolor);
22615 }
22616};
22617
22618drawing.fillGroupStyle = function(s) {
22619 s.style('stroke-width', 0)
22620 .each(function(d) {
22621 var shape = d3.select(this);
22622 // N.B. 'd' won't be a calcdata item when
22623 // fill !== 'none' on a segment-less and marker-less trace
22624 if(d[0].trace) {
22625 shape.call(Color.fill, d[0].trace.fillcolor);
22626 }
22627 });
22628};
22629
22630var SYMBOLDEFS = _dereq_('./symbol_defs');
22631
22632drawing.symbolNames = [];
22633drawing.symbolFuncs = [];
22634drawing.symbolNeedLines = {};
22635drawing.symbolNoDot = {};
22636drawing.symbolNoFill = {};
22637drawing.symbolList = [];
22638
22639Object.keys(SYMBOLDEFS).forEach(function(k) {
22640 var symDef = SYMBOLDEFS[k];
22641 var n = symDef.n;
22642 drawing.symbolList.push(
22643 n,
22644 k,
22645 n + 100,
22646 k + '-open'
22647 );
22648 drawing.symbolNames[n] = k;
22649 drawing.symbolFuncs[n] = symDef.f;
22650
22651 if(symDef.needLine) {
22652 drawing.symbolNeedLines[n] = true;
22653 }
22654 if(symDef.noDot) {
22655 drawing.symbolNoDot[n] = true;
22656 } else {
22657 drawing.symbolList.push(
22658 n + 200,
22659 k + '-dot',
22660 n + 300,
22661 k + '-open-dot'
22662 );
22663 }
22664 if(symDef.noFill) {
22665 drawing.symbolNoFill[n] = true;
22666 }
22667});
22668
22669var MAXSYMBOL = drawing.symbolNames.length;
22670// add a dot in the middle of the symbol
22671var DOTPATH = 'M0,0.5L0.5,0L0,-0.5L-0.5,0Z';
22672
22673drawing.symbolNumber = function(v) {
22674 if(typeof v === 'string') {
22675 var vbase = 0;
22676 if(v.indexOf('-open') > 0) {
22677 vbase = 100;
22678 v = v.replace('-open', '');
22679 }
22680 if(v.indexOf('-dot') > 0) {
22681 vbase += 200;
22682 v = v.replace('-dot', '');
22683 }
22684 v = drawing.symbolNames.indexOf(v);
22685 if(v >= 0) { v += vbase; }
22686 }
22687
22688 return (v % 100 >= MAXSYMBOL || v >= 400) ?
22689 0 : Math.floor(Math.max(v, 0));
22690};
22691
22692function makePointPath(symbolNumber, r) {
22693 var base = symbolNumber % 100;
22694 return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
22695}
22696
22697var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
22698var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0};
22699var stopFormatter = d3.format('~.1f');
22700var gradientInfo = {
22701 radial: {node: 'radialGradient'},
22702 radialreversed: {node: 'radialGradient', reversed: true},
22703 horizontal: {node: 'linearGradient', attrs: HORZGRADIENT},
22704 horizontalreversed: {node: 'linearGradient', attrs: HORZGRADIENT, reversed: true},
22705 vertical: {node: 'linearGradient', attrs: VERTGRADIENT},
22706 verticalreversed: {node: 'linearGradient', attrs: VERTGRADIENT, reversed: true}
22707};
22708
22709/**
22710 * gradient: create and apply a gradient fill
22711 *
22712 * @param {object} sel: d3 selection to apply this gradient to
22713 * You can use `selection.call(Drawing.gradient, ...)`
22714 * @param {DOM element} gd: the graph div `sel` is part of
22715 * @param {string} gradientID: a unique (within this plot) identifier
22716 * for this gradient, so that we don't create unnecessary definitions
22717 * @param {string} type: 'radial', 'horizontal', or 'vertical', optionally with
22718 * 'reversed' at the end. Normally radial goes center to edge,
22719 * horizontal goes right to left, and vertical goes bottom to top
22720 * @param {array} colorscale: as in attribute values, [[fraction, color], ...]
22721 * @param {string} prop: the property to apply to, 'fill' or 'stroke'
22722 */
22723drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
22724 var len = colorscale.length;
22725 var info = gradientInfo[type];
22726 var colorStops = new Array(len);
22727 for(var i = 0; i < len; i++) {
22728 if(info.reversed) {
22729 colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]];
22730 } else {
22731 colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]];
22732 }
22733 }
22734
22735 var fullLayout = gd._fullLayout;
22736 var fullID = 'g' + fullLayout._uid + '-' + gradientID;
22737
22738 var gradient = fullLayout._defs.select('.gradients')
22739 .selectAll('#' + fullID)
22740 .data([type + colorStops.join(';')], Lib.identity);
22741
22742 gradient.exit().remove();
22743
22744 gradient.enter()
22745 .append(info.node)
22746 .each(function() {
22747 var el = d3.select(this);
22748 if(info.attrs) el.attr(info.attrs);
22749
22750 el.attr('id', fullID);
22751
22752 var stops = el.selectAll('stop')
22753 .data(colorStops);
22754 stops.exit().remove();
22755 stops.enter().append('stop');
22756
22757 stops.each(function(d) {
22758 var tc = tinycolor(d[1]);
22759 d3.select(this).attr({
22760 offset: d[0] + '%',
22761 'stop-color': Color.tinyRGB(tc),
22762 'stop-opacity': tc.getAlpha()
22763 });
22764 });
22765 });
22766
22767 sel.style(prop, getFullUrl(fullID, gd))
22768 .style(prop + '-opacity', null);
22769
22770 var className2query = function(s) {
22771 return '.' + s.attr('class').replace(/\s/g, '.');
22772 };
22773 var k = className2query(d3.select(sel.node().parentNode)) +
22774 '>' + className2query(sel);
22775 fullLayout._gradientUrlQueryParts[k] = 1;
22776};
22777
22778/*
22779 * Make the gradients container and clear out any previous gradients.
22780 * We never collect all the gradients we need in one place,
22781 * so we can't ever remove gradients that have stopped being useful,
22782 * except all at once before a full redraw.
22783 * The upside of this is arbitrary points can share gradient defs
22784 */
22785drawing.initGradients = function(gd) {
22786 var fullLayout = gd._fullLayout;
22787
22788 var gradientsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'gradients');
22789 gradientsGroup.selectAll('linearGradient,radialGradient').remove();
22790
22791 // initialize stash of query parts filled in Drawing.gradient,
22792 // used to fix URL strings during image exports
22793 fullLayout._gradientUrlQueryParts = {};
22794};
22795
22796
22797drawing.pointStyle = function(s, trace, gd) {
22798 if(!s.size()) return;
22799
22800 var fns = drawing.makePointStyleFns(trace);
22801
22802 s.each(function(d) {
22803 drawing.singlePointStyle(d, d3.select(this), trace, fns, gd);
22804 });
22805};
22806
22807drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
22808 var marker = trace.marker;
22809 var markerLine = marker.line;
22810
22811 sel.style('opacity',
22812 fns.selectedOpacityFn ? fns.selectedOpacityFn(d) :
22813 (d.mo === undefined ? marker.opacity : d.mo)
22814 );
22815
22816 if(fns.ms2mrc) {
22817 var r;
22818
22819 // handle multi-trace graph edit case
22820 if(d.ms === 'various' || marker.size === 'various') {
22821 r = 3;
22822 } else {
22823 r = fns.ms2mrc(d.ms);
22824 }
22825
22826 // store the calculated size so hover can use it
22827 d.mrc = r;
22828
22829 if(fns.selectedSizeFn) {
22830 r = d.mrc = fns.selectedSizeFn(d);
22831 }
22832
22833 // turn the symbol into a sanitized number
22834 var x = drawing.symbolNumber(d.mx || marker.symbol) || 0;
22835
22836 // save if this marker is open
22837 // because that impacts how to handle colors
22838 d.om = x % 200 >= 100;
22839
22840 sel.attr('d', makePointPath(x, r));
22841 }
22842
22843 var perPointGradient = false;
22844 var fillColor, lineColor, lineWidth;
22845
22846 // 'so' is suspected outliers, for box plots
22847 if(d.so) {
22848 lineWidth = markerLine.outlierwidth;
22849 lineColor = markerLine.outliercolor;
22850 fillColor = marker.outliercolor;
22851 } else {
22852 var markerLineWidth = (markerLine || {}).width;
22853
22854 lineWidth = (
22855 d.mlw + 1 ||
22856 markerLineWidth + 1 ||
22857 // TODO: we need the latter for legends... can we get rid of it?
22858 (d.trace ? (d.trace.marker.line || {}).width : 0) + 1
22859 ) - 1 || 0;
22860
22861 if('mlc' in d) lineColor = d.mlcc = fns.lineScale(d.mlc);
22862 // weird case: array wasn't long enough to apply to every point
22863 else if(Lib.isArrayOrTypedArray(markerLine.color)) lineColor = Color.defaultLine;
22864 else lineColor = markerLine.color;
22865
22866 if(Lib.isArrayOrTypedArray(marker.color)) {
22867 fillColor = Color.defaultLine;
22868 perPointGradient = true;
22869 }
22870
22871 if('mc' in d) {
22872 fillColor = d.mcc = fns.markerScale(d.mc);
22873 } else {
22874 fillColor = marker.color || 'rgba(0,0,0,0)';
22875 }
22876
22877 if(fns.selectedColorFn) {
22878 fillColor = fns.selectedColorFn(d);
22879 }
22880 }
22881
22882 if(d.om) {
22883 // open markers can't have zero linewidth, default to 1px,
22884 // and use fill color as stroke color
22885 sel.call(Color.stroke, fillColor)
22886 .style({
22887 'stroke-width': (lineWidth || 1) + 'px',
22888 fill: 'none'
22889 });
22890 } else {
22891 sel.style('stroke-width', (d.isBlank ? 0 : lineWidth) + 'px');
22892
22893 var markerGradient = marker.gradient;
22894
22895 var gradientType = d.mgt;
22896 if(gradientType) perPointGradient = true;
22897 else gradientType = markerGradient && markerGradient.type;
22898
22899 // for legend - arrays will propagate through here, but we don't need
22900 // to treat it as per-point.
22901 if(Array.isArray(gradientType)) {
22902 gradientType = gradientType[0];
22903 if(!gradientInfo[gradientType]) gradientType = 0;
22904 }
22905
22906 if(gradientType && gradientType !== 'none') {
22907 var gradientColor = d.mgc;
22908 if(gradientColor) perPointGradient = true;
22909 else gradientColor = markerGradient.color;
22910
22911 var gradientID = trace.uid;
22912 if(perPointGradient) gradientID += '-' + d.i;
22913
22914 drawing.gradient(sel, gd, gradientID, gradientType,
22915 [[0, gradientColor], [1, fillColor]], 'fill');
22916 } else {
22917 Color.fill(sel, fillColor);
22918 }
22919
22920 if(lineWidth) {
22921 Color.stroke(sel, lineColor);
22922 }
22923 }
22924};
22925
22926drawing.makePointStyleFns = function(trace) {
22927 var out = {};
22928 var marker = trace.marker;
22929
22930 // allow array marker and marker line colors to be
22931 // scaled by given max and min to colorscales
22932 out.markerScale = drawing.tryColorscale(marker, '');
22933 out.lineScale = drawing.tryColorscale(marker, 'line');
22934
22935 if(Registry.traceIs(trace, 'symbols')) {
22936 out.ms2mrc = subTypes.isBubble(trace) ?
22937 makeBubbleSizeFn(trace) :
22938 function() { return (marker.size || 6) / 2; };
22939 }
22940
22941 if(trace.selectedpoints) {
22942 Lib.extendFlat(out, drawing.makeSelectedPointStyleFns(trace));
22943 }
22944
22945 return out;
22946};
22947
22948drawing.makeSelectedPointStyleFns = function(trace) {
22949 var out = {};
22950
22951 var selectedAttrs = trace.selected || {};
22952 var unselectedAttrs = trace.unselected || {};
22953
22954 var marker = trace.marker || {};
22955 var selectedMarker = selectedAttrs.marker || {};
22956 var unselectedMarker = unselectedAttrs.marker || {};
22957
22958 var mo = marker.opacity;
22959 var smo = selectedMarker.opacity;
22960 var usmo = unselectedMarker.opacity;
22961 var smoIsDefined = smo !== undefined;
22962 var usmoIsDefined = usmo !== undefined;
22963
22964 if(Lib.isArrayOrTypedArray(mo) || smoIsDefined || usmoIsDefined) {
22965 out.selectedOpacityFn = function(d) {
22966 var base = d.mo === undefined ? marker.opacity : d.mo;
22967
22968 if(d.selected) {
22969 return smoIsDefined ? smo : base;
22970 } else {
22971 return usmoIsDefined ? usmo : DESELECTDIM * base;
22972 }
22973 };
22974 }
22975
22976 var mc = marker.color;
22977 var smc = selectedMarker.color;
22978 var usmc = unselectedMarker.color;
22979
22980 if(smc || usmc) {
22981 out.selectedColorFn = function(d) {
22982 var base = d.mcc || mc;
22983
22984 if(d.selected) {
22985 return smc || base;
22986 } else {
22987 return usmc || base;
22988 }
22989 };
22990 }
22991
22992 var ms = marker.size;
22993 var sms = selectedMarker.size;
22994 var usms = unselectedMarker.size;
22995 var smsIsDefined = sms !== undefined;
22996 var usmsIsDefined = usms !== undefined;
22997
22998 if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
22999 out.selectedSizeFn = function(d) {
23000 var base = d.mrc || ms / 2;
23001
23002 if(d.selected) {
23003 return smsIsDefined ? sms / 2 : base;
23004 } else {
23005 return usmsIsDefined ? usms / 2 : base;
23006 }
23007 };
23008 }
23009
23010 return out;
23011};
23012
23013drawing.makeSelectedTextStyleFns = function(trace) {
23014 var out = {};
23015
23016 var selectedAttrs = trace.selected || {};
23017 var unselectedAttrs = trace.unselected || {};
23018
23019 var textFont = trace.textfont || {};
23020 var selectedTextFont = selectedAttrs.textfont || {};
23021 var unselectedTextFont = unselectedAttrs.textfont || {};
23022
23023 var tc = textFont.color;
23024 var stc = selectedTextFont.color;
23025 var utc = unselectedTextFont.color;
23026
23027 out.selectedTextColorFn = function(d) {
23028 var base = d.tc || tc;
23029
23030 if(d.selected) {
23031 return stc || base;
23032 } else {
23033 if(utc) return utc;
23034 else return stc ? base : Color.addOpacity(base, DESELECTDIM);
23035 }
23036 };
23037
23038 return out;
23039};
23040
23041drawing.selectedPointStyle = function(s, trace) {
23042 if(!s.size() || !trace.selectedpoints) return;
23043
23044 var fns = drawing.makeSelectedPointStyleFns(trace);
23045 var marker = trace.marker || {};
23046 var seq = [];
23047
23048 if(fns.selectedOpacityFn) {
23049 seq.push(function(pt, d) {
23050 pt.style('opacity', fns.selectedOpacityFn(d));
23051 });
23052 }
23053
23054 if(fns.selectedColorFn) {
23055 seq.push(function(pt, d) {
23056 Color.fill(pt, fns.selectedColorFn(d));
23057 });
23058 }
23059
23060 if(fns.selectedSizeFn) {
23061 seq.push(function(pt, d) {
23062 var mx = d.mx || marker.symbol || 0;
23063 var mrc2 = fns.selectedSizeFn(d);
23064
23065 pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
23066
23067 // save for Drawing.selectedTextStyle
23068 d.mrc2 = mrc2;
23069 });
23070 }
23071
23072 if(seq.length) {
23073 s.each(function(d) {
23074 var pt = d3.select(this);
23075 for(var i = 0; i < seq.length; i++) {
23076 seq[i](pt, d);
23077 }
23078 });
23079 }
23080};
23081
23082drawing.tryColorscale = function(marker, prefix) {
23083 var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker;
23084
23085 if(cont) {
23086 var colorArray = cont.color;
23087 if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) {
23088 return Colorscale.makeColorScaleFuncFromTrace(cont);
23089 }
23090 }
23091 return Lib.identity;
23092};
23093
23094var TEXTOFFSETSIGN = {
23095 start: 1, end: -1, middle: 0, bottom: 1, top: -1
23096};
23097
23098function textPointPosition(s, textPosition, fontSize, markerRadius) {
23099 var group = d3.select(s.node().parentNode);
23100
23101 var v = textPosition.indexOf('top') !== -1 ?
23102 'top' :
23103 textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle';
23104 var h = textPosition.indexOf('left') !== -1 ?
23105 'end' :
23106 textPosition.indexOf('right') !== -1 ? 'start' : 'middle';
23107
23108 // if markers are shown, offset a little more than
23109 // the nominal marker size
23110 // ie 2/1.6 * nominal, bcs some markers are a bit bigger
23111 var r = markerRadius ? markerRadius / 0.8 + 1 : 0;
23112
23113 var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1;
23114 var dx = TEXTOFFSETSIGN[h] * r;
23115 var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
23116 (TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
23117
23118 // fix the overall text group position
23119 s.attr('text-anchor', h);
23120 group.attr('transform', 'translate(' + dx + ',' + dy + ')');
23121}
23122
23123function extracTextFontSize(d, trace) {
23124 var fontSize = d.ts || trace.textfont.size;
23125 return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
23126}
23127
23128// draw text at points
23129drawing.textPointStyle = function(s, trace, gd) {
23130 if(!s.size()) return;
23131
23132 var selectedTextColorFn;
23133 if(trace.selectedpoints) {
23134 var fns = drawing.makeSelectedTextStyleFns(trace);
23135 selectedTextColorFn = fns.selectedTextColorFn;
23136 }
23137
23138 var texttemplate = trace.texttemplate;
23139 var fullLayout = gd._fullLayout;
23140
23141 s.each(function(d) {
23142 var p = d3.select(this);
23143
23144 var text = texttemplate ?
23145 Lib.extractOption(d, trace, 'txt', 'texttemplate') :
23146 Lib.extractOption(d, trace, 'tx', 'text');
23147
23148 if(!text && text !== 0) {
23149 p.remove();
23150 return;
23151 }
23152
23153 if(texttemplate) {
23154 var labels = trace._module.formatLabels ? trace._module.formatLabels(d, trace, fullLayout) : {};
23155 var pointValues = {};
23156 appendArrayPointValue(pointValues, trace, d.i);
23157 var meta = trace._meta || {};
23158 text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta);
23159 }
23160
23161 var pos = d.tp || trace.textposition;
23162 var fontSize = extracTextFontSize(d, trace);
23163 var fontColor = selectedTextColorFn ?
23164 selectedTextColorFn(d) :
23165 (d.tc || trace.textfont.color);
23166
23167 p.call(drawing.font,
23168 d.tf || trace.textfont.family,
23169 fontSize,
23170 fontColor)
23171 .text(text)
23172 .call(svgTextUtils.convertToTspans, gd)
23173 .call(textPointPosition, pos, fontSize, d.mrc);
23174 });
23175};
23176
23177drawing.selectedTextStyle = function(s, trace) {
23178 if(!s.size() || !trace.selectedpoints) return;
23179
23180 var fns = drawing.makeSelectedTextStyleFns(trace);
23181
23182 s.each(function(d) {
23183 var tx = d3.select(this);
23184 var tc = fns.selectedTextColorFn(d);
23185 var tp = d.tp || trace.textposition;
23186 var fontSize = extracTextFontSize(d, trace);
23187
23188 Color.fill(tx, tc);
23189 textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc);
23190 });
23191};
23192
23193// generalized Catmull-Rom splines, per
23194// http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
23195var CatmullRomExp = 0.5;
23196drawing.smoothopen = function(pts, smoothness) {
23197 if(pts.length < 3) { return 'M' + pts.join('L');}
23198 var path = 'M' + pts[0];
23199 var tangents = [];
23200 var i;
23201 for(i = 1; i < pts.length - 1; i++) {
23202 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
23203 }
23204 path += 'Q' + tangents[0][0] + ' ' + pts[1];
23205 for(i = 2; i < pts.length - 1; i++) {
23206 path += 'C' + tangents[i - 2][1] + ' ' + tangents[i - 1][0] + ' ' + pts[i];
23207 }
23208 path += 'Q' + tangents[pts.length - 3][1] + ' ' + pts[pts.length - 1];
23209 return path;
23210};
23211
23212drawing.smoothclosed = function(pts, smoothness) {
23213 if(pts.length < 3) { return 'M' + pts.join('L') + 'Z'; }
23214 var path = 'M' + pts[0];
23215 var pLast = pts.length - 1;
23216 var tangents = [makeTangent(pts[pLast], pts[0], pts[1], smoothness)];
23217 var i;
23218 for(i = 1; i < pLast; i++) {
23219 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
23220 }
23221 tangents.push(
23222 makeTangent(pts[pLast - 1], pts[pLast], pts[0], smoothness)
23223 );
23224
23225 for(i = 1; i <= pLast; i++) {
23226 path += 'C' + tangents[i - 1][1] + ' ' + tangents[i][0] + ' ' + pts[i];
23227 }
23228 path += 'C' + tangents[pLast][1] + ' ' + tangents[0][0] + ' ' + pts[0] + 'Z';
23229 return path;
23230};
23231
23232function makeTangent(prevpt, thispt, nextpt, smoothness) {
23233 var d1x = prevpt[0] - thispt[0];
23234 var d1y = prevpt[1] - thispt[1];
23235 var d2x = nextpt[0] - thispt[0];
23236 var d2y = nextpt[1] - thispt[1];
23237 var d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2);
23238 var d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2);
23239 var numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness;
23240 var numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness;
23241 var denom1 = 3 * d2a * (d1a + d2a);
23242 var denom2 = 3 * d1a * (d1a + d2a);
23243 return [
23244 [
23245 d3.round(thispt[0] + (denom1 && numx / denom1), 2),
23246 d3.round(thispt[1] + (denom1 && numy / denom1), 2)
23247 ], [
23248 d3.round(thispt[0] - (denom2 && numx / denom2), 2),
23249 d3.round(thispt[1] - (denom2 && numy / denom2), 2)
23250 ]
23251 ];
23252}
23253
23254// step paths - returns a generator function for paths
23255// with the given step shape
23256var STEPPATH = {
23257 hv: function(p0, p1) {
23258 return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
23259 },
23260 vh: function(p0, p1) {
23261 return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
23262 },
23263 hvh: function(p0, p1) {
23264 return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' +
23265 d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
23266 },
23267 vhv: function(p0, p1) {
23268 return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' +
23269 d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
23270 }
23271};
23272var STEPLINEAR = function(p0, p1) {
23273 return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2);
23274};
23275drawing.steps = function(shape) {
23276 var onestep = STEPPATH[shape] || STEPLINEAR;
23277 return function(pts) {
23278 var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2);
23279 for(var i = 1; i < pts.length; i++) {
23280 path += onestep(pts[i - 1], pts[i]);
23281 }
23282 return path;
23283 };
23284};
23285
23286// off-screen svg render testing element, shared by the whole page
23287// uses the id 'js-plotly-tester' and stores it in drawing.tester
23288drawing.makeTester = function() {
23289 var tester = Lib.ensureSingleById(d3.select('body'), 'svg', 'js-plotly-tester', function(s) {
23290 s.attr(xmlnsNamespaces.svgAttrs)
23291 .style({
23292 position: 'absolute',
23293 left: '-10000px',
23294 top: '-10000px',
23295 width: '9000px',
23296 height: '9000px',
23297 'z-index': '1'
23298 });
23299 });
23300
23301 // browsers differ on how they describe the bounding rect of
23302 // the svg if its contents spill over... so make a 1x1px
23303 // reference point we can measure off of.
23304 var testref = Lib.ensureSingle(tester, 'path', 'js-reference-point', function(s) {
23305 s.attr('d', 'M0,0H1V1H0Z')
23306 .style({
23307 'stroke-width': 0,
23308 fill: 'black'
23309 });
23310 });
23311
23312 drawing.tester = tester;
23313 drawing.testref = testref;
23314};
23315
23316/*
23317 * use our offscreen tester to get a clientRect for an element,
23318 * in a reference frame where it isn't translated (or transformed) and
23319 * its anchor point is at (0,0)
23320 * always returns a copy of the bbox, so the caller can modify it safely
23321 *
23322 * @param {SVGElement} node: the element to measure. If possible this should be
23323 * a <text> or MathJax <g> element that's already passed through
23324 * `convertToTspans` because in that case we can cache the results, but it's
23325 * possible to pass in any svg element.
23326 *
23327 * @param {boolean} inTester: is this element already in `drawing.tester`?
23328 * If you are measuring a dummy element, rather than one you really intend
23329 * to use on the plot, making it in `drawing.tester` in the first place
23330 * allows us to test faster because it cuts out cloning and appending it.
23331 *
23332 * @param {string} hash: for internal use only, if we already know the cache key
23333 * for this element beforehand.
23334 *
23335 * @return {object}: a plain object containing the width, height, left, right,
23336 * top, and bottom of `node`
23337 */
23338drawing.savedBBoxes = {};
23339var savedBBoxesCount = 0;
23340var maxSavedBBoxes = 10000;
23341
23342drawing.bBox = function(node, inTester, hash) {
23343 /*
23344 * Cache elements we've already measured so we don't have to
23345 * remeasure the same thing many times
23346 * We have a few bBox callers though who pass a node larger than
23347 * a <text> or a MathJax <g>, such as an axis group containing many labels.
23348 * These will not generate a hash (unless we figure out an appropriate
23349 * hash key for them) and thus we will not hash them.
23350 */
23351 if(!hash) hash = nodeHash(node);
23352 var out;
23353 if(hash) {
23354 out = drawing.savedBBoxes[hash];
23355 if(out) return Lib.extendFlat({}, out);
23356 } else if(node.childNodes.length === 1) {
23357 /*
23358 * If we have only one child element, which is itself hashable, make
23359 * a new hash from this element plus its x,y,transform
23360 * These bounding boxes *include* x,y,transform - mostly for use by
23361 * callers trying to avoid overlaps (ie titles)
23362 */
23363 var innerNode = node.childNodes[0];
23364
23365 hash = nodeHash(innerNode);
23366 if(hash) {
23367 var x = +innerNode.getAttribute('x') || 0;
23368 var y = +innerNode.getAttribute('y') || 0;
23369 var transform = innerNode.getAttribute('transform');
23370
23371 if(!transform) {
23372 // in this case, just varying x and y, don't bother caching
23373 // the final bBox because the alteration is quick.
23374 var innerBB = drawing.bBox(innerNode, false, hash);
23375 if(x) {
23376 innerBB.left += x;
23377 innerBB.right += x;
23378 }
23379 if(y) {
23380 innerBB.top += y;
23381 innerBB.bottom += y;
23382 }
23383 return innerBB;
23384 }
23385 /*
23386 * else we have a transform - rather than make a complicated
23387 * (and error-prone and probably slow) transform parser/calculator,
23388 * just continue on calculating the boundingClientRect of the group
23389 * and use the new composite hash to cache it.
23390 * That said, `innerNode.transform.baseVal` is an array of
23391 * `SVGTransform` objects, that *do* seem to have a nice matrix
23392 * multiplication interface that we could use to avoid making
23393 * another getBoundingClientRect call...
23394 */
23395 hash += '~' + x + '~' + y + '~' + transform;
23396
23397 out = drawing.savedBBoxes[hash];
23398 if(out) return Lib.extendFlat({}, out);
23399 }
23400 }
23401 var testNode, tester;
23402 if(inTester) {
23403 testNode = node;
23404 } else {
23405 tester = drawing.tester.node();
23406
23407 // copy the node to test into the tester
23408 testNode = node.cloneNode(true);
23409 tester.appendChild(testNode);
23410 }
23411
23412 // standardize its position (and newline tspans if any)
23413 d3.select(testNode)
23414 .attr('transform', null)
23415 .call(svgTextUtils.positionText, 0, 0);
23416
23417 var testRect = testNode.getBoundingClientRect();
23418 var refRect = drawing.testref
23419 .node()
23420 .getBoundingClientRect();
23421
23422 if(!inTester) tester.removeChild(testNode);
23423
23424 var bb = {
23425 height: testRect.height,
23426 width: testRect.width,
23427 left: testRect.left - refRect.left,
23428 top: testRect.top - refRect.top,
23429 right: testRect.right - refRect.left,
23430 bottom: testRect.bottom - refRect.top
23431 };
23432
23433 // make sure we don't have too many saved boxes,
23434 // or a long session could overload on memory
23435 // by saving boxes for long-gone elements
23436 if(savedBBoxesCount >= maxSavedBBoxes) {
23437 drawing.savedBBoxes = {};
23438 savedBBoxesCount = 0;
23439 }
23440
23441 // cache this bbox
23442 if(hash) drawing.savedBBoxes[hash] = bb;
23443 savedBBoxesCount++;
23444
23445 return Lib.extendFlat({}, bb);
23446};
23447
23448// capture everything about a node (at least in our usage) that
23449// impacts its bounding box, given that bBox clears x, y, and transform
23450function nodeHash(node) {
23451 var inputText = node.getAttribute('data-unformatted');
23452 if(inputText === null) return;
23453 return inputText +
23454 node.getAttribute('data-math') +
23455 node.getAttribute('text-anchor') +
23456 node.getAttribute('style');
23457}
23458
23459/**
23460 * Set clipPath URL in a way that work for all situations.
23461 *
23462 * In details, graphs on pages with <base> HTML tags need to prepend
23463 * the clip path ids with the page's base url EXCEPT during toImage exports.
23464 *
23465 * @param {d3 selection} s : node to add clip-path attribute
23466 * @param {string} localId : local clip-path (w/o base url) id
23467 * @param {DOM element || object} gd
23468 * - context._baseUrl {string}
23469 * - context._exportedPlot {boolean}
23470 */
23471drawing.setClipUrl = function(s, localId, gd) {
23472 s.attr('clip-path', getFullUrl(localId, gd));
23473};
23474
23475function getFullUrl(localId, gd) {
23476 if(!localId) return null;
23477
23478 var context = gd._context;
23479 var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');
23480 return 'url(\'' + baseUrl + '#' + localId + '\')';
23481}
23482
23483drawing.getTranslate = function(element) {
23484 // Note the separator [^\d] between x and y in this regex
23485 // We generally use ',' but IE will convert it to ' '
23486 var re = /.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/;
23487 var getter = element.attr ? 'attr' : 'getAttribute';
23488 var transform = element[getter]('transform') || '';
23489
23490 var translate = transform.replace(re, function(match, p1, p2) {
23491 return [p1, p2].join(' ');
23492 })
23493 .split(' ');
23494
23495 return {
23496 x: +translate[0] || 0,
23497 y: +translate[1] || 0
23498 };
23499};
23500
23501drawing.setTranslate = function(element, x, y) {
23502 var re = /(\btranslate\(.*?\);?)/;
23503 var getter = element.attr ? 'attr' : 'getAttribute';
23504 var setter = element.attr ? 'attr' : 'setAttribute';
23505 var transform = element[getter]('transform') || '';
23506
23507 x = x || 0;
23508 y = y || 0;
23509
23510 transform = transform.replace(re, '').trim();
23511 transform += ' translate(' + x + ', ' + y + ')';
23512 transform = transform.trim();
23513
23514 element[setter]('transform', transform);
23515
23516 return transform;
23517};
23518
23519drawing.getScale = function(element) {
23520 var re = /.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/;
23521 var getter = element.attr ? 'attr' : 'getAttribute';
23522 var transform = element[getter]('transform') || '';
23523
23524 var translate = transform.replace(re, function(match, p1, p2) {
23525 return [p1, p2].join(' ');
23526 })
23527 .split(' ');
23528
23529 return {
23530 x: +translate[0] || 1,
23531 y: +translate[1] || 1
23532 };
23533};
23534
23535drawing.setScale = function(element, x, y) {
23536 var re = /(\bscale\(.*?\);?)/;
23537 var getter = element.attr ? 'attr' : 'getAttribute';
23538 var setter = element.attr ? 'attr' : 'setAttribute';
23539 var transform = element[getter]('transform') || '';
23540
23541 x = x || 1;
23542 y = y || 1;
23543
23544 transform = transform.replace(re, '').trim();
23545 transform += ' scale(' + x + ', ' + y + ')';
23546 transform = transform.trim();
23547
23548 element[setter]('transform', transform);
23549
23550 return transform;
23551};
23552
23553var SCALE_RE = /\s*sc.*/;
23554
23555drawing.setPointGroupScale = function(selection, xScale, yScale) {
23556 xScale = xScale || 1;
23557 yScale = yScale || 1;
23558
23559 if(!selection) return;
23560
23561 // The same scale transform for every point:
23562 var scale = (xScale === 1 && yScale === 1) ?
23563 '' :
23564 ' scale(' + xScale + ',' + yScale + ')';
23565
23566 selection.each(function() {
23567 var t = (this.getAttribute('transform') || '').replace(SCALE_RE, '');
23568 t += scale;
23569 t = t.trim();
23570 this.setAttribute('transform', t);
23571 });
23572};
23573
23574var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/;
23575
23576drawing.setTextPointsScale = function(selection, xScale, yScale) {
23577 if(!selection) return;
23578
23579 selection.each(function() {
23580 var transforms;
23581 var el = d3.select(this);
23582 var text = el.select('text');
23583
23584 if(!text.node()) return;
23585
23586 var x = parseFloat(text.attr('x') || 0);
23587 var y = parseFloat(text.attr('y') || 0);
23588
23589 var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE);
23590
23591 if(xScale === 1 && yScale === 1) {
23592 transforms = [];
23593 } else {
23594 transforms = [
23595 'translate(' + x + ',' + y + ')',
23596 'scale(' + xScale + ',' + yScale + ')',
23597 'translate(' + (-x) + ',' + (-y) + ')',
23598 ];
23599 }
23600
23601 if(existingTransform) {
23602 transforms.push(existingTransform);
23603 }
23604
23605 el.attr('transform', transforms.join(' '));
23606 });
23607};
23608
23609},{"../../components/fx/helpers":86,"../../constants/alignment":152,"../../constants/interactions":154,"../../constants/xmlns_namespaces":156,"../../lib":177,"../../lib/svg_text_utils":198,"../../registry":272,"../../traces/scatter/make_bubble_size_func":311,"../../traces/scatter/subtypes":318,"../color":50,"../colorscale":62,"./symbol_defs":73,"d3":13,"fast-isnumeric":15,"tinycolor2":32}],73:[function(_dereq_,module,exports){
23610/**
23611* Copyright 2012-2020, Plotly, Inc.
23612* All rights reserved.
23613*
23614* This source code is licensed under the MIT license found in the
23615* LICENSE file in the root directory of this source tree.
23616*/
23617
23618
23619'use strict';
23620
23621var d3 = _dereq_('d3');
23622
23623/** Marker symbol definitions
23624 * users can specify markers either by number or name
23625 * add 100 (or '-open') and you get an open marker
23626 * open markers have no fill and use line color as the stroke color
23627 * add 200 (or '-dot') and you get a dot in the middle
23628 * add both and you get both
23629 */
23630
23631module.exports = {
23632 circle: {
23633 n: 0,
23634 f: function(r) {
23635 var rs = d3.round(r, 2);
23636 return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
23637 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
23638 }
23639 },
23640 square: {
23641 n: 1,
23642 f: function(r) {
23643 var rs = d3.round(r, 2);
23644 return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
23645 }
23646 },
23647 diamond: {
23648 n: 2,
23649 f: function(r) {
23650 var rd = d3.round(r * 1.3, 2);
23651 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z';
23652 }
23653 },
23654 cross: {
23655 n: 3,
23656 f: function(r) {
23657 var rc = d3.round(r * 0.4, 2);
23658 var rc2 = d3.round(r * 1.2, 2);
23659 return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
23660 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 +
23661 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z';
23662 }
23663 },
23664 x: {
23665 n: 4,
23666 f: function(r) {
23667 var rx = d3.round(r * 0.8 / Math.sqrt(2), 2);
23668 var ne = 'l' + rx + ',' + rx;
23669 var se = 'l' + rx + ',-' + rx;
23670 var sw = 'l-' + rx + ',-' + rx;
23671 var nw = 'l-' + rx + ',' + rx;
23672 return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z';
23673 }
23674 },
23675 'triangle-up': {
23676 n: 5,
23677 f: function(r) {
23678 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
23679 var r2 = d3.round(r / 2, 2);
23680 var rs = d3.round(r, 2);
23681 return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z';
23682 }
23683 },
23684 'triangle-down': {
23685 n: 6,
23686 f: function(r) {
23687 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
23688 var r2 = d3.round(r / 2, 2);
23689 var rs = d3.round(r, 2);
23690 return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z';
23691 }
23692 },
23693 'triangle-left': {
23694 n: 7,
23695 f: function(r) {
23696 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
23697 var r2 = d3.round(r / 2, 2);
23698 var rs = d3.round(r, 2);
23699 return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z';
23700 }
23701 },
23702 'triangle-right': {
23703 n: 8,
23704 f: function(r) {
23705 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
23706 var r2 = d3.round(r / 2, 2);
23707 var rs = d3.round(r, 2);
23708 return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z';
23709 }
23710 },
23711 'triangle-ne': {
23712 n: 9,
23713 f: function(r) {
23714 var r1 = d3.round(r * 0.6, 2);
23715 var r2 = d3.round(r * 1.2, 2);
23716 return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z';
23717 }
23718 },
23719 'triangle-se': {
23720 n: 10,
23721 f: function(r) {
23722 var r1 = d3.round(r * 0.6, 2);
23723 var r2 = d3.round(r * 1.2, 2);
23724 return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z';
23725 }
23726 },
23727 'triangle-sw': {
23728 n: 11,
23729 f: function(r) {
23730 var r1 = d3.round(r * 0.6, 2);
23731 var r2 = d3.round(r * 1.2, 2);
23732 return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z';
23733 }
23734 },
23735 'triangle-nw': {
23736 n: 12,
23737 f: function(r) {
23738 var r1 = d3.round(r * 0.6, 2);
23739 var r2 = d3.round(r * 1.2, 2);
23740 return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z';
23741 }
23742 },
23743 pentagon: {
23744 n: 13,
23745 f: function(r) {
23746 var x1 = d3.round(r * 0.951, 2);
23747 var x2 = d3.round(r * 0.588, 2);
23748 var y0 = d3.round(-r, 2);
23749 var y1 = d3.round(r * -0.309, 2);
23750 var y2 = d3.round(r * 0.809, 2);
23751 return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
23752 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z';
23753 }
23754 },
23755 hexagon: {
23756 n: 14,
23757 f: function(r) {
23758 var y0 = d3.round(r, 2);
23759 var y1 = d3.round(r / 2, 2);
23760 var x = d3.round(r * Math.sqrt(3) / 2, 2);
23761 return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
23762 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z';
23763 }
23764 },
23765 hexagon2: {
23766 n: 15,
23767 f: function(r) {
23768 var x0 = d3.round(r, 2);
23769 var x1 = d3.round(r / 2, 2);
23770 var y = d3.round(r * Math.sqrt(3) / 2, 2);
23771 return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
23772 ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z';
23773 }
23774 },
23775 octagon: {
23776 n: 16,
23777 f: function(r) {
23778 var a = d3.round(r * 0.924, 2);
23779 var b = d3.round(r * 0.383, 2);
23780 return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
23781 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z';
23782 }
23783 },
23784 star: {
23785 n: 17,
23786 f: function(r) {
23787 var rs = r * 1.4;
23788 var x1 = d3.round(rs * 0.225, 2);
23789 var x2 = d3.round(rs * 0.951, 2);
23790 var x3 = d3.round(rs * 0.363, 2);
23791 var x4 = d3.round(rs * 0.588, 2);
23792 var y0 = d3.round(-rs, 2);
23793 var y1 = d3.round(rs * -0.309, 2);
23794 var y3 = d3.round(rs * 0.118, 2);
23795 var y4 = d3.round(rs * 0.809, 2);
23796 var y5 = d3.round(rs * 0.382, 2);
23797 return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
23798 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 +
23799 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 +
23800 'L0,' + y0 + 'Z';
23801 }
23802 },
23803 hexagram: {
23804 n: 18,
23805 f: function(r) {
23806 var y = d3.round(r * 0.66, 2);
23807 var x1 = d3.round(r * 0.38, 2);
23808 var x2 = d3.round(r * 0.76, 2);
23809 return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
23810 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 +
23811 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 +
23812 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z';
23813 }
23814 },
23815 'star-triangle-up': {
23816 n: 19,
23817 f: function(r) {
23818 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
23819 var y1 = d3.round(r * 0.8, 2);
23820 var y2 = d3.round(r * 1.6, 2);
23821 var rc = d3.round(r * 4, 2);
23822 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
23823 return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
23824 aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z';
23825 }
23826 },
23827 'star-triangle-down': {
23828 n: 20,
23829 f: function(r) {
23830 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
23831 var y1 = d3.round(r * 0.8, 2);
23832 var y2 = d3.round(r * 1.6, 2);
23833 var rc = d3.round(r * 4, 2);
23834 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
23835 return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
23836 aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z';
23837 }
23838 },
23839 'star-square': {
23840 n: 21,
23841 f: function(r) {
23842 var rp = d3.round(r * 1.1, 2);
23843 var rc = d3.round(r * 2, 2);
23844 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
23845 return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
23846 aPart + rp + ',' + rp + aPart + rp + ',-' + rp +
23847 aPart + '-' + rp + ',-' + rp + 'Z';
23848 }
23849 },
23850 'star-diamond': {
23851 n: 22,
23852 f: function(r) {
23853 var rp = d3.round(r * 1.4, 2);
23854 var rc = d3.round(r * 1.9, 2);
23855 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
23856 return 'M-' + rp + ',0' + aPart + '0,' + rp +
23857 aPart + rp + ',0' + aPart + '0,-' + rp +
23858 aPart + '-' + rp + ',0' + 'Z';
23859 }
23860 },
23861 'diamond-tall': {
23862 n: 23,
23863 f: function(r) {
23864 var x = d3.round(r * 0.7, 2);
23865 var y = d3.round(r * 1.4, 2);
23866 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
23867 }
23868 },
23869 'diamond-wide': {
23870 n: 24,
23871 f: function(r) {
23872 var x = d3.round(r * 1.4, 2);
23873 var y = d3.round(r * 0.7, 2);
23874 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
23875 }
23876 },
23877 hourglass: {
23878 n: 25,
23879 f: function(r) {
23880 var rs = d3.round(r, 2);
23881 return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z';
23882 },
23883 noDot: true
23884 },
23885 bowtie: {
23886 n: 26,
23887 f: function(r) {
23888 var rs = d3.round(r, 2);
23889 return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z';
23890 },
23891 noDot: true
23892 },
23893 'circle-cross': {
23894 n: 27,
23895 f: function(r) {
23896 var rs = d3.round(r, 2);
23897 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
23898 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
23899 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
23900 },
23901 needLine: true,
23902 noDot: true
23903 },
23904 'circle-x': {
23905 n: 28,
23906 f: function(r) {
23907 var rs = d3.round(r, 2);
23908 var rc = d3.round(r / Math.sqrt(2), 2);
23909 return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
23910 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc +
23911 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
23912 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
23913 },
23914 needLine: true,
23915 noDot: true
23916 },
23917 'square-cross': {
23918 n: 29,
23919 f: function(r) {
23920 var rs = d3.round(r, 2);
23921 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
23922 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
23923 },
23924 needLine: true,
23925 noDot: true
23926 },
23927 'square-x': {
23928 n: 30,
23929 f: function(r) {
23930 var rs = d3.round(r, 2);
23931 return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
23932 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs +
23933 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
23934 },
23935 needLine: true,
23936 noDot: true
23937 },
23938 'diamond-cross': {
23939 n: 31,
23940 f: function(r) {
23941 var rd = d3.round(r * 1.3, 2);
23942 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
23943 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd;
23944 },
23945 needLine: true,
23946 noDot: true
23947 },
23948 'diamond-x': {
23949 n: 32,
23950 f: function(r) {
23951 var rd = d3.round(r * 1.3, 2);
23952 var r2 = d3.round(r * 0.65, 2);
23953 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
23954 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 +
23955 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2;
23956 },
23957 needLine: true,
23958 noDot: true
23959 },
23960 'cross-thin': {
23961 n: 33,
23962 f: function(r) {
23963 var rc = d3.round(r * 1.4, 2);
23964 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
23965 },
23966 needLine: true,
23967 noDot: true,
23968 noFill: true
23969 },
23970 'x-thin': {
23971 n: 34,
23972 f: function(r) {
23973 var rx = d3.round(r, 2);
23974 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
23975 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
23976 },
23977 needLine: true,
23978 noDot: true,
23979 noFill: true
23980 },
23981 asterisk: {
23982 n: 35,
23983 f: function(r) {
23984 var rc = d3.round(r * 1.2, 2);
23985 var rs = d3.round(r * 0.85, 2);
23986 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
23987 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
23988 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
23989 },
23990 needLine: true,
23991 noDot: true,
23992 noFill: true
23993 },
23994 hash: {
23995 n: 36,
23996 f: function(r) {
23997 var r1 = d3.round(r / 2, 2);
23998 var r2 = d3.round(r, 2);
23999 return 'M' + r1 + ',' + r2 + 'V-' + r2 +
24000 'm-' + r2 + ',0V' + r2 +
24001 'M' + r2 + ',' + r1 + 'H-' + r2 +
24002 'm0,-' + r2 + 'H' + r2;
24003 },
24004 needLine: true,
24005 noFill: true
24006 },
24007 'y-up': {
24008 n: 37,
24009 f: function(r) {
24010 var x = d3.round(r * 1.2, 2);
24011 var y0 = d3.round(r * 1.6, 2);
24012 var y1 = d3.round(r * 0.8, 2);
24013 return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
24014 },
24015 needLine: true,
24016 noDot: true,
24017 noFill: true
24018 },
24019 'y-down': {
24020 n: 38,
24021 f: function(r) {
24022 var x = d3.round(r * 1.2, 2);
24023 var y0 = d3.round(r * 1.6, 2);
24024 var y1 = d3.round(r * 0.8, 2);
24025 return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
24026 },
24027 needLine: true,
24028 noDot: true,
24029 noFill: true
24030 },
24031 'y-left': {
24032 n: 39,
24033 f: function(r) {
24034 var y = d3.round(r * 1.2, 2);
24035 var x0 = d3.round(r * 1.6, 2);
24036 var x1 = d3.round(r * 0.8, 2);
24037 return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
24038 },
24039 needLine: true,
24040 noDot: true,
24041 noFill: true
24042 },
24043 'y-right': {
24044 n: 40,
24045 f: function(r) {
24046 var y = d3.round(r * 1.2, 2);
24047 var x0 = d3.round(r * 1.6, 2);
24048 var x1 = d3.round(r * 0.8, 2);
24049 return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
24050 },
24051 needLine: true,
24052 noDot: true,
24053 noFill: true
24054 },
24055 'line-ew': {
24056 n: 41,
24057 f: function(r) {
24058 var rc = d3.round(r * 1.4, 2);
24059 return 'M' + rc + ',0H-' + rc;
24060 },
24061 needLine: true,
24062 noDot: true,
24063 noFill: true
24064 },
24065 'line-ns': {
24066 n: 42,
24067 f: function(r) {
24068 var rc = d3.round(r * 1.4, 2);
24069 return 'M0,' + rc + 'V-' + rc;
24070 },
24071 needLine: true,
24072 noDot: true,
24073 noFill: true
24074 },
24075 'line-ne': {
24076 n: 43,
24077 f: function(r) {
24078 var rx = d3.round(r, 2);
24079 return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
24080 },
24081 needLine: true,
24082 noDot: true,
24083 noFill: true
24084 },
24085 'line-nw': {
24086 n: 44,
24087 f: function(r) {
24088 var rx = d3.round(r, 2);
24089 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
24090 },
24091 needLine: true,
24092 noDot: true,
24093 noFill: true
24094 }
24095};
24096
24097},{"d3":13}],74:[function(_dereq_,module,exports){
24098/**
24099* Copyright 2012-2020, Plotly, Inc.
24100* All rights reserved.
24101*
24102* This source code is licensed under the MIT license found in the
24103* LICENSE file in the root directory of this source tree.
24104*/
24105
24106'use strict';
24107
24108
24109module.exports = {
24110 visible: {
24111 valType: 'boolean',
24112
24113 editType: 'calc',
24114
24115 },
24116 type: {
24117 valType: 'enumerated',
24118 values: ['percent', 'constant', 'sqrt', 'data'],
24119
24120 editType: 'calc',
24121
24122 },
24123 symmetric: {
24124 valType: 'boolean',
24125
24126 editType: 'calc',
24127
24128 },
24129 array: {
24130 valType: 'data_array',
24131 editType: 'calc',
24132
24133 },
24134 arrayminus: {
24135 valType: 'data_array',
24136 editType: 'calc',
24137
24138 },
24139 value: {
24140 valType: 'number',
24141 min: 0,
24142 dflt: 10,
24143
24144 editType: 'calc',
24145
24146 },
24147 valueminus: {
24148 valType: 'number',
24149 min: 0,
24150 dflt: 10,
24151
24152 editType: 'calc',
24153
24154 },
24155 traceref: {
24156 valType: 'integer',
24157 min: 0,
24158 dflt: 0,
24159
24160 editType: 'style'
24161 },
24162 tracerefminus: {
24163 valType: 'integer',
24164 min: 0,
24165 dflt: 0,
24166
24167 editType: 'style'
24168 },
24169 copy_ystyle: {
24170 valType: 'boolean',
24171
24172 editType: 'plot'
24173 },
24174 copy_zstyle: {
24175 valType: 'boolean',
24176
24177 editType: 'style'
24178 },
24179 color: {
24180 valType: 'color',
24181
24182 editType: 'style',
24183
24184 },
24185 thickness: {
24186 valType: 'number',
24187 min: 0,
24188 dflt: 2,
24189
24190 editType: 'style',
24191
24192 },
24193 width: {
24194 valType: 'number',
24195 min: 0,
24196
24197 editType: 'plot',
24198
24199 },
24200 editType: 'calc',
24201
24202 _deprecated: {
24203 opacity: {
24204 valType: 'number',
24205
24206 editType: 'style',
24207
24208 }
24209 }
24210};
24211
24212},{}],75:[function(_dereq_,module,exports){
24213/**
24214* Copyright 2012-2020, Plotly, Inc.
24215* All rights reserved.
24216*
24217* This source code is licensed under the MIT license found in the
24218* LICENSE file in the root directory of this source tree.
24219*/
24220
24221'use strict';
24222
24223var isNumeric = _dereq_('fast-isnumeric');
24224
24225var Registry = _dereq_('../../registry');
24226var Axes = _dereq_('../../plots/cartesian/axes');
24227var Lib = _dereq_('../../lib');
24228
24229var makeComputeError = _dereq_('./compute_error');
24230
24231module.exports = function calc(gd) {
24232 var calcdata = gd.calcdata;
24233
24234 for(var i = 0; i < calcdata.length; i++) {
24235 var calcTrace = calcdata[i];
24236 var trace = calcTrace[0].trace;
24237
24238 if(trace.visible === true && Registry.traceIs(trace, 'errorBarsOK')) {
24239 var xa = Axes.getFromId(gd, trace.xaxis);
24240 var ya = Axes.getFromId(gd, trace.yaxis);
24241 calcOneAxis(calcTrace, trace, xa, 'x');
24242 calcOneAxis(calcTrace, trace, ya, 'y');
24243 }
24244 }
24245};
24246
24247function calcOneAxis(calcTrace, trace, axis, coord) {
24248 var opts = trace['error_' + coord] || {};
24249 var isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1);
24250 var vals = [];
24251
24252 if(!isVisible) return;
24253
24254 var computeError = makeComputeError(opts);
24255
24256 for(var i = 0; i < calcTrace.length; i++) {
24257 var calcPt = calcTrace[i];
24258
24259 var iIn = calcPt.i;
24260
24261 // for types that don't include `i` in each calcdata point
24262 if(iIn === undefined) iIn = i;
24263
24264 // for stacked area inserted points
24265 // TODO: errorbars have been tested cursorily with stacked area,
24266 // but not thoroughly. It's not even really clear what you want to do:
24267 // Should it just be calculated based on that trace's size data?
24268 // Should you add errors from below in quadrature?
24269 // And what about normalization, where in principle the errors shrink
24270 // again when you get up to the top end?
24271 // One option would be to forbid errorbars with stacking until we
24272 // decide how to handle these questions.
24273 else if(iIn === null) continue;
24274
24275 var calcCoord = calcPt[coord];
24276
24277 if(!isNumeric(axis.c2l(calcCoord))) continue;
24278
24279 var errors = computeError(calcCoord, iIn);
24280 if(isNumeric(errors[0]) && isNumeric(errors[1])) {
24281 var shoe = calcPt[coord + 's'] = calcCoord - errors[0];
24282 var hat = calcPt[coord + 'h'] = calcCoord + errors[1];
24283 vals.push(shoe, hat);
24284 }
24285 }
24286
24287 var axId = axis._id;
24288 var baseExtremes = trace._extremes[axId];
24289 var extremes = Axes.findExtremes(
24290 axis,
24291 vals,
24292 Lib.extendFlat({tozero: baseExtremes.opts.tozero}, {padded: true})
24293 );
24294 baseExtremes.min = baseExtremes.min.concat(extremes.min);
24295 baseExtremes.max = baseExtremes.max.concat(extremes.max);
24296}
24297
24298},{"../../lib":177,"../../plots/cartesian/axes":222,"../../registry":272,"./compute_error":76,"fast-isnumeric":15}],76:[function(_dereq_,module,exports){
24299/**
24300* Copyright 2012-2020, Plotly, Inc.
24301* All rights reserved.
24302*
24303* This source code is licensed under the MIT license found in the
24304* LICENSE file in the root directory of this source tree.
24305*/
24306
24307
24308'use strict';
24309
24310
24311/**
24312 * Error bar computing function generator
24313 *
24314 * N.B. The generated function does not clean the dataPt entries. Non-numeric
24315 * entries result in undefined error magnitudes.
24316 *
24317 * @param {object} opts error bar attributes
24318 *
24319 * @return {function} :
24320 * @param {numeric} dataPt data point from where to compute the error magnitude
24321 * @param {number} index index of dataPt in its corresponding data array
24322 * @return {array}
24323 * - error[0] : error magnitude in the negative direction
24324 * - error[1] : " " " " positive "
24325 */
24326module.exports = function makeComputeError(opts) {
24327 var type = opts.type;
24328 var symmetric = opts.symmetric;
24329
24330 if(type === 'data') {
24331 var array = opts.array || [];
24332
24333 if(symmetric) {
24334 return function computeError(dataPt, index) {
24335 var val = +(array[index]);
24336 return [val, val];
24337 };
24338 } else {
24339 var arrayminus = opts.arrayminus || [];
24340 return function computeError(dataPt, index) {
24341 var val = +array[index];
24342 var valMinus = +arrayminus[index];
24343 // in case one is present and the other is missing, fill in 0
24344 // so we still see the present one. Mostly useful during manual
24345 // data entry.
24346 if(!isNaN(val) || !isNaN(valMinus)) {
24347 return [valMinus || 0, val || 0];
24348 }
24349 return [NaN, NaN];
24350 };
24351 }
24352 } else {
24353 var computeErrorValue = makeComputeErrorValue(type, opts.value);
24354 var computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus);
24355
24356 if(symmetric || opts.valueminus === undefined) {
24357 return function computeError(dataPt) {
24358 var val = computeErrorValue(dataPt);
24359 return [val, val];
24360 };
24361 } else {
24362 return function computeError(dataPt) {
24363 return [
24364 computeErrorValueMinus(dataPt),
24365 computeErrorValue(dataPt)
24366 ];
24367 };
24368 }
24369 }
24370};
24371
24372/**
24373 * Compute error bar magnitude (for all types except data)
24374 *
24375 * @param {string} type error bar type
24376 * @param {numeric} value error bar value
24377 *
24378 * @return {function} :
24379 * @param {numeric} dataPt
24380 */
24381function makeComputeErrorValue(type, value) {
24382 if(type === 'percent') {
24383 return function(dataPt) {
24384 return Math.abs(dataPt * value / 100);
24385 };
24386 }
24387 if(type === 'constant') {
24388 return function() {
24389 return Math.abs(value);
24390 };
24391 }
24392 if(type === 'sqrt') {
24393 return function(dataPt) {
24394 return Math.sqrt(Math.abs(dataPt));
24395 };
24396 }
24397}
24398
24399},{}],77:[function(_dereq_,module,exports){
24400/**
24401* Copyright 2012-2020, Plotly, Inc.
24402* All rights reserved.
24403*
24404* This source code is licensed under the MIT license found in the
24405* LICENSE file in the root directory of this source tree.
24406*/
24407
24408'use strict';
24409
24410var isNumeric = _dereq_('fast-isnumeric');
24411
24412var Registry = _dereq_('../../registry');
24413var Lib = _dereq_('../../lib');
24414var Template = _dereq_('../../plot_api/plot_template');
24415
24416var attributes = _dereq_('./attributes');
24417
24418
24419module.exports = function(traceIn, traceOut, defaultColor, opts) {
24420 var objName = 'error_' + opts.axis;
24421 var containerOut = Template.newContainer(traceOut, objName);
24422 var containerIn = traceIn[objName] || {};
24423
24424 function coerce(attr, dflt) {
24425 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
24426 }
24427
24428 var hasErrorBars = (
24429 containerIn.array !== undefined ||
24430 containerIn.value !== undefined ||
24431 containerIn.type === 'sqrt'
24432 );
24433
24434 var visible = coerce('visible', hasErrorBars);
24435
24436 if(visible === false) return;
24437
24438 var type = coerce('type', 'array' in containerIn ? 'data' : 'percent');
24439 var symmetric = true;
24440
24441 if(type !== 'sqrt') {
24442 symmetric = coerce('symmetric',
24443 !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn));
24444 }
24445
24446 if(type === 'data') {
24447 coerce('array');
24448 coerce('traceref');
24449 if(!symmetric) {
24450 coerce('arrayminus');
24451 coerce('tracerefminus');
24452 }
24453 } else if(type === 'percent' || type === 'constant') {
24454 coerce('value');
24455 if(!symmetric) coerce('valueminus');
24456 }
24457
24458 var copyAttr = 'copy_' + opts.inherit + 'style';
24459 if(opts.inherit) {
24460 var inheritObj = traceOut['error_' + opts.inherit];
24461 if((inheritObj || {}).visible) {
24462 coerce(copyAttr, !(containerIn.color ||
24463 isNumeric(containerIn.thickness) ||
24464 isNumeric(containerIn.width)));
24465 }
24466 }
24467 if(!opts.inherit || !containerOut[copyAttr]) {
24468 coerce('color', defaultColor);
24469 coerce('thickness');
24470 coerce('width', Registry.traceIs(traceOut, 'gl3d') ? 0 : 4);
24471 }
24472};
24473
24474},{"../../lib":177,"../../plot_api/plot_template":212,"../../registry":272,"./attributes":74,"fast-isnumeric":15}],78:[function(_dereq_,module,exports){
24475/**
24476* Copyright 2012-2020, Plotly, Inc.
24477* All rights reserved.
24478*
24479* This source code is licensed under the MIT license found in the
24480* LICENSE file in the root directory of this source tree.
24481*/
24482
24483'use strict';
24484
24485var Lib = _dereq_('../../lib');
24486var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
24487
24488var attributes = _dereq_('./attributes');
24489
24490var xyAttrs = {
24491 error_x: Lib.extendFlat({}, attributes),
24492 error_y: Lib.extendFlat({}, attributes)
24493};
24494delete xyAttrs.error_x.copy_zstyle;
24495delete xyAttrs.error_y.copy_zstyle;
24496delete xyAttrs.error_y.copy_ystyle;
24497
24498var xyzAttrs = {
24499 error_x: Lib.extendFlat({}, attributes),
24500 error_y: Lib.extendFlat({}, attributes),
24501 error_z: Lib.extendFlat({}, attributes)
24502};
24503delete xyzAttrs.error_x.copy_ystyle;
24504delete xyzAttrs.error_y.copy_ystyle;
24505delete xyzAttrs.error_z.copy_ystyle;
24506delete xyzAttrs.error_z.copy_zstyle;
24507
24508module.exports = {
24509 moduleType: 'component',
24510 name: 'errorbars',
24511
24512 schema: {
24513 traces: {
24514 scatter: xyAttrs,
24515 bar: xyAttrs,
24516 histogram: xyAttrs,
24517 scatter3d: overrideAll(xyzAttrs, 'calc', 'nested'),
24518 scattergl: overrideAll(xyAttrs, 'calc', 'nested')
24519 }
24520 },
24521
24522 supplyDefaults: _dereq_('./defaults'),
24523
24524 calc: _dereq_('./calc'),
24525 makeComputeError: _dereq_('./compute_error'),
24526
24527 plot: _dereq_('./plot'),
24528 style: _dereq_('./style'),
24529 hoverInfo: hoverInfo
24530};
24531
24532function hoverInfo(calcPoint, trace, hoverPoint) {
24533 if((trace.error_y || {}).visible) {
24534 hoverPoint.yerr = calcPoint.yh - calcPoint.y;
24535 if(!trace.error_y.symmetric) hoverPoint.yerrneg = calcPoint.y - calcPoint.ys;
24536 }
24537 if((trace.error_x || {}).visible) {
24538 hoverPoint.xerr = calcPoint.xh - calcPoint.x;
24539 if(!trace.error_x.symmetric) hoverPoint.xerrneg = calcPoint.x - calcPoint.xs;
24540 }
24541}
24542
24543},{"../../lib":177,"../../plot_api/edit_types":205,"./attributes":74,"./calc":75,"./compute_error":76,"./defaults":77,"./plot":79,"./style":80}],79:[function(_dereq_,module,exports){
24544/**
24545* Copyright 2012-2020, Plotly, Inc.
24546* All rights reserved.
24547*
24548* This source code is licensed under the MIT license found in the
24549* LICENSE file in the root directory of this source tree.
24550*/
24551
24552
24553'use strict';
24554
24555var d3 = _dereq_('d3');
24556var isNumeric = _dereq_('fast-isnumeric');
24557
24558var Drawing = _dereq_('../drawing');
24559var subTypes = _dereq_('../../traces/scatter/subtypes');
24560
24561module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
24562 var isNew;
24563
24564 var xa = plotinfo.xaxis;
24565 var ya = plotinfo.yaxis;
24566
24567 var hasAnimation = transitionOpts && transitionOpts.duration > 0;
24568
24569 traces.each(function(d) {
24570 var trace = d[0].trace;
24571 // || {} is in case the trace (specifically scatterternary)
24572 // doesn't support error bars at all, but does go through
24573 // the scatter.plot mechanics, which calls ErrorBars.plot
24574 // internally
24575 var xObj = trace.error_x || {};
24576 var yObj = trace.error_y || {};
24577
24578 var keyFunc;
24579
24580 if(trace.ids) {
24581 keyFunc = function(d) {return d.id;};
24582 }
24583
24584 var sparse = (
24585 subTypes.hasMarkers(trace) &&
24586 trace.marker.maxdisplayed > 0
24587 );
24588
24589 if(!yObj.visible && !xObj.visible) d = [];
24590
24591 var errorbars = d3.select(this).selectAll('g.errorbar')
24592 .data(d, keyFunc);
24593
24594 errorbars.exit().remove();
24595
24596 if(!d.length) return;
24597
24598 if(!xObj.visible) errorbars.selectAll('path.xerror').remove();
24599 if(!yObj.visible) errorbars.selectAll('path.yerror').remove();
24600
24601 errorbars.style('opacity', 1);
24602
24603 var enter = errorbars.enter().append('g')
24604 .classed('errorbar', true);
24605
24606 if(hasAnimation) {
24607 enter.style('opacity', 0).transition()
24608 .duration(transitionOpts.duration)
24609 .style('opacity', 1);
24610 }
24611
24612 Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
24613
24614 errorbars.each(function(d) {
24615 var errorbar = d3.select(this);
24616 var coords = errorCoords(d, xa, ya);
24617
24618 if(sparse && !d.vis) return;
24619
24620 var path;
24621
24622 var yerror = errorbar.select('path.yerror');
24623 if(yObj.visible && isNumeric(coords.x) &&
24624 isNumeric(coords.yh) &&
24625 isNumeric(coords.ys)) {
24626 var yw = yObj.width;
24627
24628 path = 'M' + (coords.x - yw) + ',' +
24629 coords.yh + 'h' + (2 * yw) + // hat
24630 'm-' + yw + ',0V' + coords.ys; // bar
24631
24632
24633 if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
24634
24635 isNew = !yerror.size();
24636
24637 if(isNew) {
24638 yerror = errorbar.append('path')
24639 .style('vector-effect', 'non-scaling-stroke')
24640 .classed('yerror', true);
24641 } else if(hasAnimation) {
24642 yerror = yerror
24643 .transition()
24644 .duration(transitionOpts.duration)
24645 .ease(transitionOpts.easing);
24646 }
24647
24648 yerror.attr('d', path);
24649 } else yerror.remove();
24650
24651 var xerror = errorbar.select('path.xerror');
24652 if(xObj.visible && isNumeric(coords.y) &&
24653 isNumeric(coords.xh) &&
24654 isNumeric(coords.xs)) {
24655 var xw = (xObj.copy_ystyle ? yObj : xObj).width;
24656
24657 path = 'M' + coords.xh + ',' +
24658 (coords.y - xw) + 'v' + (2 * xw) + // hat
24659 'm0,-' + xw + 'H' + coords.xs; // bar
24660
24661 if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
24662
24663 isNew = !xerror.size();
24664
24665 if(isNew) {
24666 xerror = errorbar.append('path')
24667 .style('vector-effect', 'non-scaling-stroke')
24668 .classed('xerror', true);
24669 } else if(hasAnimation) {
24670 xerror = xerror
24671 .transition()
24672 .duration(transitionOpts.duration)
24673 .ease(transitionOpts.easing);
24674 }
24675
24676 xerror.attr('d', path);
24677 } else xerror.remove();
24678 });
24679 });
24680};
24681
24682// compute the coordinates of the error-bar objects
24683function errorCoords(d, xa, ya) {
24684 var out = {
24685 x: xa.c2p(d.x),
24686 y: ya.c2p(d.y)
24687 };
24688
24689 // calculate the error bar size and hat and shoe locations
24690 if(d.yh !== undefined) {
24691 out.yh = ya.c2p(d.yh);
24692 out.ys = ya.c2p(d.ys);
24693
24694 // if the shoes go off-scale (ie log scale, error bars past zero)
24695 // clip the bar and hide the shoes
24696 if(!isNumeric(out.ys)) {
24697 out.noYS = true;
24698 out.ys = ya.c2p(d.ys, true);
24699 }
24700 }
24701
24702 if(d.xh !== undefined) {
24703 out.xh = xa.c2p(d.xh);
24704 out.xs = xa.c2p(d.xs);
24705
24706 if(!isNumeric(out.xs)) {
24707 out.noXS = true;
24708 out.xs = xa.c2p(d.xs, true);
24709 }
24710 }
24711
24712 return out;
24713}
24714
24715},{"../../traces/scatter/subtypes":318,"../drawing":72,"d3":13,"fast-isnumeric":15}],80:[function(_dereq_,module,exports){
24716/**
24717* Copyright 2012-2020, Plotly, Inc.
24718* All rights reserved.
24719*
24720* This source code is licensed under the MIT license found in the
24721* LICENSE file in the root directory of this source tree.
24722*/
24723
24724
24725'use strict';
24726
24727var d3 = _dereq_('d3');
24728
24729var Color = _dereq_('../color');
24730
24731
24732module.exports = function style(traces) {
24733 traces.each(function(d) {
24734 var trace = d[0].trace;
24735 var yObj = trace.error_y || {};
24736 var xObj = trace.error_x || {};
24737
24738 var s = d3.select(this);
24739
24740 s.selectAll('path.yerror')
24741 .style('stroke-width', yObj.thickness + 'px')
24742 .call(Color.stroke, yObj.color);
24743
24744 if(xObj.copy_ystyle) xObj = yObj;
24745
24746 s.selectAll('path.xerror')
24747 .style('stroke-width', xObj.thickness + 'px')
24748 .call(Color.stroke, xObj.color);
24749 });
24750};
24751
24752},{"../color":50,"d3":13}],81:[function(_dereq_,module,exports){
24753/**
24754* Copyright 2012-2020, Plotly, Inc.
24755* All rights reserved.
24756*
24757* This source code is licensed under the MIT license found in the
24758* LICENSE file in the root directory of this source tree.
24759*/
24760
24761'use strict';
24762
24763var fontAttrs = _dereq_('../../plots/font_attributes');
24764var hoverLabelAttrs = _dereq_('./layout_attributes').hoverlabel;
24765var extendFlat = _dereq_('../../lib/extend').extendFlat;
24766
24767module.exports = {
24768 hoverlabel: {
24769 bgcolor: extendFlat({}, hoverLabelAttrs.bgcolor, {
24770 arrayOk: true,
24771
24772 }),
24773 bordercolor: extendFlat({}, hoverLabelAttrs.bordercolor, {
24774 arrayOk: true,
24775
24776 }),
24777 font: fontAttrs({
24778 arrayOk: true,
24779 editType: 'none',
24780
24781 }),
24782 align: extendFlat({}, hoverLabelAttrs.align, {arrayOk: true}),
24783 namelength: extendFlat({}, hoverLabelAttrs.namelength, {arrayOk: true}),
24784 editType: 'none'
24785 }
24786};
24787
24788},{"../../lib/extend":170,"../../plots/font_attributes":250,"./layout_attributes":91}],82:[function(_dereq_,module,exports){
24789/**
24790* Copyright 2012-2020, Plotly, Inc.
24791* All rights reserved.
24792*
24793* This source code is licensed under the MIT license found in the
24794* LICENSE file in the root directory of this source tree.
24795*/
24796
24797'use strict';
24798
24799var Lib = _dereq_('../../lib');
24800var Registry = _dereq_('../../registry');
24801
24802module.exports = function calc(gd) {
24803 var calcdata = gd.calcdata;
24804 var fullLayout = gd._fullLayout;
24805
24806 function makeCoerceHoverInfo(trace) {
24807 return function(val) {
24808 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
24809 };
24810 }
24811
24812 for(var i = 0; i < calcdata.length; i++) {
24813 var cd = calcdata[i];
24814 var trace = cd[0].trace;
24815
24816 // don't include hover calc fields for pie traces
24817 // as calcdata items might be sorted by value and
24818 // won't match the data array order.
24819 if(Registry.traceIs(trace, 'pie-like')) continue;
24820
24821 var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray;
24822
24823 fillFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace));
24824
24825 if(trace.hovertemplate) fillFn(trace.hovertemplate, cd, 'ht');
24826
24827 if(!trace.hoverlabel) continue;
24828
24829 fillFn(trace.hoverlabel.bgcolor, cd, 'hbg');
24830 fillFn(trace.hoverlabel.bordercolor, cd, 'hbc');
24831 fillFn(trace.hoverlabel.font.size, cd, 'hts');
24832 fillFn(trace.hoverlabel.font.color, cd, 'htc');
24833 fillFn(trace.hoverlabel.font.family, cd, 'htf');
24834 fillFn(trace.hoverlabel.namelength, cd, 'hnl');
24835 fillFn(trace.hoverlabel.align, cd, 'hta');
24836 }
24837};
24838
24839function paste(traceAttr, cd, cdAttr, fn) {
24840 fn = fn || Lib.identity;
24841
24842 if(Array.isArray(traceAttr)) {
24843 cd[0][cdAttr] = fn(traceAttr);
24844 }
24845}
24846
24847},{"../../lib":177,"../../registry":272}],83:[function(_dereq_,module,exports){
24848/**
24849* Copyright 2012-2020, Plotly, Inc.
24850* All rights reserved.
24851*
24852* This source code is licensed under the MIT license found in the
24853* LICENSE file in the root directory of this source tree.
24854*/
24855
24856'use strict';
24857
24858var Registry = _dereq_('../../registry');
24859var hover = _dereq_('./hover').hover;
24860
24861module.exports = function click(gd, evt, subplot) {
24862 var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
24863
24864 // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
24865 // Ternary, for example, didn't, but it was caught because tested.
24866 if(subplot !== undefined) {
24867 // The true flag at the end causes it to re-run the hover computation to figure out *which*
24868 // point is being clicked. Without this, clicking is somewhat unreliable.
24869 hover(gd, evt, subplot, true);
24870 }
24871
24872 function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
24873
24874 if(gd._hoverdata && evt && evt.target) {
24875 if(annotationsDone && annotationsDone.then) {
24876 annotationsDone.then(emitClick);
24877 } else emitClick();
24878
24879 // why do we get a double event without this???
24880 if(evt.stopImmediatePropagation) evt.stopImmediatePropagation();
24881 }
24882};
24883
24884},{"../../registry":272,"./hover":87}],84:[function(_dereq_,module,exports){
24885/**
24886* Copyright 2012-2020, Plotly, Inc.
24887* All rights reserved.
24888*
24889* This source code is licensed under the MIT license found in the
24890* LICENSE file in the root directory of this source tree.
24891*/
24892
24893'use strict';
24894
24895module.exports = {
24896 // hover labels for multiple horizontal bars get tilted by this angle
24897 YANGLE: 60,
24898
24899 // size and display constants for hover text
24900
24901 // pixel size of hover arrows
24902 HOVERARROWSIZE: 6,
24903 // pixels padding around text
24904 HOVERTEXTPAD: 3,
24905 // hover font
24906 HOVERFONTSIZE: 13,
24907 HOVERFONT: 'Arial, sans-serif',
24908
24909 // minimum time (msec) between hover calls
24910 HOVERMINTIME: 50,
24911
24912 // ID suffix (with fullLayout._uid) for hover events in the throttle cache
24913 HOVERID: '-hover'
24914};
24915
24916},{}],85:[function(_dereq_,module,exports){
24917/**
24918* Copyright 2012-2020, Plotly, Inc.
24919* All rights reserved.
24920*
24921* This source code is licensed under the MIT license found in the
24922* LICENSE file in the root directory of this source tree.
24923*/
24924
24925'use strict';
24926
24927var Lib = _dereq_('../../lib');
24928var attributes = _dereq_('./attributes');
24929var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
24930
24931module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
24932 function coerce(attr, dflt) {
24933 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
24934 }
24935
24936 var opts = Lib.extendFlat({}, layout.hoverlabel);
24937 if(traceOut.hovertemplate) opts.namelength = -1;
24938
24939 handleHoverLabelDefaults(traceIn, traceOut, coerce, opts);
24940};
24941
24942},{"../../lib":177,"./attributes":81,"./hoverlabel_defaults":88}],86:[function(_dereq_,module,exports){
24943/**
24944* Copyright 2012-2020, Plotly, Inc.
24945* All rights reserved.
24946*
24947* This source code is licensed under the MIT license found in the
24948* LICENSE file in the root directory of this source tree.
24949*/
24950
24951'use strict';
24952
24953var Lib = _dereq_('../../lib');
24954
24955// look for either subplot or xaxis and yaxis attributes
24956// does not handle splom case
24957exports.getSubplot = function(trace) {
24958 return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo;
24959};
24960
24961// is trace in given list of subplots?
24962// does handle splom case
24963exports.isTraceInSubplots = function(trace, subplots) {
24964 if(trace.type === 'splom') {
24965 var xaxes = trace.xaxes || [];
24966 var yaxes = trace.yaxes || [];
24967 for(var i = 0; i < xaxes.length; i++) {
24968 for(var j = 0; j < yaxes.length; j++) {
24969 if(subplots.indexOf(xaxes[i] + yaxes[j]) !== -1) {
24970 return true;
24971 }
24972 }
24973 }
24974 return false;
24975 }
24976
24977 return subplots.indexOf(exports.getSubplot(trace)) !== -1;
24978};
24979
24980// convenience functions for mapping all relevant axes
24981exports.flat = function(subplots, v) {
24982 var out = new Array(subplots.length);
24983 for(var i = 0; i < subplots.length; i++) {
24984 out[i] = v;
24985 }
24986 return out;
24987};
24988
24989exports.p2c = function(axArray, v) {
24990 var out = new Array(axArray.length);
24991 for(var i = 0; i < axArray.length; i++) {
24992 out[i] = axArray[i].p2c(v);
24993 }
24994 return out;
24995};
24996
24997exports.getDistanceFunction = function(mode, dx, dy, dxy) {
24998 if(mode === 'closest') return dxy || exports.quadrature(dx, dy);
24999 return mode.charAt(0) === 'x' ? dx : dy;
25000};
25001
25002exports.getClosest = function(cd, distfn, pointData) {
25003 // do we already have a point number? (array mode only)
25004 if(pointData.index !== false) {
25005 if(pointData.index >= 0 && pointData.index < cd.length) {
25006 pointData.distance = 0;
25007 } else pointData.index = false;
25008 } else {
25009 // apply the distance function to each data point
25010 // this is the longest loop... if this bogs down, we may need
25011 // to create pre-sorted data (by x or y), not sure how to
25012 // do this for 'closest'
25013 for(var i = 0; i < cd.length; i++) {
25014 var newDistance = distfn(cd[i]);
25015 if(newDistance <= pointData.distance) {
25016 pointData.index = i;
25017 pointData.distance = newDistance;
25018 }
25019 }
25020 }
25021 return pointData;
25022};
25023
25024/*
25025 * pseudo-distance function for hover effects on areas: inside the region
25026 * distance is finite (`passVal`), outside it's Infinity.
25027 *
25028 * @param {number} v0: signed difference between the current position and the left edge
25029 * @param {number} v1: signed difference between the current position and the right edge
25030 * @param {number} passVal: the value to return on success
25031 */
25032exports.inbox = function(v0, v1, passVal) {
25033 return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity;
25034};
25035
25036exports.quadrature = function(dx, dy) {
25037 return function(di) {
25038 var x = dx(di);
25039 var y = dy(di);
25040 return Math.sqrt(x * x + y * y);
25041 };
25042};
25043
25044/** Fill event data point object for hover and selection.
25045 * Invokes _module.eventData if present.
25046 *
25047 * N.B. note that point 'index' corresponds to input data array index
25048 * whereas 'number' is its post-transform version.
25049 *
25050 * If the hovered/selected pt corresponds to an multiple input points
25051 * (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices'
25052 * are include in the event data.
25053 *
25054 * @param {object} pt
25055 * @param {object} trace
25056 * @param {object} cd
25057 * @return {object}
25058 */
25059exports.makeEventData = function(pt, trace, cd) {
25060 // hover uses 'index', select uses 'pointNumber'
25061 var pointNumber = 'index' in pt ? pt.index : pt.pointNumber;
25062
25063 var out = {
25064 data: trace._input,
25065 fullData: trace,
25066 curveNumber: trace.index,
25067 pointNumber: pointNumber
25068 };
25069
25070 if(trace._indexToPoints) {
25071 var pointIndices = trace._indexToPoints[pointNumber];
25072
25073 if(pointIndices.length === 1) {
25074 out.pointIndex = pointIndices[0];
25075 } else {
25076 out.pointIndices = pointIndices;
25077 }
25078 } else {
25079 out.pointIndex = pointNumber;
25080 }
25081
25082 if(trace._module.eventData) {
25083 out = trace._module.eventData(out, pt, trace, cd, pointNumber);
25084 } else {
25085 if('xVal' in pt) out.x = pt.xVal;
25086 else if('x' in pt) out.x = pt.x;
25087
25088 if('yVal' in pt) out.y = pt.yVal;
25089 else if('y' in pt) out.y = pt.y;
25090
25091 if(pt.xa) out.xaxis = pt.xa;
25092 if(pt.ya) out.yaxis = pt.ya;
25093 if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
25094 }
25095
25096 exports.appendArrayPointValue(out, trace, pointNumber);
25097
25098 return out;
25099};
25100
25101/** Appends values inside array attributes corresponding to given point number
25102 *
25103 * @param {object} pointData : point data object (gets mutated here)
25104 * @param {object} trace : full trace object
25105 * @param {number|Array(number)} pointNumber : point number. May be a length-2 array
25106 * [row, col] to dig into 2D arrays
25107 */
25108exports.appendArrayPointValue = function(pointData, trace, pointNumber) {
25109 var arrayAttrs = trace._arrayAttrs;
25110
25111 if(!arrayAttrs) {
25112 return;
25113 }
25114
25115 for(var i = 0; i < arrayAttrs.length; i++) {
25116 var astr = arrayAttrs[i];
25117 var key = getPointKey(astr);
25118
25119 if(pointData[key] === undefined) {
25120 var val = Lib.nestedProperty(trace, astr).get();
25121 var pointVal = getPointData(val, pointNumber);
25122
25123 if(pointVal !== undefined) pointData[key] = pointVal;
25124 }
25125 }
25126};
25127
25128/**
25129 * Appends values inside array attributes corresponding to given point number array
25130 * For use when pointData references a plot entity that arose (or potentially arose)
25131 * from multiple points in the input data
25132 *
25133 * @param {object} pointData : point data object (gets mutated here)
25134 * @param {object} trace : full trace object
25135 * @param {Array(number)|Array(Array(number))} pointNumbers : Array of point numbers.
25136 * Each entry in the array may itself be a length-2 array [row, col] to dig into 2D arrays
25137 */
25138exports.appendArrayMultiPointValues = function(pointData, trace, pointNumbers) {
25139 var arrayAttrs = trace._arrayAttrs;
25140
25141 if(!arrayAttrs) {
25142 return;
25143 }
25144
25145 for(var i = 0; i < arrayAttrs.length; i++) {
25146 var astr = arrayAttrs[i];
25147 var key = getPointKey(astr);
25148
25149 if(pointData[key] === undefined) {
25150 var val = Lib.nestedProperty(trace, astr).get();
25151 var keyVal = new Array(pointNumbers.length);
25152
25153 for(var j = 0; j < pointNumbers.length; j++) {
25154 keyVal[j] = getPointData(val, pointNumbers[j]);
25155 }
25156 pointData[key] = keyVal;
25157 }
25158 }
25159};
25160
25161var pointKeyMap = {
25162 ids: 'id',
25163 locations: 'location',
25164 labels: 'label',
25165 values: 'value',
25166 'marker.colors': 'color',
25167 parents: 'parent'
25168};
25169
25170function getPointKey(astr) {
25171 return pointKeyMap[astr] || astr;
25172}
25173
25174function getPointData(val, pointNumber) {
25175 if(Array.isArray(pointNumber)) {
25176 if(Array.isArray(val) && Array.isArray(val[pointNumber[0]])) {
25177 return val[pointNumber[0]][pointNumber[1]];
25178 }
25179 } else {
25180 return val[pointNumber];
25181 }
25182}
25183
25184var xyHoverMode = {
25185 x: true,
25186 y: true
25187};
25188
25189var unifiedHoverMode = {
25190 'x unified': true,
25191 'y unified': true
25192};
25193
25194exports.isUnifiedHover = function(hovermode) {
25195 if(typeof hovermode !== 'string') return false;
25196 return !!unifiedHoverMode[hovermode];
25197};
25198
25199exports.isXYhover = function(hovermode) {
25200 if(typeof hovermode !== 'string') return false;
25201 return !!xyHoverMode[hovermode];
25202};
25203
25204},{"../../lib":177}],87:[function(_dereq_,module,exports){
25205/**
25206* Copyright 2012-2020, Plotly, Inc.
25207* All rights reserved.
25208*
25209* This source code is licensed under the MIT license found in the
25210* LICENSE file in the root directory of this source tree.
25211*/
25212
25213'use strict';
25214
25215var d3 = _dereq_('d3');
25216var isNumeric = _dereq_('fast-isnumeric');
25217var tinycolor = _dereq_('tinycolor2');
25218
25219var Lib = _dereq_('../../lib');
25220var Events = _dereq_('../../lib/events');
25221var svgTextUtils = _dereq_('../../lib/svg_text_utils');
25222var overrideCursor = _dereq_('../../lib/override_cursor');
25223var Drawing = _dereq_('../drawing');
25224var Color = _dereq_('../color');
25225var dragElement = _dereq_('../dragelement');
25226var Axes = _dereq_('../../plots/cartesian/axes');
25227var Registry = _dereq_('../../registry');
25228
25229var helpers = _dereq_('./helpers');
25230var constants = _dereq_('./constants');
25231
25232var legendSupplyDefaults = _dereq_('../legend/defaults');
25233var legendDraw = _dereq_('../legend/draw');
25234
25235// hover labels for multiple horizontal bars get tilted by some angle,
25236// then need to be offset differently if they overlap
25237var YANGLE = constants.YANGLE;
25238var YA_RADIANS = Math.PI * YANGLE / 180;
25239
25240// expansion of projected height
25241var YFACTOR = 1 / Math.sin(YA_RADIANS);
25242
25243// to make the appropriate post-rotation x offset,
25244// you need both x and y offsets
25245var YSHIFTX = Math.cos(YA_RADIANS);
25246var YSHIFTY = Math.sin(YA_RADIANS);
25247
25248// size and display constants for hover text
25249var HOVERARROWSIZE = constants.HOVERARROWSIZE;
25250var HOVERTEXTPAD = constants.HOVERTEXTPAD;
25251
25252// fx.hover: highlight data on hover
25253// evt can be a mousemove event, or an object with data about what points
25254// to hover on
25255// {xpx,ypx[,hovermode]} - pixel locations from top left
25256// (with optional overriding hovermode)
25257// {xval,yval[,hovermode]} - data values
25258// [{curveNumber,(pointNumber|xval and/or yval)}] -
25259// array of specific points to highlight
25260// pointNumber is a single integer if gd.data[curveNumber] is 1D,
25261// or a two-element array if it's 2D
25262// xval and yval are data values,
25263// 1D data may specify either or both,
25264// 2D data must specify both
25265// subplot is an id string (default "xy")
25266// makes use of gl.hovermode, which can be:
25267// x (find the points with the closest x values, ie a column),
25268// closest (find the single closest point)
25269// internally there are two more that occasionally get used:
25270// y (pick out a row - only used for multiple horizontal bar charts)
25271// array (used when the user specifies an explicit
25272// array of points to hover on)
25273//
25274// We wrap the hovers in a timer, to limit their frequency.
25275// The actual rendering is done by private function _hover.
25276exports.hover = function hover(gd, evt, subplot, noHoverEvent) {
25277 gd = Lib.getGraphDiv(gd);
25278
25279 Lib.throttle(
25280 gd._fullLayout._uid + constants.HOVERID,
25281 constants.HOVERMINTIME,
25282 function() { _hover(gd, evt, subplot, noHoverEvent); }
25283 );
25284};
25285
25286/*
25287 * Draw a single hover item or an array of hover item in a pre-existing svg container somewhere
25288 * hoverItem should have keys:
25289 * - x and y (or x0, x1, y0, and y1):
25290 * the pixel position to mark, relative to opts.container
25291 * - xLabel, yLabel, zLabel, text, and name:
25292 * info to go in the label
25293 * - color:
25294 * the background color for the label.
25295 * - idealAlign (optional):
25296 * 'left' or 'right' for which side of the x/y box to try to put this on first
25297 * - borderColor (optional):
25298 * color for the border, defaults to strongest contrast with color
25299 * - fontFamily (optional):
25300 * string, the font for this label, defaults to constants.HOVERFONT
25301 * - fontSize (optional):
25302 * the label font size, defaults to constants.HOVERFONTSIZE
25303 * - fontColor (optional):
25304 * defaults to borderColor
25305 * opts should have keys:
25306 * - bgColor:
25307 * the background color this is against, used if the trace is
25308 * non-opaque, and for the name, which goes outside the box
25309 * - container:
25310 * a <svg> or <g> element to add the hover label to
25311 * - outerContainer:
25312 * normally a parent of `container`, sets the bounding box to use to
25313 * constrain the hover label and determine whether to show it on the left or right
25314 * opts can have optional keys:
25315 * - anchorIndex:
25316 the index of the hover item used as an anchor for positioning.
25317 The other hover items will be pushed up or down to prevent overlap.
25318 */
25319exports.loneHover = function loneHover(hoverItems, opts) {
25320 var multiHover = true;
25321 if(!Array.isArray(hoverItems)) {
25322 multiHover = false;
25323 hoverItems = [hoverItems];
25324 }
25325
25326 var pointsData = hoverItems.map(function(hoverItem) {
25327 return {
25328 color: hoverItem.color || Color.defaultLine,
25329 x0: hoverItem.x0 || hoverItem.x || 0,
25330 x1: hoverItem.x1 || hoverItem.x || 0,
25331 y0: hoverItem.y0 || hoverItem.y || 0,
25332 y1: hoverItem.y1 || hoverItem.y || 0,
25333 xLabel: hoverItem.xLabel,
25334 yLabel: hoverItem.yLabel,
25335 zLabel: hoverItem.zLabel,
25336 text: hoverItem.text,
25337 name: hoverItem.name,
25338 idealAlign: hoverItem.idealAlign,
25339
25340 // optional extra bits of styling
25341 borderColor: hoverItem.borderColor,
25342 fontFamily: hoverItem.fontFamily,
25343 fontSize: hoverItem.fontSize,
25344 fontColor: hoverItem.fontColor,
25345 nameLength: hoverItem.nameLength,
25346 textAlign: hoverItem.textAlign,
25347
25348 // filler to make createHoverText happy
25349 trace: hoverItem.trace || {
25350 index: 0,
25351 hoverinfo: ''
25352 },
25353 xa: {_offset: 0},
25354 ya: {_offset: 0},
25355 index: 0,
25356
25357 hovertemplate: hoverItem.hovertemplate || false,
25358 eventData: hoverItem.eventData || false,
25359 hovertemplateLabels: hoverItem.hovertemplateLabels || false,
25360 };
25361 });
25362
25363 var container3 = d3.select(opts.container);
25364 var outerContainer3 = opts.outerContainer ? d3.select(opts.outerContainer) : container3;
25365
25366 var fullOpts = {
25367 hovermode: 'closest',
25368 rotateLabels: false,
25369 bgColor: opts.bgColor || Color.background,
25370 container: container3,
25371 outerContainer: outerContainer3
25372 };
25373
25374 var hoverLabel = createHoverText(pointsData, fullOpts, opts.gd);
25375
25376 // Fix vertical overlap
25377 var tooltipSpacing = 5;
25378 var lastBottomY = 0;
25379 var anchor = 0;
25380 hoverLabel
25381 .sort(function(a, b) {return a.y0 - b.y0;})
25382 .each(function(d, i) {
25383 var topY = d.y0 - d.by / 2;
25384
25385 if((topY - tooltipSpacing) < lastBottomY) {
25386 d.offset = (lastBottomY - topY) + tooltipSpacing;
25387 } else {
25388 d.offset = 0;
25389 }
25390
25391 lastBottomY = topY + d.by + d.offset;
25392
25393 if(i === opts.anchorIndex || 0) anchor = d.offset;
25394 })
25395 .each(function(d) {
25396 d.offset -= anchor;
25397 });
25398
25399 alignHoverText(hoverLabel, fullOpts.rotateLabels);
25400
25401 return multiHover ? hoverLabel : hoverLabel.node();
25402};
25403
25404// The actual implementation is here:
25405function _hover(gd, evt, subplot, noHoverEvent) {
25406 if(!subplot) subplot = 'xy';
25407
25408 // if the user passed in an array of subplots,
25409 // use those instead of finding overlayed plots
25410 var subplots = Array.isArray(subplot) ? subplot : [subplot];
25411
25412 var fullLayout = gd._fullLayout;
25413 var plots = fullLayout._plots || [];
25414 var plotinfo = plots[subplot];
25415 var hasCartesian = fullLayout._has('cartesian');
25416
25417 // list of all overlaid subplots to look at
25418 if(plotinfo) {
25419 var overlayedSubplots = plotinfo.overlays.map(function(pi) {
25420 return pi.id;
25421 });
25422
25423 subplots = subplots.concat(overlayedSubplots);
25424 }
25425
25426 var len = subplots.length;
25427 var xaArray = new Array(len);
25428 var yaArray = new Array(len);
25429 var supportsCompare = false;
25430
25431 for(var i = 0; i < len; i++) {
25432 var spId = subplots[i];
25433
25434 if(plots[spId]) {
25435 // 'cartesian' case
25436 supportsCompare = true;
25437 xaArray[i] = plots[spId].xaxis;
25438 yaArray[i] = plots[spId].yaxis;
25439 } else if(fullLayout[spId] && fullLayout[spId]._subplot) {
25440 // other subplot types
25441 var _subplot = fullLayout[spId]._subplot;
25442 xaArray[i] = _subplot.xaxis;
25443 yaArray[i] = _subplot.yaxis;
25444 } else {
25445 Lib.warn('Unrecognized subplot: ' + spId);
25446 return;
25447 }
25448 }
25449
25450 var hovermode = evt.hovermode || fullLayout.hovermode;
25451
25452 if(hovermode && !supportsCompare) hovermode = 'closest';
25453
25454 if(['x', 'y', 'closest', 'x unified', 'y unified'].indexOf(hovermode) === -1 || !gd.calcdata ||
25455 gd.querySelector('.zoombox') || gd._dragging) {
25456 return dragElement.unhoverRaw(gd, evt);
25457 }
25458
25459 var hoverdistance = fullLayout.hoverdistance === -1 ? Infinity : fullLayout.hoverdistance;
25460 var spikedistance = fullLayout.spikedistance === -1 ? Infinity : fullLayout.spikedistance;
25461
25462 // hoverData: the set of candidate points we've found to highlight
25463 var hoverData = [];
25464
25465 // searchData: the data to search in. Mostly this is just a copy of
25466 // gd.calcdata, filtered to the subplot and overlays we're on
25467 // but if a point array is supplied it will be a mapping
25468 // of indicated curves
25469 var searchData = [];
25470
25471 // [x|y]valArray: the axis values of the hover event
25472 // mapped onto each of the currently selected overlaid subplots
25473 var xvalArray, yvalArray;
25474
25475 var itemnum, curvenum, cd, trace, subplotId, subploti, mode,
25476 xval, yval, pointData, closedataPreviousLength;
25477
25478 // spikePoints: the set of candidate points we've found to draw spikes to
25479 var spikePoints = {
25480 hLinePoint: null,
25481 vLinePoint: null
25482 };
25483
25484 // does subplot have one (or more) horizontal traces?
25485 // This is used to determine whether we rotate the labels or not
25486 var hasOneHorizontalTrace = false;
25487
25488 // Figure out what we're hovering on:
25489 // mouse location or user-supplied data
25490
25491 if(Array.isArray(evt)) {
25492 // user specified an array of points to highlight
25493 hovermode = 'array';
25494 for(itemnum = 0; itemnum < evt.length; itemnum++) {
25495 cd = gd.calcdata[evt[itemnum].curveNumber || 0];
25496 if(cd) {
25497 trace = cd[0].trace;
25498 if(cd[0].trace.hoverinfo !== 'skip') {
25499 searchData.push(cd);
25500 if(trace.orientation === 'h') {
25501 hasOneHorizontalTrace = true;
25502 }
25503 }
25504 }
25505 }
25506 } else {
25507 for(curvenum = 0; curvenum < gd.calcdata.length; curvenum++) {
25508 cd = gd.calcdata[curvenum];
25509 trace = cd[0].trace;
25510 if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) {
25511 searchData.push(cd);
25512 if(trace.orientation === 'h') {
25513 hasOneHorizontalTrace = true;
25514 }
25515 }
25516 }
25517
25518 // [x|y]px: the pixels (from top left) of the mouse location
25519 // on the currently selected plot area
25520 // add pointerX|Y property for drawing the spikes in spikesnap 'cursor' situation
25521 var hasUserCalledHover = !evt.target;
25522 var xpx, ypx;
25523
25524 if(hasUserCalledHover) {
25525 if('xpx' in evt) xpx = evt.xpx;
25526 else xpx = xaArray[0]._length / 2;
25527
25528 if('ypx' in evt) ypx = evt.ypx;
25529 else ypx = yaArray[0]._length / 2;
25530 } else {
25531 // fire the beforehover event and quit if it returns false
25532 // note that we're only calling this on real mouse events, so
25533 // manual calls to fx.hover will always run.
25534 if(Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
25535 return;
25536 }
25537
25538 var dbb = evt.target.getBoundingClientRect();
25539
25540 xpx = evt.clientX - dbb.left;
25541 ypx = evt.clientY - dbb.top;
25542
25543 // in case hover was called from mouseout into hovertext,
25544 // it's possible you're not actually over the plot anymore
25545 if(xpx < 0 || xpx > xaArray[0]._length || ypx < 0 || ypx > yaArray[0]._length) {
25546 return dragElement.unhoverRaw(gd, evt);
25547 }
25548 }
25549
25550 evt.pointerX = xpx + xaArray[0]._offset;
25551 evt.pointerY = ypx + yaArray[0]._offset;
25552
25553 if('xval' in evt) xvalArray = helpers.flat(subplots, evt.xval);
25554 else xvalArray = helpers.p2c(xaArray, xpx);
25555
25556 if('yval' in evt) yvalArray = helpers.flat(subplots, evt.yval);
25557 else yvalArray = helpers.p2c(yaArray, ypx);
25558
25559 if(!isNumeric(xvalArray[0]) || !isNumeric(yvalArray[0])) {
25560 Lib.warn('Fx.hover failed', evt, gd);
25561 return dragElement.unhoverRaw(gd, evt);
25562 }
25563 }
25564
25565 // the pixel distance to beat as a matching point
25566 // in 'x' or 'y' mode this resets for each trace
25567 var distance = Infinity;
25568
25569 // find the closest point in each trace
25570 // this is minimum dx and/or dy, depending on mode
25571 // and the pixel position for the label (labelXpx, labelYpx)
25572 function findHoverPoints(customXVal, customYVal) {
25573 for(curvenum = 0; curvenum < searchData.length; curvenum++) {
25574 cd = searchData[curvenum];
25575
25576 // filter out invisible or broken data
25577 if(!cd || !cd[0] || !cd[0].trace) continue;
25578
25579 trace = cd[0].trace;
25580
25581 if(trace.visible !== true || trace._length === 0) continue;
25582
25583 // Explicitly bail out for these two. I don't know how to otherwise prevent
25584 // the rest of this function from running and failing
25585 if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue;
25586
25587 if(trace.type === 'splom') {
25588 // splom traces do not generate overlay subplots,
25589 // it is safe to assume here splom traces correspond to the 0th subplot
25590 subploti = 0;
25591 subplotId = subplots[subploti];
25592 } else {
25593 subplotId = helpers.getSubplot(trace);
25594 subploti = subplots.indexOf(subplotId);
25595 }
25596
25597 // within one trace mode can sometimes be overridden
25598 mode = hovermode;
25599 if(helpers.isUnifiedHover(mode)) {
25600 mode = mode.charAt(0);
25601 }
25602
25603 // container for new point, also used to pass info into module.hoverPoints
25604 pointData = {
25605 // trace properties
25606 cd: cd,
25607 trace: trace,
25608 xa: xaArray[subploti],
25609 ya: yaArray[subploti],
25610
25611 // max distances for hover and spikes - for points that want to show but do not
25612 // want to override other points, set distance/spikeDistance equal to max*Distance
25613 // and it will not get filtered out but it will be guaranteed to have a greater
25614 // distance than any point that calculated a real distance.
25615 maxHoverDistance: hoverdistance,
25616 maxSpikeDistance: spikedistance,
25617
25618 // point properties - override all of these
25619 index: false, // point index in trace - only used by plotly.js hoverdata consumers
25620 distance: Math.min(distance, hoverdistance), // pixel distance or pseudo-distance
25621
25622 // distance/pseudo-distance for spikes. This distance should always be calculated
25623 // as if in "closest" mode, and should only be set if this point should
25624 // generate a spike.
25625 spikeDistance: Infinity,
25626
25627 // in some cases the spikes have different positioning from the hover label
25628 // they don't need x0/x1, just one position
25629 xSpike: undefined,
25630 ySpike: undefined,
25631
25632 // where and how to display the hover label
25633 color: Color.defaultLine, // trace color
25634 name: trace.name,
25635 x0: undefined,
25636 x1: undefined,
25637 y0: undefined,
25638 y1: undefined,
25639 xLabelVal: undefined,
25640 yLabelVal: undefined,
25641 zLabelVal: undefined,
25642 text: undefined
25643 };
25644
25645 // add ref to subplot object (non-cartesian case)
25646 if(fullLayout[subplotId]) {
25647 pointData.subplot = fullLayout[subplotId]._subplot;
25648 }
25649 // add ref to splom scene
25650 if(fullLayout._splomScenes && fullLayout._splomScenes[trace.uid]) {
25651 pointData.scene = fullLayout._splomScenes[trace.uid];
25652 }
25653
25654 closedataPreviousLength = hoverData.length;
25655
25656 // for a highlighting array, figure out what
25657 // we're searching for with this element
25658 if(mode === 'array') {
25659 var selection = evt[curvenum];
25660 if('pointNumber' in selection) {
25661 pointData.index = selection.pointNumber;
25662 mode = 'closest';
25663 } else {
25664 mode = '';
25665 if('xval' in selection) {
25666 xval = selection.xval;
25667 mode = 'x';
25668 }
25669 if('yval' in selection) {
25670 yval = selection.yval;
25671 mode = mode ? 'closest' : 'y';
25672 }
25673 }
25674 } else if(customXVal !== undefined && customYVal !== undefined) {
25675 xval = customXVal;
25676 yval = customYVal;
25677 } else {
25678 xval = xvalArray[subploti];
25679 yval = yvalArray[subploti];
25680 }
25681
25682 // Now if there is range to look in, find the points to hover.
25683 if(hoverdistance !== 0) {
25684 if(trace._module && trace._module.hoverPoints) {
25685 var newPoints = trace._module.hoverPoints(pointData, xval, yval, mode, fullLayout._hoverlayer);
25686 if(newPoints) {
25687 var newPoint;
25688 for(var newPointNum = 0; newPointNum < newPoints.length; newPointNum++) {
25689 newPoint = newPoints[newPointNum];
25690 if(isNumeric(newPoint.x0) && isNumeric(newPoint.y0)) {
25691 hoverData.push(cleanPoint(newPoint, hovermode));
25692 }
25693 }
25694 }
25695 } else {
25696 Lib.log('Unrecognized trace type in hover:', trace);
25697 }
25698 }
25699
25700 // in closest mode, remove any existing (farther) points
25701 // and don't look any farther than this latest point (or points, some
25702 // traces like box & violin make multiple hover labels at once)
25703 if(hovermode === 'closest' && hoverData.length > closedataPreviousLength) {
25704 hoverData.splice(0, closedataPreviousLength);
25705 distance = hoverData[0].distance;
25706 }
25707
25708 // Now if there is range to look in, find the points to draw the spikelines
25709 // Do it only if there is no hoverData
25710 if(hasCartesian && (spikedistance !== 0)) {
25711 if(hoverData.length === 0) {
25712 pointData.distance = spikedistance;
25713 pointData.index = false;
25714 var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', fullLayout._hoverlayer);
25715 if(closestPoints) {
25716 closestPoints = closestPoints.filter(function(point) {
25717 // some hover points, like scatter fills, do not allow spikes,
25718 // so will generate a hover point but without a valid spikeDistance
25719 return point.spikeDistance <= spikedistance;
25720 });
25721 }
25722 if(closestPoints && closestPoints.length) {
25723 var tmpPoint;
25724 var closestVPoints = closestPoints.filter(function(point) {
25725 return point.xa.showspikes && point.xa.spikesnap !== 'hovered data';
25726 });
25727 if(closestVPoints.length) {
25728 var closestVPt = closestVPoints[0];
25729 if(isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
25730 tmpPoint = fillSpikePoint(closestVPt);
25731 if(!spikePoints.vLinePoint || (spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
25732 spikePoints.vLinePoint = tmpPoint;
25733 }
25734 }
25735 }
25736
25737 var closestHPoints = closestPoints.filter(function(point) {
25738 return point.ya.showspikes && point.ya.spikesnap !== 'hovered data';
25739 });
25740 if(closestHPoints.length) {
25741 var closestHPt = closestHPoints[0];
25742 if(isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
25743 tmpPoint = fillSpikePoint(closestHPt);
25744 if(!spikePoints.hLinePoint || (spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
25745 spikePoints.hLinePoint = tmpPoint;
25746 }
25747 }
25748 }
25749 }
25750 }
25751 }
25752 }
25753 }
25754
25755 findHoverPoints();
25756
25757 function selectClosestPoint(pointsData, spikedistance) {
25758 var resultPoint = null;
25759 var minDistance = Infinity;
25760 var thisSpikeDistance;
25761 for(var i = 0; i < pointsData.length; i++) {
25762 thisSpikeDistance = pointsData[i].spikeDistance;
25763 if(thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance) {
25764 resultPoint = pointsData[i];
25765 minDistance = thisSpikeDistance;
25766 }
25767 }
25768 return resultPoint;
25769 }
25770
25771 function fillSpikePoint(point) {
25772 if(!point) return null;
25773 return {
25774 xa: point.xa,
25775 ya: point.ya,
25776 x: point.xSpike !== undefined ? point.xSpike : (point.x0 + point.x1) / 2,
25777 y: point.ySpike !== undefined ? point.ySpike : (point.y0 + point.y1) / 2,
25778 distance: point.distance,
25779 spikeDistance: point.spikeDistance,
25780 curveNumber: point.trace.index,
25781 color: point.color,
25782 pointNumber: point.index
25783 };
25784 }
25785
25786 var spikelineOpts = {
25787 fullLayout: fullLayout,
25788 container: fullLayout._hoverlayer,
25789 outerContainer: fullLayout._paperdiv,
25790 event: evt
25791 };
25792 var oldspikepoints = gd._spikepoints;
25793 var newspikepoints = {
25794 vLinePoint: spikePoints.vLinePoint,
25795 hLinePoint: spikePoints.hLinePoint
25796 };
25797 gd._spikepoints = newspikepoints;
25798
25799 // Now if it is not restricted by spikedistance option, set the points to draw the spikelines
25800 if(hasCartesian && (spikedistance !== 0)) {
25801 if(hoverData.length !== 0) {
25802 var tmpHPointData = hoverData.filter(function(point) {
25803 return point.ya.showspikes;
25804 });
25805 var tmpHPoint = selectClosestPoint(tmpHPointData, spikedistance);
25806 spikePoints.hLinePoint = fillSpikePoint(tmpHPoint);
25807
25808 var tmpVPointData = hoverData.filter(function(point) {
25809 return point.xa.showspikes;
25810 });
25811 var tmpVPoint = selectClosestPoint(tmpVPointData, spikedistance);
25812 spikePoints.vLinePoint = fillSpikePoint(tmpVPoint);
25813 }
25814 }
25815
25816 // if hoverData is empty check for the spikes to draw and quit if there are none
25817 if(hoverData.length === 0) {
25818 var result = dragElement.unhoverRaw(gd, evt);
25819 if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) {
25820 if(spikesChanged(oldspikepoints)) {
25821 createSpikelines(gd, spikePoints, spikelineOpts);
25822 }
25823 }
25824 return result;
25825 }
25826
25827 if(hasCartesian) {
25828 if(spikesChanged(oldspikepoints)) {
25829 createSpikelines(gd, spikePoints, spikelineOpts);
25830 }
25831 }
25832
25833 hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
25834
25835 // If in compare mode, select every point at position
25836 if(
25837 helpers.isXYhover(mode) &&
25838 hoverData[0].length !== 0 &&
25839 hoverData[0].trace.type !== 'splom' // TODO: add support for splom
25840 ) {
25841 var hd = hoverData[0];
25842 var cd0 = hd.cd[hd.index];
25843 var isGrouped = (fullLayout.boxmode === 'group' || fullLayout.violinmode === 'group');
25844
25845 var xVal = hd.xVal;
25846 var ax = hd.xa;
25847 if(ax.type === 'category') xVal = ax._categoriesMap[xVal];
25848 if(ax.type === 'date') xVal = ax.d2c(xVal);
25849 if(cd0 && cd0.t && cd0.t.posLetter === ax._id && isGrouped) {
25850 xVal += cd0.t.dPos;
25851 }
25852
25853 var yVal = hd.yVal;
25854 ax = hd.ya;
25855 if(ax.type === 'category') yVal = ax._categoriesMap[yVal];
25856 if(ax.type === 'date') yVal = ax.d2c(yVal);
25857 if(cd0 && cd0.t && cd0.t.posLetter === ax._id && isGrouped) {
25858 yVal += cd0.t.dPos;
25859 }
25860
25861 findHoverPoints(xVal, yVal);
25862
25863 // Remove duplicated hoverData points
25864 // note that d3 also filters identical points in the rendering steps
25865 var repeated = {};
25866 hoverData = hoverData.filter(function(hd) {
25867 var key = hoverDataKey(hd);
25868 if(!repeated[key]) {
25869 repeated[key] = true;
25870 return repeated[key];
25871 }
25872 });
25873 }
25874
25875 // lastly, emit custom hover/unhover events
25876 var oldhoverdata = gd._hoverdata;
25877 var newhoverdata = [];
25878
25879 // pull out just the data that's useful to
25880 // other people and send it to the event
25881 for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
25882 var pt = hoverData[itemnum];
25883 var eventData = helpers.makeEventData(pt, pt.trace, pt.cd);
25884
25885 if(pt.hovertemplate !== false) {
25886 var ht = false;
25887 if(pt.cd[pt.index] && pt.cd[pt.index].ht) {
25888 ht = pt.cd[pt.index].ht;
25889 }
25890 pt.hovertemplate = ht || pt.trace.hovertemplate || false;
25891 }
25892
25893 pt.eventData = [eventData];
25894 newhoverdata.push(eventData);
25895 }
25896
25897 gd._hoverdata = newhoverdata;
25898
25899 var rotateLabels = (
25900 (hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) ||
25901 (hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1)
25902 );
25903
25904 var bgColor = Color.combine(
25905 fullLayout.plot_bgcolor || Color.background,
25906 fullLayout.paper_bgcolor
25907 );
25908
25909 var labelOpts = {
25910 hovermode: hovermode,
25911 rotateLabels: rotateLabels,
25912 bgColor: bgColor,
25913 container: fullLayout._hoverlayer,
25914 outerContainer: fullLayout._paperdiv,
25915 commonLabelOpts: fullLayout.hoverlabel,
25916 hoverdistance: fullLayout.hoverdistance
25917 };
25918
25919 var hoverLabels = createHoverText(hoverData, labelOpts, gd);
25920
25921 if(!helpers.isUnifiedHover(hovermode)) {
25922 hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout);
25923 alignHoverText(hoverLabels, rotateLabels);
25924 }
25925
25926 // TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true
25927 // we should improve the "fx" API so other plots can use it without these hack.
25928 if(evt.target && evt.target.tagName) {
25929 var hasClickToShow = Registry.getComponentMethod('annotations', 'hasClickToShow')(gd, newhoverdata);
25930 overrideCursor(d3.select(evt.target), hasClickToShow ? 'pointer' : '');
25931 }
25932
25933 // don't emit events if called manually
25934 if(!evt.target || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return;
25935
25936 if(oldhoverdata) {
25937 gd.emit('plotly_unhover', {
25938 event: evt,
25939 points: oldhoverdata
25940 });
25941 }
25942
25943 gd.emit('plotly_hover', {
25944 event: evt,
25945 points: gd._hoverdata,
25946 xaxes: xaArray,
25947 yaxes: yaArray,
25948 xvals: xvalArray,
25949 yvals: yvalArray
25950 });
25951}
25952
25953function hoverDataKey(d) {
25954 return [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa, d.ya || ''].join(',');
25955}
25956
25957var EXTRA_STRING_REGEX = /<extra>([\s\S]*)<\/extra>/;
25958
25959function createHoverText(hoverData, opts, gd) {
25960 var fullLayout = gd._fullLayout;
25961 var hovermode = opts.hovermode;
25962 var rotateLabels = opts.rotateLabels;
25963 var bgColor = opts.bgColor;
25964 var container = opts.container;
25965 var outerContainer = opts.outerContainer;
25966 var commonLabelOpts = opts.commonLabelOpts || {};
25967
25968 // opts.fontFamily/Size are used for the common label
25969 // and as defaults for each hover label, though the individual labels
25970 // can override this.
25971 var fontFamily = opts.fontFamily || constants.HOVERFONT;
25972 var fontSize = opts.fontSize || constants.HOVERFONTSIZE;
25973
25974 var c0 = hoverData[0];
25975 var xa = c0.xa;
25976 var ya = c0.ya;
25977 var commonAttr = hovermode.charAt(0) === 'y' ? 'yLabel' : 'xLabel';
25978 var t0 = c0[commonAttr];
25979 var t00 = (String(t0) || '').split(' ')[0];
25980 var outerContainerBB = outerContainer.node().getBoundingClientRect();
25981 var outerTop = outerContainerBB.top;
25982 var outerWidth = outerContainerBB.width;
25983 var outerHeight = outerContainerBB.height;
25984
25985 // show the common label, if any, on the axis
25986 // never show a common label in array mode,
25987 // even if sometimes there could be one
25988 var showCommonLabel = (
25989 (t0 !== undefined) &&
25990 (c0.distance <= opts.hoverdistance) &&
25991 (hovermode === 'x' || hovermode === 'y')
25992 );
25993
25994 // all hover traces hoverinfo must contain the hovermode
25995 // to have common labels
25996 if(showCommonLabel) {
25997 var allHaveZ = true;
25998 var i, traceHoverinfo;
25999 for(i = 0; i < hoverData.length; i++) {
26000 if(allHaveZ && hoverData[i].zLabel === undefined) allHaveZ = false;
26001
26002 traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
26003 if(traceHoverinfo) {
26004 var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+');
26005 if(parts.indexOf('all') === -1 &&
26006 parts.indexOf(hovermode) === -1) {
26007 showCommonLabel = false;
26008 break;
26009 }
26010 }
26011 }
26012
26013 // xyz labels put all info in their main label, so have no need of a common label
26014 if(allHaveZ) showCommonLabel = false;
26015 }
26016
26017 var commonLabel = container.selectAll('g.axistext')
26018 .data(showCommonLabel ? [0] : []);
26019 commonLabel.enter().append('g')
26020 .classed('axistext', true);
26021 commonLabel.exit().remove();
26022
26023 commonLabel.each(function() {
26024 var label = d3.select(this);
26025 var lpath = Lib.ensureSingle(label, 'path', '', function(s) {
26026 s.style({'stroke-width': '1px'});
26027 });
26028 var ltext = Lib.ensureSingle(label, 'text', '', function(s) {
26029 // prohibit tex interpretation until we can handle
26030 // tex and regular text together
26031 s.attr('data-notex', 1);
26032 });
26033
26034 var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine;
26035 var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor);
26036 var contrastColor = Color.contrast(commonBgColor);
26037 var commonLabelFont = {
26038 family: commonLabelOpts.font.family || fontFamily,
26039 size: commonLabelOpts.font.size || fontSize,
26040 color: commonLabelOpts.font.color || contrastColor
26041 };
26042
26043 lpath.style({
26044 fill: commonBgColor,
26045 stroke: commonStroke
26046 });
26047
26048 ltext.text(t0)
26049 .call(Drawing.font, commonLabelFont)
26050 .call(svgTextUtils.positionText, 0, 0)
26051 .call(svgTextUtils.convertToTspans, gd);
26052
26053 label.attr('transform', '');
26054
26055 var tbb = ltext.node().getBoundingClientRect();
26056 var lx, ly;
26057
26058 if(hovermode === 'x') {
26059 var topsign = xa.side === 'top' ? '-' : '';
26060
26061 ltext.attr('text-anchor', 'middle')
26062 .call(svgTextUtils.positionText, 0, (xa.side === 'top' ?
26063 (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) :
26064 (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD)));
26065
26066 lx = xa._offset + (c0.x0 + c0.x1) / 2;
26067 ly = ya._offset + (xa.side === 'top' ? 0 : ya._length);
26068
26069 var halfWidth = tbb.width / 2 + HOVERTEXTPAD;
26070
26071 if(lx < halfWidth) {
26072 lx = halfWidth;
26073
26074 lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' +
26075 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE +
26076 'H' + (HOVERTEXTPAD + tbb.width / 2) +
26077 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
26078 'H-' + halfWidth +
26079 'V' + topsign + HOVERARROWSIZE +
26080 'Z');
26081 } else if(lx > (fullLayout.width - halfWidth)) {
26082 lx = fullLayout.width - halfWidth;
26083
26084 lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' +
26085 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE +
26086 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
26087 'H-' + halfWidth +
26088 'V' + topsign + HOVERARROWSIZE +
26089 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z');
26090 } else {
26091 lpath.attr('d', 'M0,0' +
26092 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
26093 'H' + (HOVERTEXTPAD + tbb.width / 2) +
26094 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
26095 'H-' + (HOVERTEXTPAD + tbb.width / 2) +
26096 'V' + topsign + HOVERARROWSIZE +
26097 'H-' + HOVERARROWSIZE + 'Z');
26098 }
26099 } else {
26100 var anchor;
26101 var sgn;
26102 var leftsign;
26103 if(ya.side === 'right') {
26104 anchor = 'start';
26105 sgn = 1;
26106 leftsign = '';
26107 lx = xa._offset + xa._length;
26108 } else {
26109 anchor = 'end';
26110 sgn = -1;
26111 leftsign = '-';
26112 lx = xa._offset;
26113 }
26114
26115 ly = ya._offset + (c0.y0 + c0.y1) / 2;
26116
26117 ltext.attr('text-anchor', anchor);
26118
26119 lpath.attr('d', 'M0,0' +
26120 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE +
26121 'V' + (HOVERTEXTPAD + tbb.height / 2) +
26122 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) +
26123 'V-' + (HOVERTEXTPAD + tbb.height / 2) +
26124 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z');
26125
26126 var halfHeight = tbb.height / 2;
26127 var lty = outerTop - tbb.top - halfHeight;
26128 var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id;
26129 var clipPath;
26130
26131 if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) {
26132 clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight +
26133 'h-' + (tbb.width - HOVERTEXTPAD) +
26134 'V' + halfHeight +
26135 'h' + (tbb.width - HOVERTEXTPAD) + 'Z';
26136
26137 var ltx = tbb.width - lx + HOVERTEXTPAD;
26138 svgTextUtils.positionText(ltext, ltx, lty);
26139
26140 // shift each line (except the longest) so that start-of-line
26141 // is always visible
26142 if(anchor === 'end') {
26143 ltext.selectAll('tspan').each(function() {
26144 var s = d3.select(this);
26145 var dummy = Drawing.tester.append('text')
26146 .text(s.text())
26147 .call(Drawing.font, commonLabelFont);
26148 var dummyBB = dummy.node().getBoundingClientRect();
26149 if(Math.round(dummyBB.width) < Math.round(tbb.width)) {
26150 s.attr('x', ltx - dummyBB.width);
26151 }
26152 dummy.remove();
26153 });
26154 }
26155 } else {
26156 svgTextUtils.positionText(ltext, sgn * (HOVERTEXTPAD + HOVERARROWSIZE), lty);
26157 clipPath = null;
26158 }
26159
26160 var textClip = fullLayout._topclips.selectAll('#' + clipId).data(clipPath ? [0] : []);
26161 textClip.enter().append('clipPath').attr('id', clipId).append('path');
26162 textClip.exit().remove();
26163 textClip.select('path').attr('d', clipPath);
26164 Drawing.setClipUrl(ltext, clipPath ? clipId : null, gd);
26165 }
26166
26167 label.attr('transform', 'translate(' + lx + ',' + ly + ')');
26168
26169 // remove the "close but not quite" points
26170 // because of error bars, only take up to a space
26171 hoverData = filterClosePoints(hoverData);
26172 });
26173
26174 function filterClosePoints(hoverData) {
26175 return hoverData.filter(function(d) {
26176 return (d.zLabelVal !== undefined) ||
26177 (d[commonAttr] || '').split(' ')[0] === t00;
26178 });
26179 }
26180
26181 // Show a single hover label
26182 if(helpers.isUnifiedHover(hovermode)) {
26183 // Delete leftover hover labels from other hovermodes
26184 container.selectAll('g.hovertext').remove();
26185
26186 // similarly to compare mode, we remove the "close but not quite together" points
26187 if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData);
26188
26189 // Return early if nothing is hovered on
26190 if(hoverData.length === 0) return;
26191
26192 // mock legend
26193 var mockLayoutIn = {
26194 showlegend: true,
26195 legend: {
26196 title: {text: t0, font: fullLayout.hoverlabel.font},
26197 font: fullLayout.hoverlabel.font,
26198 bgcolor: fullLayout.hoverlabel.bgcolor,
26199 bordercolor: fullLayout.hoverlabel.bordercolor,
26200 borderwidth: 1,
26201 tracegroupgap: 7,
26202 traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined,
26203 orientation: 'v'
26204 }
26205 };
26206 var mockLayoutOut = {};
26207 legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData);
26208 var legendOpts = mockLayoutOut.legend;
26209
26210 // prepare items for the legend
26211 legendOpts.entries = [];
26212 for(var j = 0; j < hoverData.length; j++) {
26213 var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0);
26214 var text = texts[0];
26215 var name = texts[1];
26216 var pt = hoverData[j];
26217 pt.name = name;
26218 if(name !== '') {
26219 pt.text = name + ' : ' + text;
26220 } else {
26221 pt.text = text;
26222 }
26223
26224 // pass through marker's calcdata to style legend items
26225 var cd = pt.cd[pt.index];
26226 if(cd) {
26227 if(cd.mc) pt.mc = cd.mc;
26228 if(cd.mcc) pt.mc = cd.mcc;
26229 if(cd.mlc) pt.mlc = cd.mlc;
26230 if(cd.mlcc) pt.mlc = cd.mlcc;
26231 if(cd.mlw) pt.mlw = cd.mlw;
26232 if(cd.mrc) pt.mrc = cd.mrc;
26233 if(cd.dir) pt.dir = cd.dir;
26234 }
26235 pt._distinct = true;
26236
26237 legendOpts.entries.push([pt]);
26238 }
26239 legendOpts.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;});
26240 legendOpts.layer = container;
26241
26242 // Draw unified hover label
26243 legendDraw(gd, legendOpts);
26244
26245 // Position the hover
26246 var ly = Lib.mean(hoverData.map(function(c) {return (c.y0 + c.y1) / 2;}));
26247 var lx = Lib.mean(hoverData.map(function(c) {return (c.x0 + c.x1) / 2;}));
26248 var legendContainer = container.select('g.legend');
26249 var tbb = legendContainer.node().getBoundingClientRect();
26250 lx += xa._offset;
26251 ly += ya._offset - tbb.height / 2;
26252
26253 // Change horizontal alignment to end up on screen
26254 var txWidth = tbb.width + 2 * HOVERTEXTPAD;
26255 var anchorStartOK = lx + txWidth <= outerWidth;
26256 var anchorEndOK = lx - txWidth >= 0;
26257 if(!anchorStartOK && anchorEndOK) {
26258 lx -= txWidth;
26259 } else {
26260 lx += 2 * HOVERTEXTPAD;
26261 }
26262
26263 // Change vertical alignement to end up on screen
26264 var txHeight = tbb.height + 2 * HOVERTEXTPAD;
26265 var overflowTop = ly <= outerTop;
26266 var overflowBottom = ly + txHeight >= outerHeight;
26267 var canFit = txHeight <= outerHeight;
26268 if(canFit) {
26269 if(overflowTop) {
26270 ly = ya._offset + 2 * HOVERTEXTPAD;
26271 } else if(overflowBottom) {
26272 ly = outerHeight - txHeight;
26273 }
26274 }
26275 legendContainer.attr('transform', 'translate(' + lx + ',' + ly + ')');
26276
26277 return legendContainer;
26278 }
26279
26280 // show all the individual labels
26281
26282 // first create the objects
26283 var hoverLabels = container.selectAll('g.hovertext')
26284 .data(hoverData, function(d) {
26285 // N.B. when multiple items have the same result key-function value,
26286 // only the first of those items in hoverData gets rendered
26287 return hoverDataKey(d);
26288 });
26289 hoverLabels.enter().append('g')
26290 .classed('hovertext', true)
26291 .each(function() {
26292 var g = d3.select(this);
26293 // trace name label (rect and text.name)
26294 g.append('rect')
26295 .call(Color.fill, Color.addOpacity(bgColor, 0.8));
26296 g.append('text').classed('name', true);
26297 // trace data label (path and text.nums)
26298 g.append('path')
26299 .style('stroke-width', '1px');
26300 g.append('text').classed('nums', true)
26301 .call(Drawing.font, fontFamily, fontSize);
26302 });
26303 hoverLabels.exit().remove();
26304
26305 // then put the text in, position the pointer to the data,
26306 // and figure out sizes
26307 hoverLabels.each(function(d) {
26308 var g = d3.select(this).attr('transform', '');
26309
26310 // combine possible non-opaque trace color with bgColor
26311 var color0 = d.bgcolor || d.color;
26312 // color for 'nums' part of the label
26313 var numsColor = Color.combine(
26314 Color.opacity(color0) ? color0 : Color.defaultLine,
26315 bgColor
26316 );
26317 // color for 'name' part of the label
26318 var nameColor = Color.combine(
26319 Color.opacity(d.color) ? d.color : Color.defaultLine,
26320 bgColor
26321 );
26322 // find a contrasting color for border and text
26323 var contrastColor = d.borderColor || Color.contrast(numsColor);
26324
26325 var texts = getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g);
26326 var text = texts[0];
26327 var name = texts[1];
26328
26329 // main label
26330 var tx = g.select('text.nums')
26331 .call(Drawing.font,
26332 d.fontFamily || fontFamily,
26333 d.fontSize || fontSize,
26334 d.fontColor || contrastColor)
26335 .text(text)
26336 .attr('data-notex', 1)
26337 .call(svgTextUtils.positionText, 0, 0)
26338 .call(svgTextUtils.convertToTspans, gd);
26339
26340 var tx2 = g.select('text.name');
26341 var tx2width = 0;
26342 var tx2height = 0;
26343
26344 // secondary label for non-empty 'name'
26345 if(name && name !== text) {
26346 tx2.call(Drawing.font,
26347 d.fontFamily || fontFamily,
26348 d.fontSize || fontSize,
26349 nameColor)
26350 .text(name)
26351 .attr('data-notex', 1)
26352 .call(svgTextUtils.positionText, 0, 0)
26353 .call(svgTextUtils.convertToTspans, gd);
26354
26355 var t2bb = tx2.node().getBoundingClientRect();
26356 tx2width = t2bb.width + 2 * HOVERTEXTPAD;
26357 tx2height = t2bb.height + 2 * HOVERTEXTPAD;
26358 } else {
26359 tx2.remove();
26360 g.select('rect').remove();
26361 }
26362
26363 g.select('path').style({
26364 fill: numsColor,
26365 stroke: contrastColor
26366 });
26367
26368 var tbb = tx.node().getBoundingClientRect();
26369 var htx = d.xa._offset + (d.x0 + d.x1) / 2;
26370 var hty = d.ya._offset + (d.y0 + d.y1) / 2;
26371 var dx = Math.abs(d.x1 - d.x0);
26372 var dy = Math.abs(d.y1 - d.y0);
26373 var txTotalWidth = tbb.width + HOVERARROWSIZE + HOVERTEXTPAD + tx2width;
26374 var anchorStartOK, anchorEndOK;
26375
26376 d.ty0 = outerTop - tbb.top;
26377 d.bx = tbb.width + 2 * HOVERTEXTPAD;
26378 d.by = Math.max(tbb.height + 2 * HOVERTEXTPAD, tx2height);
26379 d.anchor = 'start';
26380 d.txwidth = tbb.width;
26381 d.tx2width = tx2width;
26382 d.offset = 0;
26383
26384 if(rotateLabels) {
26385 d.pos = htx;
26386 anchorStartOK = hty + dy / 2 + txTotalWidth <= outerHeight;
26387 anchorEndOK = hty - dy / 2 - txTotalWidth >= 0;
26388 if((d.idealAlign === 'top' || !anchorStartOK) && anchorEndOK) {
26389 hty -= dy / 2;
26390 d.anchor = 'end';
26391 } else if(anchorStartOK) {
26392 hty += dy / 2;
26393 d.anchor = 'start';
26394 } else d.anchor = 'middle';
26395 } else {
26396 d.pos = hty;
26397 anchorStartOK = htx + dx / 2 + txTotalWidth <= outerWidth;
26398 anchorEndOK = htx - dx / 2 - txTotalWidth >= 0;
26399
26400 if((d.idealAlign === 'left' || !anchorStartOK) && anchorEndOK) {
26401 htx -= dx / 2;
26402 d.anchor = 'end';
26403 } else if(anchorStartOK) {
26404 htx += dx / 2;
26405 d.anchor = 'start';
26406 } else {
26407 d.anchor = 'middle';
26408
26409 var txHalfWidth = txTotalWidth / 2;
26410 var overflowR = htx + txHalfWidth - outerWidth;
26411 var overflowL = htx - txHalfWidth;
26412 if(overflowR > 0) htx -= overflowR;
26413 if(overflowL < 0) htx += -overflowL;
26414 }
26415 }
26416
26417 tx.attr('text-anchor', d.anchor);
26418 if(tx2width) tx2.attr('text-anchor', d.anchor);
26419 g.attr('transform', 'translate(' + htx + ',' + hty + ')' +
26420 (rotateLabels ? 'rotate(' + YANGLE + ')' : ''));
26421 });
26422
26423 return hoverLabels;
26424}
26425
26426function getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g) {
26427 var name = '';
26428 var text = '';
26429 // to get custom 'name' labels pass cleanPoint
26430 if(d.nameOverride !== undefined) d.name = d.nameOverride;
26431
26432 if(d.name) {
26433 if(d.trace._meta) {
26434 d.name = Lib.templateString(d.name, d.trace._meta);
26435 }
26436 name = plainText(d.name, d.nameLength);
26437 }
26438
26439 if(d.zLabel !== undefined) {
26440 if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '<br>';
26441 if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '<br>';
26442 if(d.trace.type !== 'choropleth' && d.trace.type !== 'choroplethmapbox') {
26443 text += (text ? 'z: ' : '') + d.zLabel;
26444 }
26445 } else if(showCommonLabel && d[hovermode.charAt(0) + 'Label'] === t0) {
26446 text = d[(hovermode.charAt(0) === 'x' ? 'y' : 'x') + 'Label'] || '';
26447 } else if(d.xLabel === undefined) {
26448 if(d.yLabel !== undefined && d.trace.type !== 'scattercarpet') {
26449 text = d.yLabel;
26450 }
26451 } else if(d.yLabel === undefined) text = d.xLabel;
26452 else text = '(' + d.xLabel + ', ' + d.yLabel + ')';
26453
26454 if((d.text || d.text === 0) && !Array.isArray(d.text)) {
26455 text += (text ? '<br>' : '') + d.text;
26456 }
26457
26458 // used by other modules (initially just ternary) that
26459 // manage their own hoverinfo independent of cleanPoint
26460 // the rest of this will still apply, so such modules
26461 // can still put things in (x|y|z)Label, text, and name
26462 // and hoverinfo will still determine their visibility
26463 if(d.extraText !== undefined) text += (text ? '<br>' : '') + d.extraText;
26464
26465 // if 'text' is empty at this point,
26466 // and hovertemplate is not defined,
26467 // put 'name' in main label and don't show secondary label
26468 if(g && text === '' && !d.hovertemplate) {
26469 // if 'name' is also empty, remove entire label
26470 if(name === '') g.remove();
26471 text = name;
26472 }
26473
26474 // hovertemplate
26475 var d3locale = fullLayout._d3locale;
26476 var hovertemplate = d.hovertemplate || false;
26477 var hovertemplateLabels = d.hovertemplateLabels || d;
26478 var eventData = d.eventData[0] || {};
26479 if(hovertemplate) {
26480 text = Lib.hovertemplateString(
26481 hovertemplate,
26482 hovertemplateLabels,
26483 d3locale,
26484 eventData,
26485 d.trace._meta
26486 );
26487
26488 text = text.replace(EXTRA_STRING_REGEX, function(match, extra) {
26489 // assign name for secondary text label
26490 name = plainText(extra, d.nameLength);
26491 // remove from main text label
26492 return '';
26493 });
26494 }
26495 return [text, name];
26496}
26497
26498// Make groups of touching points, and within each group
26499// move each point so that no labels overlap, but the average
26500// label position is the same as it was before moving. Indicentally,
26501// this is equivalent to saying all the labels are on equal linear
26502// springs about their initial position. Initially, each point is
26503// its own group, but as we find overlaps we will clump the points.
26504//
26505// Also, there are hard constraints at the edges of the graphs,
26506// that push all groups to the middle so they are visible. I don't
26507// know what happens if the group spans all the way from one edge to
26508// the other, though it hardly matters - there's just too much
26509// information then.
26510function hoverAvoidOverlaps(hoverLabels, axKey, fullLayout) {
26511 var nummoves = 0;
26512 var axSign = 1;
26513 var nLabels = hoverLabels.size();
26514
26515 // make groups of touching points
26516 var pointgroups = new Array(nLabels);
26517 var k = 0;
26518
26519 hoverLabels.each(function(d) {
26520 var ax = d[axKey];
26521 var axIsX = ax._id.charAt(0) === 'x';
26522 var rng = ax.range;
26523
26524 if(k === 0 && rng && ((rng[0] > rng[1]) !== axIsX)) {
26525 axSign = -1;
26526 }
26527 pointgroups[k++] = [{
26528 datum: d,
26529 traceIndex: d.trace.index,
26530 dp: 0,
26531 pos: d.pos,
26532 posref: d.posref,
26533 size: d.by * (axIsX ? YFACTOR : 1) / 2,
26534 pmin: 0,
26535 pmax: (axIsX ? fullLayout.width : fullLayout.height)
26536 }];
26537 });
26538
26539 pointgroups.sort(function(a, b) {
26540 return (a[0].posref - b[0].posref) ||
26541 // for equal positions, sort trace indices increasing or decreasing
26542 // depending on whether the axis is reversed or not... so stacked
26543 // traces will generally keep their order even if one trace adds
26544 // nothing to the stack.
26545 (axSign * (b[0].traceIndex - a[0].traceIndex));
26546 });
26547
26548 var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp;
26549
26550 function constrainGroup(grp) {
26551 var minPt = grp[0];
26552 var maxPt = grp[grp.length - 1];
26553
26554 // overlap with the top - positive vals are overlaps
26555 topOverlap = minPt.pmin - minPt.pos - minPt.dp + minPt.size;
26556
26557 // overlap with the bottom - positive vals are overlaps
26558 bottomOverlap = maxPt.pos + maxPt.dp + maxPt.size - minPt.pmax;
26559
26560 // check for min overlap first, so that we always
26561 // see the largest labels
26562 // allow for .01px overlap, so we don't get an
26563 // infinite loop from rounding errors
26564 if(topOverlap > 0.01) {
26565 for(j = grp.length - 1; j >= 0; j--) grp[j].dp += topOverlap;
26566 donepositioning = false;
26567 }
26568 if(bottomOverlap < 0.01) return;
26569 if(topOverlap < -0.01) {
26570 // make sure we're not pushing back and forth
26571 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
26572 donepositioning = false;
26573 }
26574 if(!donepositioning) return;
26575
26576 // no room to fix positioning, delete off-screen points
26577
26578 // first see how many points we need to delete
26579 var deleteCount = 0;
26580 for(i = 0; i < grp.length; i++) {
26581 pti = grp[i];
26582 if(pti.pos + pti.dp + pti.size > minPt.pmax) deleteCount++;
26583 }
26584
26585 // start by deleting points whose data is off screen
26586 for(i = grp.length - 1; i >= 0; i--) {
26587 if(deleteCount <= 0) break;
26588 pti = grp[i];
26589
26590 // pos has already been constrained to [pmin,pmax]
26591 // so look for points close to that to delete
26592 if(pti.pos > minPt.pmax - 1) {
26593 pti.del = true;
26594 deleteCount--;
26595 }
26596 }
26597 for(i = 0; i < grp.length; i++) {
26598 if(deleteCount <= 0) break;
26599 pti = grp[i];
26600
26601 // pos has already been constrained to [pmin,pmax]
26602 // so look for points close to that to delete
26603 if(pti.pos < minPt.pmin + 1) {
26604 pti.del = true;
26605 deleteCount--;
26606
26607 // shift the whole group minus into this new space
26608 bottomOverlap = pti.size * 2;
26609 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
26610 }
26611 }
26612 // then delete points that go off the bottom
26613 for(i = grp.length - 1; i >= 0; i--) {
26614 if(deleteCount <= 0) break;
26615 pti = grp[i];
26616 if(pti.pos + pti.dp + pti.size > minPt.pmax) {
26617 pti.del = true;
26618 deleteCount--;
26619 }
26620 }
26621 }
26622
26623 // loop through groups, combining them if they overlap,
26624 // until nothing moves
26625 while(!donepositioning && nummoves <= nLabels) {
26626 // to avoid infinite loops, don't move more times
26627 // than there are traces
26628 nummoves++;
26629
26630 // assume nothing will move in this iteration,
26631 // reverse this if it does
26632 donepositioning = true;
26633 i = 0;
26634 while(i < pointgroups.length - 1) {
26635 // the higher (g0) and lower (g1) point group
26636 var g0 = pointgroups[i];
26637 var g1 = pointgroups[i + 1];
26638
26639 // the lowest point in the higher group (p0)
26640 // the highest point in the lower group (p1)
26641 var p0 = g0[g0.length - 1];
26642 var p1 = g1[0];
26643 topOverlap = p0.pos + p0.dp + p0.size - p1.pos - p1.dp + p1.size;
26644
26645 // Only group points that lie on the same axes
26646 if(topOverlap > 0.01 && (p0.pmin === p1.pmin) && (p0.pmax === p1.pmax)) {
26647 // push the new point(s) added to this group out of the way
26648 for(j = g1.length - 1; j >= 0; j--) g1[j].dp += topOverlap;
26649
26650 // add them to the group
26651 g0.push.apply(g0, g1);
26652 pointgroups.splice(i + 1, 1);
26653
26654 // adjust for minimum average movement
26655 sumdp = 0;
26656 for(j = g0.length - 1; j >= 0; j--) sumdp += g0[j].dp;
26657 bottomOverlap = sumdp / g0.length;
26658 for(j = g0.length - 1; j >= 0; j--) g0[j].dp -= bottomOverlap;
26659 donepositioning = false;
26660 } else i++;
26661 }
26662
26663 // check if we're going off the plot on either side and fix
26664 pointgroups.forEach(constrainGroup);
26665 }
26666
26667 // now put these offsets into hoverData
26668 for(i = pointgroups.length - 1; i >= 0; i--) {
26669 var grp = pointgroups[i];
26670 for(j = grp.length - 1; j >= 0; j--) {
26671 var pt = grp[j];
26672 var hoverPt = pt.datum;
26673 hoverPt.offset = pt.dp;
26674 hoverPt.del = pt.del;
26675 }
26676 }
26677}
26678
26679function alignHoverText(hoverLabels, rotateLabels) {
26680 // finally set the text positioning relative to the data and draw the
26681 // box around it
26682 hoverLabels.each(function(d) {
26683 var g = d3.select(this);
26684 if(d.del) return g.remove();
26685
26686 var tx = g.select('text.nums');
26687 var anchor = d.anchor;
26688 var horzSign = anchor === 'end' ? -1 : 1;
26689 var alignShift = {start: 1, end: -1, middle: 0}[anchor];
26690 var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD);
26691 var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD);
26692 var offsetX = 0;
26693 var offsetY = d.offset;
26694
26695 if(anchor === 'middle') {
26696 txx -= d.tx2width / 2;
26697 tx2x += d.txwidth / 2 + HOVERTEXTPAD;
26698 }
26699 if(rotateLabels) {
26700 offsetY *= -YSHIFTY;
26701 offsetX = d.offset * YSHIFTX;
26702 }
26703
26704 g.select('path').attr('d', anchor === 'middle' ?
26705 // middle aligned: rect centered on data
26706 ('M-' + (d.bx / 2 + d.tx2width / 2) + ',' + (offsetY - d.by / 2) +
26707 'h' + d.bx + 'v' + d.by + 'h-' + d.bx + 'Z') :
26708 // left or right aligned: side rect with arrow to data
26709 ('M0,0L' + (horzSign * HOVERARROWSIZE + offsetX) + ',' + (HOVERARROWSIZE + offsetY) +
26710 'v' + (d.by / 2 - HOVERARROWSIZE) +
26711 'h' + (horzSign * d.bx) +
26712 'v-' + d.by +
26713 'H' + (horzSign * HOVERARROWSIZE + offsetX) +
26714 'V' + (offsetY - HOVERARROWSIZE) +
26715 'Z'));
26716
26717 var posX = txx + offsetX;
26718 var posY = offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD;
26719 var textAlign = d.textAlign || 'auto';
26720
26721 if(textAlign !== 'auto') {
26722 if(textAlign === 'left' && anchor !== 'start') {
26723 tx.attr('text-anchor', 'start');
26724 posX = anchor === 'middle' ?
26725 -d.bx / 2 - d.tx2width / 2 + HOVERTEXTPAD :
26726 -d.bx - HOVERTEXTPAD;
26727 } else if(textAlign === 'right' && anchor !== 'end') {
26728 tx.attr('text-anchor', 'end');
26729 posX = anchor === 'middle' ?
26730 d.bx / 2 - d.tx2width / 2 - HOVERTEXTPAD :
26731 d.bx + HOVERTEXTPAD;
26732 }
26733 }
26734
26735 tx.call(svgTextUtils.positionText, posX, posY);
26736
26737 if(d.tx2width) {
26738 g.select('text.name')
26739 .call(svgTextUtils.positionText,
26740 tx2x + alignShift * HOVERTEXTPAD + offsetX,
26741 offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD);
26742 g.select('rect')
26743 .call(Drawing.setRect,
26744 tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX,
26745 offsetY - d.by / 2 - 1,
26746 d.tx2width, d.by + 2);
26747 }
26748 });
26749}
26750
26751function cleanPoint(d, hovermode) {
26752 var index = d.index;
26753 var trace = d.trace || {};
26754 var cd0 = d.cd[0];
26755 var cd = d.cd[index] || {};
26756
26757 function pass(v) {
26758 return v || (isNumeric(v) && v === 0);
26759 }
26760
26761 var getVal = Array.isArray(index) ?
26762 function(calcKey, traceKey) {
26763 var v = Lib.castOption(cd0, index, calcKey);
26764 return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey);
26765 } :
26766 function(calcKey, traceKey) {
26767 return Lib.extractOption(cd, trace, calcKey, traceKey);
26768 };
26769
26770 function fill(key, calcKey, traceKey) {
26771 var val = getVal(calcKey, traceKey);
26772 if(pass(val)) d[key] = val;
26773 }
26774
26775 fill('hoverinfo', 'hi', 'hoverinfo');
26776 fill('bgcolor', 'hbg', 'hoverlabel.bgcolor');
26777 fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
26778 fill('fontFamily', 'htf', 'hoverlabel.font.family');
26779 fill('fontSize', 'hts', 'hoverlabel.font.size');
26780 fill('fontColor', 'htc', 'hoverlabel.font.color');
26781 fill('nameLength', 'hnl', 'hoverlabel.namelength');
26782 fill('textAlign', 'hta', 'hoverlabel.align');
26783
26784 d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ?
26785 (d.xa._offset + (d.x0 + d.x1) / 2) :
26786 (d.ya._offset + (d.y0 + d.y1) / 2);
26787
26788 // then constrain all the positions to be on the plot
26789 d.x0 = Lib.constrain(d.x0, 0, d.xa._length);
26790 d.x1 = Lib.constrain(d.x1, 0, d.xa._length);
26791 d.y0 = Lib.constrain(d.y0, 0, d.ya._length);
26792 d.y1 = Lib.constrain(d.y1, 0, d.ya._length);
26793
26794 // and convert the x and y label values into formatted text
26795 if(d.xLabelVal !== undefined) {
26796 d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
26797 d.xVal = d.xa.c2d(d.xLabelVal);
26798 }
26799 if(d.yLabelVal !== undefined) {
26800 d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
26801 d.yVal = d.ya.c2d(d.yLabelVal);
26802 }
26803
26804 // Traces like heatmaps generate the zLabel in their hoverPoints function
26805 if(d.zLabelVal !== undefined && d.zLabel === undefined) {
26806 d.zLabel = String(d.zLabelVal);
26807 }
26808
26809 // for box means and error bars, add the range to the label
26810 if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) {
26811 var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text;
26812 if(d.xerrneg !== undefined) {
26813 d.xLabel += ' +' + xeText + ' / -' +
26814 Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text;
26815 } else d.xLabel += ' ± ' + xeText;
26816
26817 // small distance penalty for error bars, so that if there are
26818 // traces with errors and some without, the error bar label will
26819 // hoist up to the point
26820 if(hovermode === 'x') d.distance += 1;
26821 }
26822 if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) {
26823 var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text;
26824 if(d.yerrneg !== undefined) {
26825 d.yLabel += ' +' + yeText + ' / -' +
26826 Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text;
26827 } else d.yLabel += ' ± ' + yeText;
26828
26829 if(hovermode === 'y') d.distance += 1;
26830 }
26831
26832 var infomode = d.hoverinfo || d.trace.hoverinfo;
26833
26834 if(infomode && infomode !== 'all') {
26835 infomode = Array.isArray(infomode) ? infomode : infomode.split('+');
26836 if(infomode.indexOf('x') === -1) d.xLabel = undefined;
26837 if(infomode.indexOf('y') === -1) d.yLabel = undefined;
26838 if(infomode.indexOf('z') === -1) d.zLabel = undefined;
26839 if(infomode.indexOf('text') === -1) d.text = undefined;
26840 if(infomode.indexOf('name') === -1) d.name = undefined;
26841 }
26842
26843 return d;
26844}
26845
26846function createSpikelines(gd, closestPoints, opts) {
26847 var container = opts.container;
26848 var fullLayout = opts.fullLayout;
26849 var gs = fullLayout._size;
26850 var evt = opts.event;
26851 var showY = !!closestPoints.hLinePoint;
26852 var showX = !!closestPoints.vLinePoint;
26853
26854 var xa, ya;
26855
26856 // Remove old spikeline items
26857 container.selectAll('.spikeline').remove();
26858
26859 if(!(showX || showY)) return;
26860
26861 var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor);
26862
26863 // Horizontal line (to y-axis)
26864 if(showY) {
26865 var hLinePoint = closestPoints.hLinePoint;
26866 var hLinePointX, hLinePointY;
26867
26868 xa = hLinePoint && hLinePoint.xa;
26869 ya = hLinePoint && hLinePoint.ya;
26870 var ySnap = ya.spikesnap;
26871
26872 if(ySnap === 'cursor') {
26873 hLinePointX = evt.pointerX;
26874 hLinePointY = evt.pointerY;
26875 } else {
26876 hLinePointX = xa._offset + hLinePoint.x;
26877 hLinePointY = ya._offset + hLinePoint.y;
26878 }
26879 var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ?
26880 Color.contrast(contrastColor) : hLinePoint.color;
26881 var yMode = ya.spikemode;
26882 var yThickness = ya.spikethickness;
26883 var yColor = ya.spikecolor || dfltHLineColor;
26884 var xEdge = Axes.getPxPosition(gd, ya);
26885 var xBase, xEndSpike;
26886
26887 if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) {
26888 if(yMode.indexOf('toaxis') !== -1) {
26889 xBase = xEdge;
26890 xEndSpike = hLinePointX;
26891 }
26892 if(yMode.indexOf('across') !== -1) {
26893 var xAcross0 = ya._counterDomainMin;
26894 var xAcross1 = ya._counterDomainMax;
26895 if(ya.anchor === 'free') {
26896 xAcross0 = Math.min(xAcross0, ya.position);
26897 xAcross1 = Math.max(xAcross1, ya.position);
26898 }
26899 xBase = gs.l + xAcross0 * gs.w;
26900 xEndSpike = gs.l + xAcross1 * gs.w;
26901 }
26902
26903 // Foreground horizontal line (to y-axis)
26904 container.insert('line', ':first-child')
26905 .attr({
26906 x1: xBase,
26907 x2: xEndSpike,
26908 y1: hLinePointY,
26909 y2: hLinePointY,
26910 'stroke-width': yThickness,
26911 stroke: yColor,
26912 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness)
26913 })
26914 .classed('spikeline', true)
26915 .classed('crisp', true);
26916
26917 // Background horizontal Line (to y-axis)
26918 container.insert('line', ':first-child')
26919 .attr({
26920 x1: xBase,
26921 x2: xEndSpike,
26922 y1: hLinePointY,
26923 y2: hLinePointY,
26924 'stroke-width': yThickness + 2,
26925 stroke: contrastColor
26926 })
26927 .classed('spikeline', true)
26928 .classed('crisp', true);
26929 }
26930 // Y axis marker
26931 if(yMode.indexOf('marker') !== -1) {
26932 container.insert('circle', ':first-child')
26933 .attr({
26934 cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness),
26935 cy: hLinePointY,
26936 r: yThickness,
26937 fill: yColor
26938 })
26939 .classed('spikeline', true);
26940 }
26941 }
26942
26943 if(showX) {
26944 var vLinePoint = closestPoints.vLinePoint;
26945 var vLinePointX, vLinePointY;
26946
26947 xa = vLinePoint && vLinePoint.xa;
26948 ya = vLinePoint && vLinePoint.ya;
26949 var xSnap = xa.spikesnap;
26950
26951 if(xSnap === 'cursor') {
26952 vLinePointX = evt.pointerX;
26953 vLinePointY = evt.pointerY;
26954 } else {
26955 vLinePointX = xa._offset + vLinePoint.x;
26956 vLinePointY = ya._offset + vLinePoint.y;
26957 }
26958 var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ?
26959 Color.contrast(contrastColor) : vLinePoint.color;
26960 var xMode = xa.spikemode;
26961 var xThickness = xa.spikethickness;
26962 var xColor = xa.spikecolor || dfltVLineColor;
26963 var yEdge = Axes.getPxPosition(gd, xa);
26964 var yBase, yEndSpike;
26965
26966 if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) {
26967 if(xMode.indexOf('toaxis') !== -1) {
26968 yBase = yEdge;
26969 yEndSpike = vLinePointY;
26970 }
26971 if(xMode.indexOf('across') !== -1) {
26972 var yAcross0 = xa._counterDomainMin;
26973 var yAcross1 = xa._counterDomainMax;
26974 if(xa.anchor === 'free') {
26975 yAcross0 = Math.min(yAcross0, xa.position);
26976 yAcross1 = Math.max(yAcross1, xa.position);
26977 }
26978 yBase = gs.t + (1 - yAcross1) * gs.h;
26979 yEndSpike = gs.t + (1 - yAcross0) * gs.h;
26980 }
26981
26982 // Foreground vertical line (to x-axis)
26983 container.insert('line', ':first-child')
26984 .attr({
26985 x1: vLinePointX,
26986 x2: vLinePointX,
26987 y1: yBase,
26988 y2: yEndSpike,
26989 'stroke-width': xThickness,
26990 stroke: xColor,
26991 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness)
26992 })
26993 .classed('spikeline', true)
26994 .classed('crisp', true);
26995
26996 // Background vertical line (to x-axis)
26997 container.insert('line', ':first-child')
26998 .attr({
26999 x1: vLinePointX,
27000 x2: vLinePointX,
27001 y1: yBase,
27002 y2: yEndSpike,
27003 'stroke-width': xThickness + 2,
27004 stroke: contrastColor
27005 })
27006 .classed('spikeline', true)
27007 .classed('crisp', true);
27008 }
27009
27010 // X axis marker
27011 if(xMode.indexOf('marker') !== -1) {
27012 container.insert('circle', ':first-child')
27013 .attr({
27014 cx: vLinePointX,
27015 cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness),
27016 r: xThickness,
27017 fill: xColor
27018 })
27019 .classed('spikeline', true);
27020 }
27021 }
27022}
27023
27024function hoverChanged(gd, evt, oldhoverdata) {
27025 // don't emit any events if nothing changed
27026 if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true;
27027
27028 for(var i = oldhoverdata.length - 1; i >= 0; i--) {
27029 var oldPt = oldhoverdata[i];
27030 var newPt = gd._hoverdata[i];
27031
27032 if(oldPt.curveNumber !== newPt.curveNumber ||
27033 String(oldPt.pointNumber) !== String(newPt.pointNumber) ||
27034 String(oldPt.pointNumbers) !== String(newPt.pointNumbers)
27035 ) {
27036 return true;
27037 }
27038 }
27039 return false;
27040}
27041
27042function spikesChanged(gd, oldspikepoints) {
27043 // don't relayout the plot because of new spikelines if spikelines points didn't change
27044 if(!oldspikepoints) return true;
27045 if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint ||
27046 oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint
27047 ) return true;
27048 return false;
27049}
27050
27051function plainText(s, len) {
27052 return svgTextUtils.plainText(s || '', {
27053 len: len,
27054 allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em']
27055 });
27056}
27057
27058},{"../../lib":177,"../../lib/events":169,"../../lib/override_cursor":188,"../../lib/svg_text_utils":198,"../../plots/cartesian/axes":222,"../../registry":272,"../color":50,"../dragelement":69,"../drawing":72,"../legend/defaults":102,"../legend/draw":103,"./constants":84,"./helpers":86,"d3":13,"fast-isnumeric":15,"tinycolor2":32}],88:[function(_dereq_,module,exports){
27059/**
27060* Copyright 2012-2020, Plotly, Inc.
27061* All rights reserved.
27062*
27063* This source code is licensed under the MIT license found in the
27064* LICENSE file in the root directory of this source tree.
27065*/
27066
27067'use strict';
27068
27069var Lib = _dereq_('../../lib');
27070var Color = _dereq_('../color');
27071var isUnifiedHover = _dereq_('./helpers').isUnifiedHover;
27072
27073module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) {
27074 opts = opts || {};
27075
27076 function inheritFontAttr(attr) {
27077 if(!opts.font[attr]) {
27078 opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr];
27079 }
27080 }
27081
27082 // In unified hover, inherit from layout.legend if available or layout
27083 if(contOut && isUnifiedHover(contOut.hovermode)) {
27084 if(!opts.font) opts.font = {};
27085 inheritFontAttr('size');
27086 inheritFontAttr('family');
27087 inheritFontAttr('color');
27088
27089 if(contOut.legend) {
27090 if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor);
27091 if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor;
27092 } else {
27093 if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor;
27094 }
27095 }
27096
27097 coerce('hoverlabel.bgcolor', opts.bgcolor);
27098 coerce('hoverlabel.bordercolor', opts.bordercolor);
27099 coerce('hoverlabel.namelength', opts.namelength);
27100 Lib.coerceFont(coerce, 'hoverlabel.font', opts.font);
27101 coerce('hoverlabel.align', opts.align);
27102};
27103
27104},{"../../lib":177,"../color":50,"./helpers":86}],89:[function(_dereq_,module,exports){
27105/**
27106* Copyright 2012-2020, Plotly, Inc.
27107* All rights reserved.
27108*
27109* This source code is licensed under the MIT license found in the
27110* LICENSE file in the root directory of this source tree.
27111*/
27112
27113'use strict';
27114
27115var Lib = _dereq_('../../lib');
27116var layoutAttributes = _dereq_('./layout_attributes');
27117
27118module.exports = function handleHoverModeDefaults(layoutIn, layoutOut, fullData) {
27119 function coerce(attr, dflt) {
27120 // don't coerce if it is already coerced in other place e.g. in cartesian defaults
27121 if(layoutOut[attr] !== undefined) return layoutOut[attr];
27122
27123 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
27124 }
27125
27126 var clickmode = coerce('clickmode');
27127
27128 var hovermodeDflt;
27129 if(layoutOut._has('cartesian')) {
27130 if(clickmode.indexOf('select') > -1) {
27131 hovermodeDflt = 'closest';
27132 } else {
27133 // flag for 'horizontal' plots:
27134 // determines the state of the mode bar 'compare' hovermode button
27135 layoutOut._isHoriz = isHoriz(fullData, layoutOut);
27136 hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x';
27137 }
27138 } else hovermodeDflt = 'closest';
27139
27140 return coerce('hovermode', hovermodeDflt);
27141};
27142
27143function isHoriz(fullData, fullLayout) {
27144 var stackOpts = fullLayout._scatterStackOpts || {};
27145
27146 for(var i = 0; i < fullData.length; i++) {
27147 var trace = fullData[i];
27148 var subplot = trace.xaxis + trace.yaxis;
27149 var subplotStackOpts = stackOpts[subplot] || {};
27150 var groupOpts = subplotStackOpts[trace.stackgroup] || {};
27151
27152 if(trace.orientation !== 'h' && groupOpts.orientation !== 'h') {
27153 return false;
27154 }
27155 }
27156
27157 return true;
27158}
27159
27160},{"../../lib":177,"./layout_attributes":91}],90:[function(_dereq_,module,exports){
27161/**
27162* Copyright 2012-2020, Plotly, Inc.
27163* All rights reserved.
27164*
27165* This source code is licensed under the MIT license found in the
27166* LICENSE file in the root directory of this source tree.
27167*/
27168
27169'use strict';
27170
27171var d3 = _dereq_('d3');
27172var Lib = _dereq_('../../lib');
27173var dragElement = _dereq_('../dragelement');
27174var helpers = _dereq_('./helpers');
27175var layoutAttributes = _dereq_('./layout_attributes');
27176var hoverModule = _dereq_('./hover');
27177
27178module.exports = {
27179 moduleType: 'component',
27180 name: 'fx',
27181
27182 constants: _dereq_('./constants'),
27183 schema: {
27184 layout: layoutAttributes
27185 },
27186
27187 attributes: _dereq_('./attributes'),
27188 layoutAttributes: layoutAttributes,
27189
27190 supplyLayoutGlobalDefaults: _dereq_('./layout_global_defaults'),
27191 supplyDefaults: _dereq_('./defaults'),
27192 supplyLayoutDefaults: _dereq_('./layout_defaults'),
27193
27194 calc: _dereq_('./calc'),
27195
27196 getDistanceFunction: helpers.getDistanceFunction,
27197 getClosest: helpers.getClosest,
27198 inbox: helpers.inbox,
27199 quadrature: helpers.quadrature,
27200 appendArrayPointValue: helpers.appendArrayPointValue,
27201
27202 castHoverOption: castHoverOption,
27203 castHoverinfo: castHoverinfo,
27204
27205 hover: hoverModule.hover,
27206 unhover: dragElement.unhover,
27207
27208 loneHover: hoverModule.loneHover,
27209 loneUnhover: loneUnhover,
27210
27211 click: _dereq_('./click')
27212};
27213
27214function loneUnhover(containerOrSelection) {
27215 // duck type whether the arg is a d3 selection because ie9 doesn't
27216 // handle instanceof like modern browsers do.
27217 var selection = Lib.isD3Selection(containerOrSelection) ?
27218 containerOrSelection :
27219 d3.select(containerOrSelection);
27220
27221 selection.selectAll('g.hovertext').remove();
27222 selection.selectAll('.spikeline').remove();
27223}
27224
27225// helpers for traces that use Fx.loneHover
27226
27227function castHoverOption(trace, ptNumber, attr) {
27228 return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr);
27229}
27230
27231function castHoverinfo(trace, fullLayout, ptNumber) {
27232 function _coerce(val) {
27233 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
27234 }
27235
27236 return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce);
27237}
27238
27239},{"../../lib":177,"../dragelement":69,"./attributes":81,"./calc":82,"./click":83,"./constants":84,"./defaults":85,"./helpers":86,"./hover":87,"./layout_attributes":91,"./layout_defaults":92,"./layout_global_defaults":93,"d3":13}],91:[function(_dereq_,module,exports){
27240/**
27241* Copyright 2012-2020, Plotly, Inc.
27242* All rights reserved.
27243*
27244* This source code is licensed under the MIT license found in the
27245* LICENSE file in the root directory of this source tree.
27246*/
27247
27248'use strict';
27249
27250var constants = _dereq_('./constants');
27251
27252var fontAttrs = _dereq_('../../plots/font_attributes')({
27253 editType: 'none',
27254
27255});
27256fontAttrs.family.dflt = constants.HOVERFONT;
27257fontAttrs.size.dflt = constants.HOVERFONTSIZE;
27258
27259module.exports = {
27260 clickmode: {
27261 valType: 'flaglist',
27262
27263 flags: ['event', 'select'],
27264 dflt: 'event',
27265 editType: 'plot',
27266 extras: ['none'],
27267
27268 },
27269 dragmode: {
27270 valType: 'enumerated',
27271
27272 values: [
27273 'zoom',
27274 'pan',
27275 'select',
27276 'lasso',
27277 'drawclosedpath',
27278 'drawopenpath',
27279 'drawline',
27280 'drawrect',
27281 'drawcircle',
27282 'orbit',
27283 'turntable',
27284 false
27285 ],
27286 dflt: 'zoom',
27287 editType: 'modebar',
27288
27289 },
27290 hovermode: {
27291 valType: 'enumerated',
27292
27293 values: ['x', 'y', 'closest', false, 'x unified', 'y unified'],
27294 editType: 'modebar',
27295
27296 },
27297 hoverdistance: {
27298 valType: 'integer',
27299 min: -1,
27300 dflt: 20,
27301
27302 editType: 'none',
27303
27304 },
27305 spikedistance: {
27306 valType: 'integer',
27307 min: -1,
27308 dflt: 20,
27309
27310 editType: 'none',
27311
27312 },
27313 hoverlabel: {
27314 bgcolor: {
27315 valType: 'color',
27316
27317 editType: 'none',
27318
27319 },
27320 bordercolor: {
27321 valType: 'color',
27322
27323 editType: 'none',
27324
27325 },
27326 font: fontAttrs,
27327 align: {
27328 valType: 'enumerated',
27329 values: ['left', 'right', 'auto'],
27330 dflt: 'auto',
27331
27332 editType: 'none',
27333
27334 },
27335 namelength: {
27336 valType: 'integer',
27337 min: -1,
27338 dflt: 15,
27339
27340 editType: 'none',
27341
27342 },
27343 editType: 'none'
27344 },
27345 selectdirection: {
27346 valType: 'enumerated',
27347
27348 values: ['h', 'v', 'd', 'any'],
27349 dflt: 'any',
27350
27351 editType: 'none'
27352 }
27353};
27354
27355},{"../../plots/font_attributes":250,"./constants":84}],92:[function(_dereq_,module,exports){
27356/**
27357* Copyright 2012-2020, Plotly, Inc.
27358* All rights reserved.
27359*
27360* This source code is licensed under the MIT license found in the
27361* LICENSE file in the root directory of this source tree.
27362*/
27363
27364'use strict';
27365
27366var Lib = _dereq_('../../lib');
27367var isUnifiedHover = _dereq_('./helpers').isUnifiedHover;
27368var layoutAttributes = _dereq_('./layout_attributes');
27369var handleHoverModeDefaults = _dereq_('./hovermode_defaults');
27370var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
27371
27372module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
27373 function coerce(attr, dflt) {
27374 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
27375 }
27376
27377 var hoverMode = handleHoverModeDefaults(layoutIn, layoutOut, fullData);
27378 if(hoverMode) {
27379 coerce('hoverdistance');
27380 coerce('spikedistance', isUnifiedHover(hoverMode) ? -1 : undefined);
27381 }
27382
27383 var dragMode = coerce('dragmode');
27384 if(dragMode === 'select') coerce('selectdirection');
27385
27386 // if only mapbox or geo subplots is present on graph,
27387 // reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,
27388 // so that the correct modebar button is active
27389 var hasMapbox = layoutOut._has('mapbox');
27390 var hasGeo = layoutOut._has('geo');
27391 var len = layoutOut._basePlotModules.length;
27392
27393 if(layoutOut.dragmode === 'zoom' && (
27394 ((hasMapbox || hasGeo) && len === 1) ||
27395 (hasMapbox && hasGeo && len === 2)
27396 )) {
27397 layoutOut.dragmode = 'pan';
27398 }
27399
27400 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
27401};
27402
27403},{"../../lib":177,"./helpers":86,"./hoverlabel_defaults":88,"./hovermode_defaults":89,"./layout_attributes":91}],93:[function(_dereq_,module,exports){
27404/**
27405* Copyright 2012-2020, Plotly, Inc.
27406* All rights reserved.
27407*
27408* This source code is licensed under the MIT license found in the
27409* LICENSE file in the root directory of this source tree.
27410*/
27411
27412'use strict';
27413
27414var Lib = _dereq_('../../lib');
27415var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
27416var layoutAttributes = _dereq_('./layout_attributes');
27417
27418module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) {
27419 function coerce(attr, dflt) {
27420 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
27421 }
27422
27423 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
27424};
27425
27426},{"../../lib":177,"./hoverlabel_defaults":88,"./layout_attributes":91}],94:[function(_dereq_,module,exports){
27427/**
27428* Copyright 2012-2020, Plotly, Inc.
27429* All rights reserved.
27430*
27431* This source code is licensed under the MIT license found in the
27432* LICENSE file in the root directory of this source tree.
27433*/
27434
27435'use strict';
27436
27437var Lib = _dereq_('../../lib');
27438var counterRegex = _dereq_('../../lib/regex').counter;
27439var domainAttrs = _dereq_('../../plots/domain').attributes;
27440var cartesianIdRegex = _dereq_('../../plots/cartesian/constants').idRegex;
27441var Template = _dereq_('../../plot_api/plot_template');
27442
27443var gridAttrs = {
27444 rows: {
27445 valType: 'integer',
27446 min: 1,
27447
27448 editType: 'plot',
27449
27450 },
27451 roworder: {
27452 valType: 'enumerated',
27453 values: ['top to bottom', 'bottom to top'],
27454 dflt: 'top to bottom',
27455
27456 editType: 'plot',
27457
27458 },
27459 columns: {
27460 valType: 'integer',
27461 min: 1,
27462
27463 editType: 'plot',
27464
27465 },
27466 subplots: {
27467 valType: 'info_array',
27468 freeLength: true,
27469 dimensions: 2,
27470 items: {valType: 'enumerated', values: [counterRegex('xy').toString(), ''], editType: 'plot'},
27471
27472 editType: 'plot',
27473
27474 },
27475 xaxes: {
27476 valType: 'info_array',
27477 freeLength: true,
27478 items: {valType: 'enumerated', values: [cartesianIdRegex.x.toString(), ''], editType: 'plot'},
27479
27480 editType: 'plot',
27481
27482 },
27483 yaxes: {
27484 valType: 'info_array',
27485 freeLength: true,
27486 items: {valType: 'enumerated', values: [cartesianIdRegex.y.toString(), ''], editType: 'plot'},
27487
27488 editType: 'plot',
27489
27490 },
27491 pattern: {
27492 valType: 'enumerated',
27493 values: ['independent', 'coupled'],
27494 dflt: 'coupled',
27495
27496 editType: 'plot',
27497
27498 },
27499 xgap: {
27500 valType: 'number',
27501 min: 0,
27502 max: 1,
27503
27504 editType: 'plot',
27505
27506 },
27507 ygap: {
27508 valType: 'number',
27509 min: 0,
27510 max: 1,
27511
27512 editType: 'plot',
27513
27514 },
27515 domain: domainAttrs({name: 'grid', editType: 'plot', noGridCell: true}, {
27516
27517 }),
27518 xside: {
27519 valType: 'enumerated',
27520 values: ['bottom', 'bottom plot', 'top plot', 'top'],
27521 dflt: 'bottom plot',
27522
27523 editType: 'plot',
27524
27525 },
27526 yside: {
27527 valType: 'enumerated',
27528 values: ['left', 'left plot', 'right plot', 'right'],
27529 dflt: 'left plot',
27530
27531 editType: 'plot',
27532
27533 },
27534 editType: 'plot'
27535};
27536
27537function getAxes(layout, grid, axLetter) {
27538 var gridVal = grid[axLetter + 'axes'];
27539 var splomVal = Object.keys((layout._splomAxes || {})[axLetter] || {});
27540
27541 if(Array.isArray(gridVal)) return gridVal;
27542 if(splomVal.length) return splomVal;
27543}
27544
27545// the shape of the grid - this needs to be done BEFORE supplyDataDefaults
27546// so that non-subplot traces can place themselves in the grid
27547function sizeDefaults(layoutIn, layoutOut) {
27548 var gridIn = layoutIn.grid || {};
27549 var xAxes = getAxes(layoutOut, gridIn, 'x');
27550 var yAxes = getAxes(layoutOut, gridIn, 'y');
27551
27552 if(!layoutIn.grid && !xAxes && !yAxes) return;
27553
27554 var hasSubplotGrid = Array.isArray(gridIn.subplots) && Array.isArray(gridIn.subplots[0]);
27555 var hasXaxes = Array.isArray(xAxes);
27556 var hasYaxes = Array.isArray(yAxes);
27557 var isSplomGenerated = (
27558 hasXaxes && xAxes !== gridIn.xaxes &&
27559 hasYaxes && yAxes !== gridIn.yaxes
27560 );
27561
27562 var dfltRows, dfltColumns;
27563
27564 if(hasSubplotGrid) {
27565 dfltRows = gridIn.subplots.length;
27566 dfltColumns = gridIn.subplots[0].length;
27567 } else {
27568 if(hasYaxes) dfltRows = yAxes.length;
27569 if(hasXaxes) dfltColumns = xAxes.length;
27570 }
27571
27572 var gridOut = Template.newContainer(layoutOut, 'grid');
27573
27574 function coerce(attr, dflt) {
27575 return Lib.coerce(gridIn, gridOut, gridAttrs, attr, dflt);
27576 }
27577
27578 var rows = coerce('rows', dfltRows);
27579 var columns = coerce('columns', dfltColumns);
27580
27581 if(!(rows * columns > 1)) {
27582 delete layoutOut.grid;
27583 return;
27584 }
27585
27586 if(!hasSubplotGrid && !hasXaxes && !hasYaxes) {
27587 var useDefaultSubplots = coerce('pattern') === 'independent';
27588 if(useDefaultSubplots) hasSubplotGrid = true;
27589 }
27590 gridOut._hasSubplotGrid = hasSubplotGrid;
27591
27592 var rowOrder = coerce('roworder');
27593 var reversed = rowOrder === 'top to bottom';
27594
27595 var dfltGapX = hasSubplotGrid ? 0.2 : 0.1;
27596 var dfltGapY = hasSubplotGrid ? 0.3 : 0.1;
27597
27598 var dfltSideX, dfltSideY;
27599 if(isSplomGenerated && layoutOut._splomGridDflt) {
27600 dfltSideX = layoutOut._splomGridDflt.xside;
27601 dfltSideY = layoutOut._splomGridDflt.yside;
27602 }
27603
27604 gridOut._domains = {
27605 x: fillGridPositions('x', coerce, dfltGapX, dfltSideX, columns),
27606 y: fillGridPositions('y', coerce, dfltGapY, dfltSideY, rows, reversed)
27607 };
27608}
27609
27610// coerce x or y sizing attributes and return an array of domains for this direction
27611function fillGridPositions(axLetter, coerce, dfltGap, dfltSide, len, reversed) {
27612 var dirGap = coerce(axLetter + 'gap', dfltGap);
27613 var domain = coerce('domain.' + axLetter);
27614 coerce(axLetter + 'side', dfltSide);
27615
27616 var out = new Array(len);
27617 var start = domain[0];
27618 var step = (domain[1] - start) / (len - dirGap);
27619 var cellDomain = step * (1 - dirGap);
27620 for(var i = 0; i < len; i++) {
27621 var cellStart = start + step * i;
27622 out[reversed ? (len - 1 - i) : i] = [cellStart, cellStart + cellDomain];
27623 }
27624 return out;
27625}
27626
27627// the (cartesian) contents of the grid - this needs to happen AFTER supplyDataDefaults
27628// so that we know what cartesian subplots are available
27629function contentDefaults(layoutIn, layoutOut) {
27630 var gridOut = layoutOut.grid;
27631 // make sure we got to the end of handleGridSizing
27632 if(!gridOut || !gridOut._domains) return;
27633
27634 var gridIn = layoutIn.grid || {};
27635 var subplots = layoutOut._subplots;
27636 var hasSubplotGrid = gridOut._hasSubplotGrid;
27637 var rows = gridOut.rows;
27638 var columns = gridOut.columns;
27639 var useDefaultSubplots = gridOut.pattern === 'independent';
27640
27641 var i, j, xId, yId, subplotId, subplotsOut, yPos;
27642
27643 var axisMap = gridOut._axisMap = {};
27644
27645 if(hasSubplotGrid) {
27646 var subplotsIn = gridIn.subplots || [];
27647 subplotsOut = gridOut.subplots = new Array(rows);
27648 var index = 1;
27649
27650 for(i = 0; i < rows; i++) {
27651 var rowOut = subplotsOut[i] = new Array(columns);
27652 var rowIn = subplotsIn[i] || [];
27653 for(j = 0; j < columns; j++) {
27654 if(useDefaultSubplots) {
27655 subplotId = (index === 1) ? 'xy' : ('x' + index + 'y' + index);
27656 index++;
27657 } else subplotId = rowIn[j];
27658
27659 rowOut[j] = '';
27660
27661 if(subplots.cartesian.indexOf(subplotId) !== -1) {
27662 yPos = subplotId.indexOf('y');
27663 xId = subplotId.slice(0, yPos);
27664 yId = subplotId.slice(yPos);
27665 if((axisMap[xId] !== undefined && axisMap[xId] !== j) ||
27666 (axisMap[yId] !== undefined && axisMap[yId] !== i)
27667 ) {
27668 continue;
27669 }
27670
27671 rowOut[j] = subplotId;
27672 axisMap[xId] = j;
27673 axisMap[yId] = i;
27674 }
27675 }
27676 }
27677 } else {
27678 var xAxes = getAxes(layoutOut, gridIn, 'x');
27679 var yAxes = getAxes(layoutOut, gridIn, 'y');
27680 gridOut.xaxes = fillGridAxes(xAxes, subplots.xaxis, columns, axisMap, 'x');
27681 gridOut.yaxes = fillGridAxes(yAxes, subplots.yaxis, rows, axisMap, 'y');
27682 }
27683
27684 var anchors = gridOut._anchors = {};
27685 var reversed = gridOut.roworder === 'top to bottom';
27686
27687 for(var axisId in axisMap) {
27688 var axLetter = axisId.charAt(0);
27689 var side = gridOut[axLetter + 'side'];
27690
27691 var i0, inc, iFinal;
27692
27693 if(side.length < 8) {
27694 // grid edge - ie not "* plot" - make these as free axes
27695 // since we're not guaranteed to have a subplot there at all
27696 anchors[axisId] = 'free';
27697 } else if(axLetter === 'x') {
27698 if((side.charAt(0) === 't') === reversed) {
27699 i0 = 0;
27700 inc = 1;
27701 iFinal = rows;
27702 } else {
27703 i0 = rows - 1;
27704 inc = -1;
27705 iFinal = -1;
27706 }
27707 if(hasSubplotGrid) {
27708 var column = axisMap[axisId];
27709 for(i = i0; i !== iFinal; i += inc) {
27710 subplotId = subplotsOut[i][column];
27711 if(!subplotId) continue;
27712 yPos = subplotId.indexOf('y');
27713 if(subplotId.slice(0, yPos) === axisId) {
27714 anchors[axisId] = subplotId.slice(yPos);
27715 break;
27716 }
27717 }
27718 } else {
27719 for(i = i0; i !== iFinal; i += inc) {
27720 yId = gridOut.yaxes[i];
27721 if(subplots.cartesian.indexOf(axisId + yId) !== -1) {
27722 anchors[axisId] = yId;
27723 break;
27724 }
27725 }
27726 }
27727 } else {
27728 if((side.charAt(0) === 'l')) {
27729 i0 = 0;
27730 inc = 1;
27731 iFinal = columns;
27732 } else {
27733 i0 = columns - 1;
27734 inc = -1;
27735 iFinal = -1;
27736 }
27737 if(hasSubplotGrid) {
27738 var row = axisMap[axisId];
27739 for(i = i0; i !== iFinal; i += inc) {
27740 subplotId = subplotsOut[row][i];
27741 if(!subplotId) continue;
27742 yPos = subplotId.indexOf('y');
27743 if(subplotId.slice(yPos) === axisId) {
27744 anchors[axisId] = subplotId.slice(0, yPos);
27745 break;
27746 }
27747 }
27748 } else {
27749 for(i = i0; i !== iFinal; i += inc) {
27750 xId = gridOut.xaxes[i];
27751 if(subplots.cartesian.indexOf(xId + axisId) !== -1) {
27752 anchors[axisId] = xId;
27753 break;
27754 }
27755 }
27756 }
27757 }
27758 }
27759}
27760
27761function fillGridAxes(axesIn, axesAllowed, len, axisMap, axLetter) {
27762 var out = new Array(len);
27763 var i;
27764
27765 function fillOneAxis(i, axisId) {
27766 if(axesAllowed.indexOf(axisId) !== -1 && axisMap[axisId] === undefined) {
27767 out[i] = axisId;
27768 axisMap[axisId] = i;
27769 } else out[i] = '';
27770 }
27771
27772 if(Array.isArray(axesIn)) {
27773 for(i = 0; i < len; i++) {
27774 fillOneAxis(i, axesIn[i]);
27775 }
27776 } else {
27777 // default axis list is the first `len` axis ids
27778 fillOneAxis(0, axLetter);
27779 for(i = 1; i < len; i++) {
27780 fillOneAxis(i, axLetter + (i + 1));
27781 }
27782 }
27783
27784 return out;
27785}
27786
27787module.exports = {
27788 moduleType: 'component',
27789 name: 'grid',
27790
27791 schema: {
27792 layout: {grid: gridAttrs}
27793 },
27794
27795 layoutAttributes: gridAttrs,
27796 sizeDefaults: sizeDefaults,
27797 contentDefaults: contentDefaults
27798};
27799
27800},{"../../lib":177,"../../lib/regex":192,"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228,"../../plots/domain":249}],95:[function(_dereq_,module,exports){
27801/**
27802* Copyright 2012-2020, Plotly, Inc.
27803* All rights reserved.
27804*
27805* This source code is licensed under the MIT license found in the
27806* LICENSE file in the root directory of this source tree.
27807*/
27808
27809'use strict';
27810
27811var cartesianConstants = _dereq_('../../plots/cartesian/constants');
27812var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
27813
27814
27815module.exports = templatedArray('image', {
27816 visible: {
27817 valType: 'boolean',
27818
27819 dflt: true,
27820 editType: 'arraydraw',
27821
27822 },
27823
27824 source: {
27825 valType: 'string',
27826
27827 editType: 'arraydraw',
27828
27829 },
27830
27831 layer: {
27832 valType: 'enumerated',
27833 values: ['below', 'above'],
27834 dflt: 'above',
27835
27836 editType: 'arraydraw',
27837
27838 },
27839
27840 sizex: {
27841 valType: 'number',
27842
27843 dflt: 0,
27844 editType: 'arraydraw',
27845
27846 },
27847
27848 sizey: {
27849 valType: 'number',
27850
27851 dflt: 0,
27852 editType: 'arraydraw',
27853
27854 },
27855
27856 sizing: {
27857 valType: 'enumerated',
27858 values: ['fill', 'contain', 'stretch'],
27859 dflt: 'contain',
27860
27861 editType: 'arraydraw',
27862
27863 },
27864
27865 opacity: {
27866 valType: 'number',
27867
27868 min: 0,
27869 max: 1,
27870 dflt: 1,
27871 editType: 'arraydraw',
27872
27873 },
27874
27875 x: {
27876 valType: 'any',
27877
27878 dflt: 0,
27879 editType: 'arraydraw',
27880
27881 },
27882
27883 y: {
27884 valType: 'any',
27885
27886 dflt: 0,
27887 editType: 'arraydraw',
27888
27889 },
27890
27891 xanchor: {
27892 valType: 'enumerated',
27893 values: ['left', 'center', 'right'],
27894 dflt: 'left',
27895
27896 editType: 'arraydraw',
27897
27898 },
27899
27900 yanchor: {
27901 valType: 'enumerated',
27902 values: ['top', 'middle', 'bottom'],
27903 dflt: 'top',
27904
27905 editType: 'arraydraw',
27906
27907 },
27908
27909 xref: {
27910 valType: 'enumerated',
27911 values: [
27912 'paper',
27913 cartesianConstants.idRegex.x.toString()
27914 ],
27915 dflt: 'paper',
27916
27917 editType: 'arraydraw',
27918
27919 },
27920
27921 yref: {
27922 valType: 'enumerated',
27923 values: [
27924 'paper',
27925 cartesianConstants.idRegex.y.toString()
27926 ],
27927 dflt: 'paper',
27928
27929 editType: 'arraydraw',
27930
27931 },
27932 editType: 'arraydraw'
27933});
27934
27935},{"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228}],96:[function(_dereq_,module,exports){
27936/**
27937* Copyright 2012-2020, Plotly, Inc.
27938* All rights reserved.
27939*
27940* This source code is licensed under the MIT license found in the
27941* LICENSE file in the root directory of this source tree.
27942*/
27943
27944
27945'use strict';
27946
27947var isNumeric = _dereq_('fast-isnumeric');
27948var toLogRange = _dereq_('../../lib/to_log_range');
27949
27950/*
27951 * convertCoords: when converting an axis between log and linear
27952 * you need to alter any images on that axis to keep them
27953 * pointing at the same data point.
27954 * In v2.0 this will become obsolete (or perhaps size will still need conversion?)
27955 * we convert size by declaring that the maximum extent *in data units* should be
27956 * the same, assuming the image is anchored by its center (could remove that restriction
27957 * if we think it's important) even though the actual left and right values will not be
27958 * quite the same since the scale becomes nonlinear (and central anchor means the pixel
27959 * center of the image, not the data units center)
27960 *
27961 * gd: the plot div
27962 * ax: the axis being changed
27963 * newType: the type it's getting
27964 * doExtra: function(attr, val) from inside relayout that sets the attribute.
27965 * Use this to make the changes as it's aware if any other changes in the
27966 * same relayout call should override this conversion.
27967 */
27968module.exports = function convertCoords(gd, ax, newType, doExtra) {
27969 ax = ax || {};
27970
27971 var toLog = (newType === 'log') && (ax.type === 'linear');
27972 var fromLog = (newType === 'linear') && (ax.type === 'log');
27973
27974 if(!(toLog || fromLog)) return;
27975
27976 var images = gd._fullLayout.images;
27977 var axLetter = ax._id.charAt(0);
27978 var image;
27979 var attrPrefix;
27980
27981 for(var i = 0; i < images.length; i++) {
27982 image = images[i];
27983 attrPrefix = 'images[' + i + '].';
27984
27985 if(image[axLetter + 'ref'] === ax._id) {
27986 var currentPos = image[axLetter];
27987 var currentSize = image['size' + axLetter];
27988 var newPos = null;
27989 var newSize = null;
27990
27991 if(toLog) {
27992 newPos = toLogRange(currentPos, ax.range);
27993
27994 // this is the inverse of the conversion we do in fromLog below
27995 // so that the conversion is reversible (notice the fromLog conversion
27996 // is like sinh, and this one looks like arcsinh)
27997 var dx = currentSize / Math.pow(10, newPos) / 2;
27998 newSize = 2 * Math.log(dx + Math.sqrt(1 + dx * dx)) / Math.LN10;
27999 } else {
28000 newPos = Math.pow(10, currentPos);
28001 newSize = newPos * (Math.pow(10, currentSize / 2) - Math.pow(10, -currentSize / 2));
28002 }
28003
28004 // if conversion failed, delete the value so it can get a default later on
28005 if(!isNumeric(newPos)) {
28006 newPos = null;
28007 newSize = null;
28008 } else if(!isNumeric(newSize)) newSize = null;
28009
28010 doExtra(attrPrefix + axLetter, newPos);
28011 doExtra(attrPrefix + 'size' + axLetter, newSize);
28012 }
28013 }
28014};
28015
28016},{"../../lib/to_log_range":200,"fast-isnumeric":15}],97:[function(_dereq_,module,exports){
28017/**
28018* Copyright 2012-2020, Plotly, Inc.
28019* All rights reserved.
28020*
28021* This source code is licensed under the MIT license found in the
28022* LICENSE file in the root directory of this source tree.
28023*/
28024
28025'use strict';
28026
28027var Lib = _dereq_('../../lib');
28028var Axes = _dereq_('../../plots/cartesian/axes');
28029var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
28030
28031var attributes = _dereq_('./attributes');
28032var name = 'images';
28033
28034module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
28035 var opts = {
28036 name: name,
28037 handleItemDefaults: imageDefaults
28038 };
28039
28040 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
28041};
28042
28043
28044function imageDefaults(imageIn, imageOut, fullLayout) {
28045 function coerce(attr, dflt) {
28046 return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
28047 }
28048
28049 var source = coerce('source');
28050 var visible = coerce('visible', !!source);
28051
28052 if(!visible) return imageOut;
28053
28054 coerce('layer');
28055 coerce('xanchor');
28056 coerce('yanchor');
28057 coerce('sizex');
28058 coerce('sizey');
28059 coerce('sizing');
28060 coerce('opacity');
28061
28062 var gdMock = { _fullLayout: fullLayout };
28063 var axLetters = ['x', 'y'];
28064
28065 for(var i = 0; i < 2; i++) {
28066 // 'paper' is the fallback axref
28067 var axLetter = axLetters[i];
28068 var axRef = Axes.coerceRef(imageIn, imageOut, gdMock, axLetter, 'paper');
28069
28070 if(axRef !== 'paper') {
28071 var ax = Axes.getFromId(gdMock, axRef);
28072 ax._imgIndices.push(imageOut._index);
28073 }
28074
28075 Axes.coercePosition(imageOut, gdMock, coerce, axRef, axLetter, 0);
28076 }
28077
28078 return imageOut;
28079}
28080
28081},{"../../lib":177,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":95}],98:[function(_dereq_,module,exports){
28082/**
28083* Copyright 2012-2020, Plotly, Inc.
28084* All rights reserved.
28085*
28086* This source code is licensed under the MIT license found in the
28087* LICENSE file in the root directory of this source tree.
28088*/
28089
28090'use strict';
28091
28092var d3 = _dereq_('d3');
28093var Drawing = _dereq_('../drawing');
28094var Axes = _dereq_('../../plots/cartesian/axes');
28095var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
28096
28097module.exports = function draw(gd) {
28098 var fullLayout = gd._fullLayout;
28099 var imageDataAbove = [];
28100 var imageDataSubplot = {};
28101 var imageDataBelow = [];
28102 var subplot;
28103 var i;
28104
28105 // Sort into top, subplot, and bottom layers
28106 for(i = 0; i < fullLayout.images.length; i++) {
28107 var img = fullLayout.images[i];
28108
28109 if(img.visible) {
28110 if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
28111 subplot = img.xref + img.yref;
28112
28113 var plotinfo = fullLayout._plots[subplot];
28114
28115 if(!plotinfo) {
28116 // Fall back to _imageLowerLayer in case the requested subplot doesn't exist.
28117 // This can happen if you reference the image to an x / y axis combination
28118 // that doesn't have any data on it (and layer is below)
28119 imageDataBelow.push(img);
28120 continue;
28121 }
28122
28123 if(plotinfo.mainplot) {
28124 subplot = plotinfo.mainplot.id;
28125 }
28126
28127 if(!imageDataSubplot[subplot]) {
28128 imageDataSubplot[subplot] = [];
28129 }
28130 imageDataSubplot[subplot].push(img);
28131 } else if(img.layer === 'above') {
28132 imageDataAbove.push(img);
28133 } else {
28134 imageDataBelow.push(img);
28135 }
28136 }
28137 }
28138
28139
28140 var anchors = {
28141 x: {
28142 left: { sizing: 'xMin', offset: 0 },
28143 center: { sizing: 'xMid', offset: -1 / 2 },
28144 right: { sizing: 'xMax', offset: -1 }
28145 },
28146 y: {
28147 top: { sizing: 'YMin', offset: 0 },
28148 middle: { sizing: 'YMid', offset: -1 / 2 },
28149 bottom: { sizing: 'YMax', offset: -1 }
28150 }
28151 };
28152
28153
28154 // Images must be converted to dataURL's for exporting.
28155 function setImage(d) {
28156 var thisImage = d3.select(this);
28157
28158 if(this._imgSrc === d.source) {
28159 return;
28160 }
28161
28162 thisImage.attr('xmlns', xmlnsNamespaces.svg);
28163
28164 if(d.source && d.source.slice(0, 5) === 'data:') {
28165 thisImage.attr('xlink:href', d.source);
28166 this._imgSrc = d.source;
28167 } else {
28168 var imagePromise = new Promise(function(resolve) {
28169 var img = new Image();
28170 this.img = img;
28171
28172 // If not set, a `tainted canvas` error is thrown
28173 img.setAttribute('crossOrigin', 'anonymous');
28174 img.onerror = errorHandler;
28175 img.onload = function() {
28176 var canvas = document.createElement('canvas');
28177 canvas.width = this.width;
28178 canvas.height = this.height;
28179
28180 var ctx = canvas.getContext('2d');
28181 ctx.drawImage(this, 0, 0);
28182
28183 var dataURL = canvas.toDataURL('image/png');
28184
28185 thisImage.attr('xlink:href', dataURL);
28186
28187 // resolve promise in onload handler instead of on 'load' to support IE11
28188 // see https://github.com/plotly/plotly.js/issues/1685
28189 // for more details
28190 resolve();
28191 };
28192
28193 thisImage.on('error', errorHandler);
28194
28195 img.src = d.source;
28196 this._imgSrc = d.source;
28197
28198 function errorHandler() {
28199 thisImage.remove();
28200 resolve();
28201 }
28202 }.bind(this));
28203
28204 gd._promises.push(imagePromise);
28205 }
28206 }
28207
28208 function applyAttributes(d) {
28209 var thisImage = d3.select(this);
28210
28211 // Axes if specified
28212 var xa = Axes.getFromId(gd, d.xref);
28213 var ya = Axes.getFromId(gd, d.yref);
28214
28215 var size = fullLayout._size;
28216 var width = xa ? Math.abs(xa.l2p(d.sizex) - xa.l2p(0)) : d.sizex * size.w;
28217 var height = ya ? Math.abs(ya.l2p(d.sizey) - ya.l2p(0)) : d.sizey * size.h;
28218
28219 // Offsets for anchor positioning
28220 var xOffset = width * anchors.x[d.xanchor].offset;
28221 var yOffset = height * anchors.y[d.yanchor].offset;
28222
28223 var sizing = anchors.x[d.xanchor].sizing + anchors.y[d.yanchor].sizing;
28224
28225 // Final positions
28226 var xPos = (xa ? xa.r2p(d.x) + xa._offset : d.x * size.w + size.l) + xOffset;
28227 var yPos = (ya ? ya.r2p(d.y) + ya._offset : size.h - d.y * size.h + size.t) + yOffset;
28228
28229 // Construct the proper aspectRatio attribute
28230 switch(d.sizing) {
28231 case 'fill':
28232 sizing += ' slice';
28233 break;
28234
28235 case 'stretch':
28236 sizing = 'none';
28237 break;
28238 }
28239
28240 thisImage.attr({
28241 x: xPos,
28242 y: yPos,
28243 width: width,
28244 height: height,
28245 preserveAspectRatio: sizing,
28246 opacity: d.opacity
28247 });
28248
28249
28250 // Set proper clipping on images
28251 var xId = xa ? xa._id : '';
28252 var yId = ya ? ya._id : '';
28253 var clipAxes = xId + yId;
28254
28255 Drawing.setClipUrl(
28256 thisImage,
28257 clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
28258 gd
28259 );
28260 }
28261
28262 var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
28263 .data(imageDataBelow);
28264 var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
28265 .data(imageDataAbove);
28266
28267 imagesBelow.enter().append('image');
28268 imagesAbove.enter().append('image');
28269
28270 imagesBelow.exit().remove();
28271 imagesAbove.exit().remove();
28272
28273 imagesBelow.each(function(d) {
28274 setImage.bind(this)(d);
28275 applyAttributes.bind(this)(d);
28276 });
28277 imagesAbove.each(function(d) {
28278 setImage.bind(this)(d);
28279 applyAttributes.bind(this)(d);
28280 });
28281
28282 var allSubplots = Object.keys(fullLayout._plots);
28283 for(i = 0; i < allSubplots.length; i++) {
28284 subplot = allSubplots[i];
28285 var subplotObj = fullLayout._plots[subplot];
28286
28287 // filter out overlaid plots (which havd their images on the main plot)
28288 // and gl2d plots (which don't support below images, at least not yet)
28289 if(!subplotObj.imagelayer) continue;
28290
28291 var imagesOnSubplot = subplotObj.imagelayer.selectAll('image')
28292 // even if there are no images on this subplot, we need to run
28293 // enter and exit in case there were previously
28294 .data(imageDataSubplot[subplot] || []);
28295
28296 imagesOnSubplot.enter().append('image');
28297 imagesOnSubplot.exit().remove();
28298
28299 imagesOnSubplot.each(function(d) {
28300 setImage.bind(this)(d);
28301 applyAttributes.bind(this)(d);
28302 });
28303 }
28304};
28305
28306},{"../../constants/xmlns_namespaces":156,"../../plots/cartesian/axes":222,"../drawing":72,"d3":13}],99:[function(_dereq_,module,exports){
28307/**
28308* Copyright 2012-2020, Plotly, Inc.
28309* All rights reserved.
28310*
28311* This source code is licensed under the MIT license found in the
28312* LICENSE file in the root directory of this source tree.
28313*/
28314
28315'use strict';
28316
28317module.exports = {
28318 moduleType: 'component',
28319 name: 'images',
28320
28321 layoutAttributes: _dereq_('./attributes'),
28322 supplyLayoutDefaults: _dereq_('./defaults'),
28323 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('images'),
28324
28325 draw: _dereq_('./draw'),
28326
28327 convertCoords: _dereq_('./convert_coords')
28328};
28329
28330},{"../../plots/cartesian/include_components":234,"./attributes":95,"./convert_coords":96,"./defaults":97,"./draw":98}],100:[function(_dereq_,module,exports){
28331/**
28332* Copyright 2012-2020, Plotly, Inc.
28333* All rights reserved.
28334*
28335* This source code is licensed under the MIT license found in the
28336* LICENSE file in the root directory of this source tree.
28337*/
28338
28339'use strict';
28340
28341var fontAttrs = _dereq_('../../plots/font_attributes');
28342var colorAttrs = _dereq_('../color/attributes');
28343
28344
28345module.exports = {
28346 bgcolor: {
28347 valType: 'color',
28348
28349 editType: 'legend',
28350
28351 },
28352 bordercolor: {
28353 valType: 'color',
28354 dflt: colorAttrs.defaultLine,
28355
28356 editType: 'legend',
28357
28358 },
28359 borderwidth: {
28360 valType: 'number',
28361 min: 0,
28362 dflt: 0,
28363
28364 editType: 'legend',
28365
28366 },
28367 font: fontAttrs({
28368 editType: 'legend',
28369
28370 }),
28371 orientation: {
28372 valType: 'enumerated',
28373 values: ['v', 'h'],
28374 dflt: 'v',
28375
28376 editType: 'legend',
28377
28378 },
28379 traceorder: {
28380 valType: 'flaglist',
28381 flags: ['reversed', 'grouped'],
28382 extras: ['normal'],
28383
28384 editType: 'legend',
28385
28386 },
28387 tracegroupgap: {
28388 valType: 'number',
28389 min: 0,
28390 dflt: 10,
28391
28392 editType: 'legend',
28393
28394 },
28395 itemsizing: {
28396 valType: 'enumerated',
28397 values: ['trace', 'constant'],
28398 dflt: 'trace',
28399
28400 editType: 'legend',
28401
28402 },
28403
28404 itemclick: {
28405 valType: 'enumerated',
28406 values: ['toggle', 'toggleothers', false],
28407 dflt: 'toggle',
28408
28409 editType: 'legend',
28410
28411 },
28412 itemdoubleclick: {
28413 valType: 'enumerated',
28414 values: ['toggle', 'toggleothers', false],
28415 dflt: 'toggleothers',
28416
28417 editType: 'legend',
28418
28419 },
28420
28421 x: {
28422 valType: 'number',
28423 min: -2,
28424 max: 3,
28425
28426 editType: 'legend',
28427
28428 },
28429 xanchor: {
28430 valType: 'enumerated',
28431 values: ['auto', 'left', 'center', 'right'],
28432 dflt: 'left',
28433
28434 editType: 'legend',
28435
28436 },
28437 y: {
28438 valType: 'number',
28439 min: -2,
28440 max: 3,
28441
28442 editType: 'legend',
28443
28444 },
28445 yanchor: {
28446 valType: 'enumerated',
28447 values: ['auto', 'top', 'middle', 'bottom'],
28448
28449 editType: 'legend',
28450
28451 },
28452 uirevision: {
28453 valType: 'any',
28454
28455 editType: 'none',
28456
28457 },
28458 valign: {
28459 valType: 'enumerated',
28460 values: ['top', 'middle', 'bottom'],
28461 dflt: 'middle',
28462
28463 editType: 'legend',
28464
28465 },
28466 title: {
28467 text: {
28468 valType: 'string',
28469 dflt: '',
28470
28471 editType: 'legend',
28472
28473 },
28474 font: fontAttrs({
28475 editType: 'legend',
28476
28477 }),
28478 side: {
28479 valType: 'enumerated',
28480 values: ['top', 'left', 'top left'],
28481
28482 editType: 'legend',
28483
28484 },
28485 editType: 'legend',
28486 },
28487
28488 editType: 'legend'
28489};
28490
28491},{"../../plots/font_attributes":250,"../color/attributes":49}],101:[function(_dereq_,module,exports){
28492/**
28493* Copyright 2012-2020, Plotly, Inc.
28494* All rights reserved.
28495*
28496* This source code is licensed under the MIT license found in the
28497* LICENSE file in the root directory of this source tree.
28498*/
28499
28500'use strict';
28501
28502module.exports = {
28503 scrollBarWidth: 6,
28504 scrollBarMinHeight: 20,
28505 scrollBarColor: '#808BA4',
28506 scrollBarMargin: 4,
28507 scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0},
28508
28509 // number of px between legend title and (left) side of legend (always in x direction and from inner border)
28510 titlePad: 2,
28511 // number of px between legend symbol and legend text (always in x direction)
28512 textGap: 40,
28513 // number of px between each legend item (x and/or y direction)
28514 itemGap: 5
28515};
28516
28517},{}],102:[function(_dereq_,module,exports){
28518/**
28519* Copyright 2012-2020, Plotly, Inc.
28520* All rights reserved.
28521*
28522* This source code is licensed under the MIT license found in the
28523* LICENSE file in the root directory of this source tree.
28524*/
28525
28526'use strict';
28527
28528var Registry = _dereq_('../../registry');
28529var Lib = _dereq_('../../lib');
28530var Template = _dereq_('../../plot_api/plot_template');
28531
28532var attributes = _dereq_('./attributes');
28533var basePlotLayoutAttributes = _dereq_('../../plots/layout_attributes');
28534var helpers = _dereq_('./helpers');
28535
28536
28537module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
28538 var containerIn = layoutIn.legend || {};
28539
28540 var legendTraceCount = 0;
28541 var legendReallyHasATrace = false;
28542 var defaultOrder = 'normal';
28543
28544 for(var i = 0; i < fullData.length; i++) {
28545 var trace = fullData[i];
28546
28547 if(!trace.visible) continue;
28548
28549 // Note that we explicitly count any trace that is either shown or
28550 // *would* be shown by default, toward the two traces you need to
28551 // ensure the legend is shown by default, because this can still help
28552 // disambiguate.
28553 if(trace.showlegend || (
28554 trace._dfltShowLegend && !(
28555 trace._module &&
28556 trace._module.attributes &&
28557 trace._module.attributes.showlegend &&
28558 trace._module.attributes.showlegend.dflt === false
28559 )
28560 )) {
28561 legendTraceCount++;
28562 if(trace.showlegend) {
28563 legendReallyHasATrace = true;
28564 // Always show the legend by default if there's a pie,
28565 // or if there's only one trace but it's explicitly shown
28566 if(Registry.traceIs(trace, 'pie-like') ||
28567 trace._input.showlegend === true
28568 ) {
28569 legendTraceCount++;
28570 }
28571 }
28572 }
28573
28574 if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') ||
28575 ['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) {
28576 defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ?
28577 'grouped+reversed' : 'reversed';
28578 }
28579
28580 if(trace.legendgroup !== undefined && trace.legendgroup !== '') {
28581 defaultOrder = helpers.isReversed({traceorder: defaultOrder}) ?
28582 'reversed+grouped' : 'grouped';
28583 }
28584 }
28585
28586 var showLegend = Lib.coerce(layoutIn, layoutOut,
28587 basePlotLayoutAttributes, 'showlegend',
28588 legendReallyHasATrace && legendTraceCount > 1);
28589
28590 if(showLegend === false && !containerIn.uirevision) return;
28591
28592 var containerOut = Template.newContainer(layoutOut, 'legend');
28593
28594 function coerce(attr, dflt) {
28595 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
28596 }
28597
28598 coerce('uirevision', layoutOut.uirevision);
28599
28600 if(showLegend === false) return;
28601
28602 coerce('bgcolor', layoutOut.paper_bgcolor);
28603 coerce('bordercolor');
28604 coerce('borderwidth');
28605 Lib.coerceFont(coerce, 'font', layoutOut.font);
28606
28607 var orientation = coerce('orientation');
28608 var defaultX, defaultY, defaultYAnchor;
28609
28610 if(orientation === 'h') {
28611 defaultX = 0;
28612
28613 if(Registry.getComponentMethod('rangeslider', 'isVisible')(layoutIn.xaxis)) {
28614 defaultY = 1.1;
28615 defaultYAnchor = 'bottom';
28616 } else {
28617 // maybe use y=1.1 / yanchor=bottom as above
28618 // to avoid https://github.com/plotly/plotly.js/issues/1199
28619 // in v2
28620 defaultY = -0.1;
28621 defaultYAnchor = 'top';
28622 }
28623 } else {
28624 defaultX = 1.02;
28625 defaultY = 1;
28626 defaultYAnchor = 'auto';
28627 }
28628
28629 coerce('traceorder', defaultOrder);
28630 if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
28631
28632 coerce('itemsizing');
28633
28634 coerce('itemclick');
28635 coerce('itemdoubleclick');
28636
28637 coerce('x', defaultX);
28638 coerce('xanchor');
28639 coerce('y', defaultY);
28640 coerce('yanchor', defaultYAnchor);
28641 coerce('valign');
28642 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
28643
28644 var titleText = coerce('title.text');
28645 if(titleText) {
28646 coerce('title.side', orientation === 'h' ? 'left' : 'top');
28647 Lib.coerceFont(coerce, 'title.font', layoutOut.font);
28648 }
28649};
28650
28651},{"../../lib":177,"../../plot_api/plot_template":212,"../../plots/layout_attributes":261,"../../registry":272,"./attributes":100,"./helpers":106}],103:[function(_dereq_,module,exports){
28652/**
28653* Copyright 2012-2020, Plotly, Inc.
28654* All rights reserved.
28655*
28656* This source code is licensed under the MIT license found in the
28657* LICENSE file in the root directory of this source tree.
28658*/
28659
28660'use strict';
28661
28662var d3 = _dereq_('d3');
28663
28664var Lib = _dereq_('../../lib');
28665var Plots = _dereq_('../../plots/plots');
28666var Registry = _dereq_('../../registry');
28667var Events = _dereq_('../../lib/events');
28668var dragElement = _dereq_('../dragelement');
28669var Drawing = _dereq_('../drawing');
28670var Color = _dereq_('../color');
28671var svgTextUtils = _dereq_('../../lib/svg_text_utils');
28672var handleClick = _dereq_('./handle_click');
28673
28674var constants = _dereq_('./constants');
28675var alignmentConstants = _dereq_('../../constants/alignment');
28676var LINE_SPACING = alignmentConstants.LINE_SPACING;
28677var FROM_TL = alignmentConstants.FROM_TL;
28678var FROM_BR = alignmentConstants.FROM_BR;
28679
28680var getLegendData = _dereq_('./get_legend_data');
28681var style = _dereq_('./style');
28682var helpers = _dereq_('./helpers');
28683
28684module.exports = function draw(gd, opts) {
28685 var fullLayout = gd._fullLayout;
28686 var clipId = 'legend' + fullLayout._uid;
28687 var layer;
28688
28689 // Check whether this is the main legend (ie. called without any opts)
28690 if(!opts) {
28691 opts = fullLayout.legend || {};
28692 opts._main = true;
28693 layer = fullLayout._infolayer;
28694 } else {
28695 layer = opts.layer;
28696 clipId += '-hover';
28697 }
28698
28699 if(!layer) return;
28700
28701 if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
28702
28703 var legendData;
28704 if(opts._main) {
28705 if(!gd.calcdata) return;
28706 legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
28707 } else {
28708 if(!opts.entries) return;
28709 legendData = getLegendData(opts.entries, opts);
28710 }
28711
28712 var hiddenSlices = fullLayout.hiddenlabels || [];
28713
28714 if(opts._main && (!fullLayout.showlegend || !legendData.length)) {
28715 layer.selectAll('.legend').remove();
28716 fullLayout._topdefs.select('#' + clipId).remove();
28717 return Plots.autoMargin(gd, 'legend');
28718 }
28719
28720 var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) {
28721 if(opts._main) s.attr('pointer-events', 'all');
28722 });
28723
28724 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
28725 s.append('rect');
28726 });
28727
28728 var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
28729 s.attr('shape-rendering', 'crispEdges');
28730 });
28731 bg.call(Color.stroke, opts.bordercolor)
28732 .call(Color.fill, opts.bgcolor)
28733 .style('stroke-width', opts.borderwidth + 'px');
28734
28735 var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
28736
28737 var title = opts.title;
28738 opts._titleWidth = 0;
28739 opts._titleHeight = 0;
28740 if(title.text) {
28741 var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext');
28742 titleEl.attr('text-anchor', 'start')
28743 .classed('user-select-none', true)
28744 .call(Drawing.font, title.font)
28745 .text(title.text);
28746
28747 textLayout(titleEl, scrollBox, gd, opts); // handle mathjax or multi-line text and compute title height
28748 } else {
28749 scrollBox.selectAll('.legendtitletext').remove();
28750 }
28751
28752 var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) {
28753 s.attr(constants.scrollBarEnterAttrs)
28754 .call(Color.fill, constants.scrollBarColor);
28755 });
28756
28757 var groups = scrollBox.selectAll('g.groups').data(legendData);
28758 groups.enter().append('g').attr('class', 'groups');
28759 groups.exit().remove();
28760
28761 var traces = groups.selectAll('g.traces').data(Lib.identity);
28762 traces.enter().append('g').attr('class', 'traces');
28763 traces.exit().remove();
28764
28765 traces.style('opacity', function(d) {
28766 var trace = d[0].trace;
28767 if(Registry.traceIs(trace, 'pie-like')) {
28768 return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1;
28769 } else {
28770 return trace.visible === 'legendonly' ? 0.5 : 1;
28771 }
28772 })
28773 .each(function() { d3.select(this).call(drawTexts, gd, opts); })
28774 .call(style, gd, opts)
28775 .each(function() { if(opts._main) d3.select(this).call(setupTraceToggle, gd); });
28776
28777 Lib.syncOrAsync([
28778 Plots.previousPromises,
28779 function() { return computeLegendDimensions(gd, groups, traces, opts); },
28780 function() {
28781 // IF expandMargin return a Promise (which is truthy),
28782 // we're under a doAutoMargin redraw, so we don't have to
28783 // draw the remaining pieces below
28784 if(opts._main && expandMargin(gd)) return;
28785
28786 var gs = fullLayout._size;
28787 var bw = opts.borderwidth;
28788
28789 var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
28790 var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
28791
28792 if(opts._main && fullLayout.margin.autoexpand) {
28793 var lx0 = lx;
28794 var ly0 = ly;
28795
28796 lx = Lib.constrain(lx, 0, fullLayout.width - opts._width);
28797 ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight);
28798
28799 if(lx !== lx0) {
28800 Lib.log('Constrain legend.x to make legend fit inside graph');
28801 }
28802 if(ly !== ly0) {
28803 Lib.log('Constrain legend.y to make legend fit inside graph');
28804 }
28805 }
28806
28807 // Set size and position of all the elements that make up a legend:
28808 // legend, background and border, scroll box and scroll bar as well as title
28809 if(opts._main) Drawing.setTranslate(legend, lx, ly);
28810
28811 // to be safe, remove previous listeners
28812 scrollBar.on('.drag', null);
28813 legend.on('wheel', null);
28814
28815 if(!opts._main || opts._height <= opts._maxHeight || gd._context.staticPlot) {
28816 // if scrollbar should not be shown.
28817 var height = opts._effHeight;
28818
28819 // if not the main legend, let it be its full size
28820 if(!opts._main) height = opts._height;
28821
28822 bg.attr({
28823 width: opts._width - bw,
28824 height: height - bw,
28825 x: bw / 2,
28826 y: bw / 2
28827 });
28828
28829 Drawing.setTranslate(scrollBox, 0, 0);
28830
28831 clipPath.select('rect').attr({
28832 width: opts._width - 2 * bw,
28833 height: height - 2 * bw,
28834 x: bw,
28835 y: bw
28836 });
28837
28838 Drawing.setClipUrl(scrollBox, clipId, gd);
28839
28840 Drawing.setRect(scrollBar, 0, 0, 0, 0);
28841 delete opts._scrollY;
28842 } else {
28843 var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
28844 opts._effHeight * opts._effHeight / opts._height);
28845 var scrollBarYMax = opts._effHeight -
28846 scrollBarHeight -
28847 2 * constants.scrollBarMargin;
28848 var scrollBoxYMax = opts._height - opts._effHeight;
28849 var scrollRatio = scrollBarYMax / scrollBoxYMax;
28850
28851 var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax);
28852
28853 // increase the background and clip-path width
28854 // by the scrollbar width and margin
28855 bg.attr({
28856 width: opts._width -
28857 2 * bw +
28858 constants.scrollBarWidth +
28859 constants.scrollBarMargin,
28860 height: opts._effHeight - bw,
28861 x: bw / 2,
28862 y: bw / 2
28863 });
28864
28865 clipPath.select('rect').attr({
28866 width: opts._width -
28867 2 * bw +
28868 constants.scrollBarWidth +
28869 constants.scrollBarMargin,
28870 height: opts._effHeight - 2 * bw,
28871 x: bw,
28872 y: bw + scrollBoxY
28873 });
28874
28875 Drawing.setClipUrl(scrollBox, clipId, gd);
28876
28877 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
28878
28879 // scroll legend by mousewheel or touchpad swipe up/down
28880 legend.on('wheel', function() {
28881 scrollBoxY = Lib.constrain(
28882 opts._scrollY +
28883 ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax),
28884 0, scrollBoxYMax);
28885 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
28886 if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) {
28887 d3.event.preventDefault();
28888 }
28889 });
28890
28891 var eventY0, eventY1, scrollBoxY0;
28892
28893 var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) {
28894 var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0;
28895 return Lib.constrain(y, 0, scrollBoxYMax);
28896 };
28897
28898 var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) {
28899 var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0;
28900 return Lib.constrain(y, 0, scrollBoxYMax);
28901 };
28902
28903 // scroll legend by dragging scrollBAR
28904 var scrollBarDrag = d3.behavior.drag()
28905 .on('dragstart', function() {
28906 var e = d3.event.sourceEvent;
28907 if(e.type === 'touchstart') {
28908 eventY0 = e.changedTouches[0].clientY;
28909 } else {
28910 eventY0 = e.clientY;
28911 }
28912 scrollBoxY0 = scrollBoxY;
28913 })
28914 .on('drag', function() {
28915 var e = d3.event.sourceEvent;
28916 if(e.buttons === 2 || e.ctrlKey) return;
28917 if(e.type === 'touchmove') {
28918 eventY1 = e.changedTouches[0].clientY;
28919 } else {
28920 eventY1 = e.clientY;
28921 }
28922 scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1);
28923 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
28924 });
28925 scrollBar.call(scrollBarDrag);
28926
28927 // scroll legend by touch-dragging scrollBOX
28928 var scrollBoxTouchDrag = d3.behavior.drag()
28929 .on('dragstart', function() {
28930 var e = d3.event.sourceEvent;
28931 if(e.type === 'touchstart') {
28932 eventY0 = e.changedTouches[0].clientY;
28933 scrollBoxY0 = scrollBoxY;
28934 }
28935 })
28936 .on('drag', function() {
28937 var e = d3.event.sourceEvent;
28938 if(e.type === 'touchmove') {
28939 eventY1 = e.changedTouches[0].clientY;
28940 scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1);
28941 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
28942 }
28943 });
28944 scrollBox.call(scrollBoxTouchDrag);
28945 }
28946
28947 function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
28948 opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
28949 Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
28950
28951 Drawing.setRect(
28952 scrollBar,
28953 opts._width,
28954 constants.scrollBarMargin + scrollBoxY * scrollRatio,
28955 constants.scrollBarWidth,
28956 scrollBarHeight
28957 );
28958 clipPath.select('rect').attr('y', bw + scrollBoxY);
28959 }
28960
28961 if(gd._context.edits.legendPosition) {
28962 var xf, yf, x0, y0;
28963
28964 legend.classed('cursor-move', true);
28965
28966 dragElement.init({
28967 element: legend.node(),
28968 gd: gd,
28969 prepFn: function() {
28970 var transform = Drawing.getTranslate(legend);
28971 x0 = transform.x;
28972 y0 = transform.y;
28973 },
28974 moveFn: function(dx, dy) {
28975 var newX = x0 + dx;
28976 var newY = y0 + dy;
28977
28978 Drawing.setTranslate(legend, newX, newY);
28979
28980 xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
28981 yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
28982 },
28983 doneFn: function() {
28984 if(xf !== undefined && yf !== undefined) {
28985 Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf});
28986 }
28987 },
28988 clickFn: function(numClicks, e) {
28989 var clickedTrace = layer.selectAll('g.traces').filter(function() {
28990 var bbox = this.getBoundingClientRect();
28991 return (
28992 e.clientX >= bbox.left && e.clientX <= bbox.right &&
28993 e.clientY >= bbox.top && e.clientY <= bbox.bottom
28994 );
28995 });
28996 if(clickedTrace.size() > 0) {
28997 clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e);
28998 }
28999 }
29000 });
29001 }
29002 }], gd);
29003};
29004
29005function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
29006 var trace = legendItem.data()[0][0].trace;
29007 var evtData = {
29008 event: evt,
29009 node: legendItem.node(),
29010 curveNumber: trace.index,
29011 expandedIndex: trace._expandedIndex,
29012 data: gd.data,
29013 layout: gd.layout,
29014 frames: gd._transitionData._frames,
29015 config: gd._context,
29016 fullData: gd._fullData,
29017 fullLayout: gd._fullLayout
29018 };
29019
29020 if(trace._group) {
29021 evtData.group = trace._group;
29022 }
29023 if(Registry.traceIs(trace, 'pie-like')) {
29024 evtData.label = legendItem.datum()[0].label;
29025 }
29026
29027 var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData);
29028 if(clickVal === false) return;
29029
29030 if(numClicks === 1) {
29031 legend._clickTimeout = setTimeout(function() {
29032 handleClick(legendItem, gd, numClicks);
29033 }, gd._context.doubleClickDelay);
29034 } else if(numClicks === 2) {
29035 if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
29036 gd._legendMouseDownTime = 0;
29037
29038 var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
29039 if(dblClickVal !== false) handleClick(legendItem, gd, numClicks);
29040 }
29041}
29042
29043function drawTexts(g, gd, opts) {
29044 var legendItem = g.data()[0][0];
29045 var trace = legendItem.trace;
29046 var isPieLike = Registry.traceIs(trace, 'pie-like');
29047 var traceIndex = trace.index;
29048 var isEditable = opts._main && gd._context.edits.legendText && !isPieLike;
29049 var maxNameLength = opts._maxNameLength;
29050
29051 var name;
29052 if(!opts.entries) {
29053 name = isPieLike ? legendItem.label : trace.name;
29054 if(trace._meta) {
29055 name = Lib.templateString(name, trace._meta);
29056 }
29057 } else {
29058 name = legendItem.text;
29059 }
29060
29061 var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
29062
29063 textEl.attr('text-anchor', 'start')
29064 .classed('user-select-none', true)
29065 .call(Drawing.font, opts.font)
29066 .text(isEditable ? ensureLength(name, maxNameLength) : name);
29067
29068 svgTextUtils.positionText(textEl, constants.textGap, 0);
29069
29070 if(isEditable) {
29071 textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
29072 .call(textLayout, g, gd, opts)
29073 .on('edit', function(newName) {
29074 this.text(ensureLength(newName, maxNameLength))
29075 .call(textLayout, g, gd, opts);
29076
29077 var fullInput = legendItem.trace._fullInput || {};
29078 var update = {};
29079
29080 if(Registry.hasTransform(fullInput, 'groupby')) {
29081 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
29082 var index = groupbyIndices[groupbyIndices.length - 1];
29083
29084 var kcont = Lib.keyedContainer(fullInput, 'transforms[' + index + '].styles', 'target', 'value.name');
29085
29086 kcont.set(legendItem.trace._group, newName);
29087
29088 update = kcont.constructUpdate();
29089 } else {
29090 update.name = newName;
29091 }
29092
29093 return Registry.call('_guiRestyle', gd, update, traceIndex);
29094 });
29095 } else {
29096 textLayout(textEl, g, gd, opts);
29097 }
29098}
29099
29100/*
29101 * Make sure we have a reasonably clickable region.
29102 * If this string is missing or very short, pad it with spaces out to at least
29103 * 4 characters, up to the max length of other labels, on the assumption that
29104 * most characters are wider than spaces so a string of spaces will usually be
29105 * no wider than the real labels.
29106 */
29107function ensureLength(str, maxLength) {
29108 var targetLength = Math.max(4, maxLength);
29109 if(str && str.trim().length >= targetLength / 2) return str;
29110 str = str || '';
29111 for(var i = targetLength - str.length; i > 0; i--) str += ' ';
29112 return str;
29113}
29114
29115function setupTraceToggle(g, gd) {
29116 var doubleClickDelay = gd._context.doubleClickDelay;
29117 var newMouseDownTime;
29118 var numClicks = 1;
29119
29120 var traceToggle = Lib.ensureSingle(g, 'rect', 'legendtoggle', function(s) {
29121 s.style('cursor', 'pointer')
29122 .attr('pointer-events', 'all')
29123 .call(Color.fill, 'rgba(0,0,0,0)');
29124 });
29125
29126 traceToggle.on('mousedown', function() {
29127 newMouseDownTime = (new Date()).getTime();
29128 if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) {
29129 // in a click train
29130 numClicks += 1;
29131 } else {
29132 // new click train
29133 numClicks = 1;
29134 gd._legendMouseDownTime = newMouseDownTime;
29135 }
29136 });
29137 traceToggle.on('mouseup', function() {
29138 if(gd._dragged || gd._editing) return;
29139 var legend = gd._fullLayout.legend;
29140
29141 if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) {
29142 numClicks = Math.max(numClicks - 1, 1);
29143 }
29144
29145 clickOrDoubleClick(gd, legend, g, numClicks, d3.event);
29146 });
29147}
29148
29149function textLayout(s, g, gd, opts) {
29150 if(!opts._main) s.attr('data-notex', true); // do not process MathJax if not main
29151 svgTextUtils.convertToTspans(s, gd, function() {
29152 computeTextDimensions(g, gd, opts);
29153 });
29154}
29155
29156function computeTextDimensions(g, gd, opts) {
29157 var legendItem = g.data()[0][0];
29158 if(opts._main && legendItem && !legendItem.trace.showlegend) {
29159 g.remove();
29160 return;
29161 }
29162
29163 var mathjaxGroup = g.select('g[class*=math-group]');
29164 var mathjaxNode = mathjaxGroup.node();
29165 if(!opts) opts = gd._fullLayout.legend;
29166 var bw = opts.borderwidth;
29167 var lineHeight = (legendItem ? opts : opts.title).font.size * LINE_SPACING;
29168 var height, width;
29169
29170 if(mathjaxNode) {
29171 var mathjaxBB = Drawing.bBox(mathjaxNode);
29172
29173 height = mathjaxBB.height;
29174 width = mathjaxBB.width;
29175
29176 if(legendItem) {
29177 Drawing.setTranslate(mathjaxGroup, 0, height * 0.25);
29178 } else { // case of title
29179 Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw);
29180 }
29181 } else {
29182 var textEl = g.select(legendItem ?
29183 '.legendtext' : '.legendtitletext'
29184 );
29185 var textLines = svgTextUtils.lineCount(textEl);
29186 var textNode = textEl.node();
29187
29188 height = lineHeight * textLines;
29189 width = textNode ? Drawing.bBox(textNode).width : 0;
29190
29191 // approximation to height offset to center the font
29192 // to avoid getBoundingClientRect
29193 var textY = lineHeight * ((textLines - 1) / 2 - 0.3);
29194 if(legendItem) {
29195 svgTextUtils.positionText(textEl, constants.textGap, -textY);
29196 } else { // case of title
29197 svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw);
29198 }
29199 }
29200
29201 if(legendItem) {
29202 legendItem.lineHeight = lineHeight;
29203 legendItem.height = Math.max(height, 16) + 3;
29204 legendItem.width = width;
29205 } else { // case of title
29206 opts._titleWidth = width;
29207 opts._titleHeight = height;
29208 }
29209}
29210
29211function getTitleSize(opts) {
29212 var w = 0;
29213 var h = 0;
29214
29215 var side = opts.title.side;
29216 if(side) {
29217 if(side.indexOf('left') !== -1) {
29218 w = opts._titleWidth;
29219 }
29220 if(side.indexOf('top') !== -1) {
29221 h = opts._titleHeight;
29222 }
29223 }
29224
29225 return [w, h];
29226}
29227
29228/*
29229 * Computes in fullLayout.legend:
29230 *
29231 * - _height: legend height including items past scrollbox height
29232 * - _maxHeight: maximum legend height before scrollbox is required
29233 * - _effHeight: legend height w/ or w/o scrollbox
29234 *
29235 * - _width: legend width
29236 * - _maxWidth (for orientation:h only): maximum width before starting new row
29237 */
29238function computeLegendDimensions(gd, groups, traces, opts) {
29239 var fullLayout = gd._fullLayout;
29240 if(!opts) opts = fullLayout.legend;
29241 var gs = fullLayout._size;
29242
29243 var isVertical = helpers.isVertical(opts);
29244 var isGrouped = helpers.isGrouped(opts);
29245
29246 var bw = opts.borderwidth;
29247 var bw2 = 2 * bw;
29248 var textGap = constants.textGap;
29249 var itemGap = constants.itemGap;
29250 var endPad = 2 * (bw + itemGap);
29251
29252 var yanchor = getYanchor(opts);
29253 var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top');
29254 var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom');
29255
29256 // - if below/above plot area, give it the maximum potential margin-push value
29257 // - otherwise, extend the height of the plot area
29258 opts._maxHeight = Math.max(
29259 (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h,
29260 30
29261 );
29262
29263 var toggleRectWidth = 0;
29264 opts._width = 0;
29265 opts._height = 0;
29266 var titleSize = getTitleSize(opts);
29267
29268 if(isVertical) {
29269 traces.each(function(d) {
29270 var h = d[0].height;
29271 Drawing.setTranslate(this,
29272 bw + titleSize[0],
29273 bw + titleSize[1] + opts._height + h / 2 + itemGap
29274 );
29275 opts._height += h;
29276 opts._width = Math.max(opts._width, d[0].width);
29277 });
29278
29279 toggleRectWidth = textGap + opts._width;
29280 opts._width += itemGap + textGap + bw2;
29281 opts._height += endPad;
29282
29283 if(isGrouped) {
29284 groups.each(function(d, i) {
29285 Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
29286 });
29287 opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap;
29288 }
29289 } else {
29290 var xanchor = getXanchor(opts);
29291 var isLeftOfPlotArea = opts.x < 0 || (opts.x === 0 && xanchor === 'right');
29292 var isRightOfPlotArea = opts.x > 1 || (opts.x === 1 && xanchor === 'left');
29293 var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea;
29294 var hw = fullLayout.width / 2;
29295
29296 // - if placed within x-margins, extend the width of the plot area
29297 // - else if below/above plot area and anchored in the margin, extend to opposite margin,
29298 // - otherwise give it the maximum potential margin-push value
29299 opts._maxWidth = Math.max(
29300 isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) :
29301 isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) :
29302 gs.w,
29303 2 * textGap);
29304 var maxItemWidth = 0;
29305 var combinedItemWidth = 0;
29306 traces.each(function(d) {
29307 var w = d[0].width + textGap;
29308 maxItemWidth = Math.max(maxItemWidth, w);
29309 combinedItemWidth += w;
29310 });
29311
29312 toggleRectWidth = null;
29313 var maxRowWidth = 0;
29314
29315 if(isGrouped) {
29316 var maxGroupHeightInRow = 0;
29317 var groupOffsetX = 0;
29318 var groupOffsetY = 0;
29319 groups.each(function() {
29320 var maxWidthInGroup = 0;
29321 var offsetY = 0;
29322 d3.select(this).selectAll('g.traces').each(function(d) {
29323 var h = d[0].height;
29324 Drawing.setTranslate(this,
29325 titleSize[0],
29326 titleSize[1] + bw + itemGap + h / 2 + offsetY
29327 );
29328 offsetY += h;
29329 maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width);
29330 });
29331 maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY);
29332
29333 var next = maxWidthInGroup + itemGap;
29334
29335 if((next + bw + groupOffsetX) > opts._maxWidth) {
29336 maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
29337 groupOffsetX = 0;
29338 groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap;
29339 maxGroupHeightInRow = offsetY;
29340 }
29341
29342 Drawing.setTranslate(this, groupOffsetX, groupOffsetY);
29343
29344 groupOffsetX += next;
29345 });
29346
29347 opts._width = Math.max(maxRowWidth, groupOffsetX) + bw;
29348 opts._height = groupOffsetY + maxGroupHeightInRow + endPad;
29349 } else {
29350 var nTraces = traces.size();
29351 var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth;
29352
29353 var maxItemHeightInRow = 0;
29354 var offsetX = 0;
29355 var offsetY = 0;
29356 var rowWidth = 0;
29357 traces.each(function(d) {
29358 var h = d[0].height;
29359 var w = textGap + d[0].width;
29360 var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
29361
29362 if((next + bw + offsetX) > opts._maxWidth) {
29363 maxRowWidth = Math.max(maxRowWidth, rowWidth);
29364 offsetX = 0;
29365 offsetY += maxItemHeightInRow;
29366 opts._height += maxItemHeightInRow;
29367 maxItemHeightInRow = 0;
29368 }
29369
29370 Drawing.setTranslate(this,
29371 titleSize[0] + bw + offsetX,
29372 titleSize[1] + bw + offsetY + h / 2 + itemGap
29373 );
29374
29375 rowWidth = offsetX + w + itemGap;
29376 offsetX += next;
29377 maxItemHeightInRow = Math.max(maxItemHeightInRow, h);
29378 });
29379
29380 if(oneRowLegend) {
29381 opts._width = offsetX + bw2;
29382 opts._height = maxItemHeightInRow + endPad;
29383 } else {
29384 opts._width = Math.max(maxRowWidth, rowWidth) + bw2;
29385 opts._height += maxItemHeightInRow + endPad;
29386 }
29387 }
29388 }
29389
29390 opts._width = Math.ceil(
29391 Math.max(
29392 opts._width + titleSize[0],
29393 opts._titleWidth + 2 * (bw + constants.titlePad)
29394 )
29395 );
29396
29397 opts._height = Math.ceil(
29398 Math.max(
29399 opts._height + titleSize[1],
29400 opts._titleHeight + 2 * (bw + constants.itemGap)
29401 )
29402 );
29403
29404 opts._effHeight = Math.min(opts._height, opts._maxHeight);
29405
29406 var edits = gd._context.edits;
29407 var isEditable = edits.legendText || edits.legendPosition;
29408 traces.each(function(d) {
29409 var traceToggle = d3.select(this).select('.legendtoggle');
29410 var h = d[0].height;
29411 var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width));
29412 if(!isVertical) w += itemGap / 2;
29413 Drawing.setRect(traceToggle, 0, -h / 2, w, h);
29414 });
29415}
29416
29417function expandMargin(gd) {
29418 var fullLayout = gd._fullLayout;
29419 var opts = fullLayout.legend;
29420 var xanchor = getXanchor(opts);
29421 var yanchor = getYanchor(opts);
29422
29423 return Plots.autoMargin(gd, 'legend', {
29424 x: opts.x,
29425 y: opts.y,
29426 l: opts._width * (FROM_TL[xanchor]),
29427 r: opts._width * (FROM_BR[xanchor]),
29428 b: opts._effHeight * (FROM_BR[yanchor]),
29429 t: opts._effHeight * (FROM_TL[yanchor])
29430 });
29431}
29432
29433function getXanchor(opts) {
29434 return Lib.isRightAnchor(opts) ? 'right' :
29435 Lib.isCenterAnchor(opts) ? 'center' :
29436 'left';
29437}
29438
29439function getYanchor(opts) {
29440 return Lib.isBottomAnchor(opts) ? 'bottom' :
29441 Lib.isMiddleAnchor(opts) ? 'middle' :
29442 'top';
29443}
29444
29445},{"../../constants/alignment":152,"../../lib":177,"../../lib/events":169,"../../lib/svg_text_utils":198,"../../plots/plots":263,"../../registry":272,"../color":50,"../dragelement":69,"../drawing":72,"./constants":101,"./get_legend_data":104,"./handle_click":105,"./helpers":106,"./style":108,"d3":13}],104:[function(_dereq_,module,exports){
29446/**
29447* Copyright 2012-2020, Plotly, Inc.
29448* All rights reserved.
29449*
29450* This source code is licensed under the MIT license found in the
29451* LICENSE file in the root directory of this source tree.
29452*/
29453
29454'use strict';
29455
29456var Registry = _dereq_('../../registry');
29457var helpers = _dereq_('./helpers');
29458
29459module.exports = function getLegendData(calcdata, opts) {
29460 var lgroupToTraces = {};
29461 var lgroups = [];
29462 var hasOneNonBlankGroup = false;
29463 var slicesShown = {};
29464 var lgroupi = 0;
29465 var maxNameLength = 0;
29466 var i, j;
29467 var main = opts._main;
29468
29469 function addOneItem(legendGroup, legendItem) {
29470 // each '' legend group is treated as a separate group
29471 if(legendGroup === '' || !helpers.isGrouped(opts)) {
29472 // TODO: check this against fullData legendgroups?
29473 var uniqueGroup = '~~i' + lgroupi;
29474 lgroups.push(uniqueGroup);
29475 lgroupToTraces[uniqueGroup] = [[legendItem]];
29476 lgroupi++;
29477 } else if(lgroups.indexOf(legendGroup) === -1) {
29478 lgroups.push(legendGroup);
29479 hasOneNonBlankGroup = true;
29480 lgroupToTraces[legendGroup] = [[legendItem]];
29481 } else {
29482 lgroupToTraces[legendGroup].push([legendItem]);
29483 }
29484 }
29485
29486 // build an { legendgroup: [cd0, cd0], ... } object
29487 for(i = 0; i < calcdata.length; i++) {
29488 var cd = calcdata[i];
29489 var cd0 = cd[0];
29490 var trace = cd0.trace;
29491 var lgroup = trace.legendgroup;
29492
29493 if(main && (!trace.visible || !trace.showlegend)) continue;
29494
29495 if(Registry.traceIs(trace, 'pie-like')) {
29496 if(!slicesShown[lgroup]) slicesShown[lgroup] = {};
29497
29498 for(j = 0; j < cd.length; j++) {
29499 var labelj = cd[j].label;
29500
29501 if(!slicesShown[lgroup][labelj]) {
29502 addOneItem(lgroup, {
29503 label: labelj,
29504 color: cd[j].color,
29505 i: cd[j].i,
29506 trace: trace,
29507 pts: cd[j].pts
29508 });
29509
29510 slicesShown[lgroup][labelj] = true;
29511 maxNameLength = Math.max(maxNameLength, (labelj || '').length);
29512 }
29513 }
29514 } else {
29515 addOneItem(lgroup, cd0);
29516 maxNameLength = Math.max(maxNameLength, (trace.name || '').length);
29517 }
29518 }
29519
29520 // won't draw a legend in this case
29521 if(!lgroups.length) return [];
29522
29523 // rearrange lgroupToTraces into a d3-friendly array of arrays
29524 var lgroupsLength = lgroups.length;
29525 var ltraces;
29526 var legendData;
29527
29528 if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
29529 legendData = new Array(lgroupsLength);
29530
29531 for(i = 0; i < lgroupsLength; i++) {
29532 ltraces = lgroupToTraces[lgroups[i]];
29533 legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
29534 }
29535 } else {
29536 // collapse all groups into one if all groups are blank
29537 legendData = [new Array(lgroupsLength)];
29538
29539 for(i = 0; i < lgroupsLength; i++) {
29540 ltraces = lgroupToTraces[lgroups[i]][0];
29541 legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
29542 }
29543 lgroupsLength = 1;
29544 }
29545
29546 // number of legend groups - needed in legend/draw.js
29547 opts._lgroupsLength = lgroupsLength;
29548 // maximum name/label length - needed in legend/draw.js
29549 opts._maxNameLength = maxNameLength;
29550
29551 return legendData;
29552};
29553
29554},{"../../registry":272,"./helpers":106}],105:[function(_dereq_,module,exports){
29555/**
29556* Copyright 2012-2020, Plotly, Inc.
29557* All rights reserved.
29558*
29559* This source code is licensed under the MIT license found in the
29560* LICENSE file in the root directory of this source tree.
29561*/
29562
29563'use strict';
29564
29565var Lib = _dereq_('../../lib');
29566var Registry = _dereq_('../../registry');
29567
29568var SHOWISOLATETIP = true;
29569
29570module.exports = function handleClick(g, gd, numClicks) {
29571 var fullLayout = gd._fullLayout;
29572
29573 if(gd._dragged || gd._editing) return;
29574
29575 var itemClick = fullLayout.legend.itemclick;
29576 var itemDoubleClick = fullLayout.legend.itemdoubleclick;
29577
29578 if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'toggleothers' &&
29579 SHOWISOLATETIP && gd.data && gd._context.showTips
29580 ) {
29581 Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long');
29582 SHOWISOLATETIP = false;
29583 } else {
29584 SHOWISOLATETIP = false;
29585 }
29586
29587 var mode;
29588 if(numClicks === 1) mode = itemClick;
29589 else if(numClicks === 2) mode = itemDoubleClick;
29590 if(!mode) return;
29591
29592 var hiddenSlices = fullLayout.hiddenlabels ?
29593 fullLayout.hiddenlabels.slice() :
29594 [];
29595
29596 var legendItem = g.data()[0][0];
29597 var fullData = gd._fullData;
29598 var fullTrace = legendItem.trace;
29599 var legendgroup = fullTrace.legendgroup;
29600
29601 var i, j, kcont, key, keys, val;
29602 var attrUpdate = {};
29603 var attrIndices = [];
29604 var carrs = [];
29605 var carrIdx = [];
29606
29607 function insertUpdate(traceIndex, key, value) {
29608 var attrIndex = attrIndices.indexOf(traceIndex);
29609 var valueArray = attrUpdate[key];
29610 if(!valueArray) {
29611 valueArray = attrUpdate[key] = [];
29612 }
29613
29614 if(attrIndices.indexOf(traceIndex) === -1) {
29615 attrIndices.push(traceIndex);
29616 attrIndex = attrIndices.length - 1;
29617 }
29618
29619 valueArray[attrIndex] = value;
29620
29621 return attrIndex;
29622 }
29623
29624 function setVisibility(fullTrace, visibility) {
29625 var fullInput = fullTrace._fullInput;
29626 if(Registry.hasTransform(fullInput, 'groupby')) {
29627 var kcont = carrs[fullInput.index];
29628 if(!kcont) {
29629 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
29630 var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1];
29631 kcont = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible');
29632 carrs[fullInput.index] = kcont;
29633 }
29634
29635 var curState = kcont.get(fullTrace._group);
29636
29637 // If not specified, assume visible. This happens if there are other style
29638 // properties set for a group but not the visibility. There are many similar
29639 // ways to do this (e.g. why not just `curState = fullTrace.visible`??? The
29640 // answer is: because it breaks other things like groupby trace names in
29641 // subtle ways.)
29642 if(curState === undefined) {
29643 curState = true;
29644 }
29645
29646 if(curState !== false) {
29647 // true -> legendonly. All others toggle to true:
29648 kcont.set(fullTrace._group, visibility);
29649 }
29650 carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true);
29651 } else {
29652 // false -> false (not possible since will not be visible in legend)
29653 // true -> legendonly
29654 // legendonly -> true
29655 var nextVisibility = fullInput.visible === false ? false : visibility;
29656
29657 insertUpdate(fullInput.index, 'visible', nextVisibility);
29658 }
29659 }
29660
29661 if(Registry.traceIs(fullTrace, 'pie-like')) {
29662 var thisLabel = legendItem.label;
29663 var thisLabelIndex = hiddenSlices.indexOf(thisLabel);
29664
29665 if(mode === 'toggle') {
29666 if(thisLabelIndex === -1) hiddenSlices.push(thisLabel);
29667 else hiddenSlices.splice(thisLabelIndex, 1);
29668 } else if(mode === 'toggleothers') {
29669 hiddenSlices = [];
29670 gd.calcdata[0].forEach(function(d) {
29671 if(thisLabel !== d.label) {
29672 hiddenSlices.push(d.label);
29673 }
29674 });
29675 if(gd._fullLayout.hiddenlabels && gd._fullLayout.hiddenlabels.length === hiddenSlices.length && thisLabelIndex === -1) {
29676 hiddenSlices = [];
29677 }
29678 }
29679
29680 Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices);
29681 } else {
29682 var hasLegendgroup = legendgroup && legendgroup.length;
29683 var traceIndicesInGroup = [];
29684 var tracei;
29685 if(hasLegendgroup) {
29686 for(i = 0; i < fullData.length; i++) {
29687 tracei = fullData[i];
29688 if(!tracei.visible) continue;
29689 if(tracei.legendgroup === legendgroup) {
29690 traceIndicesInGroup.push(i);
29691 }
29692 }
29693 }
29694
29695 if(mode === 'toggle') {
29696 var nextVisibility;
29697
29698 switch(fullTrace.visible) {
29699 case true:
29700 nextVisibility = 'legendonly';
29701 break;
29702 case false:
29703 nextVisibility = false;
29704 break;
29705 case 'legendonly':
29706 nextVisibility = true;
29707 break;
29708 }
29709
29710 if(hasLegendgroup) {
29711 for(i = 0; i < fullData.length; i++) {
29712 if(fullData[i].visible !== false && fullData[i].legendgroup === legendgroup) {
29713 setVisibility(fullData[i], nextVisibility);
29714 }
29715 }
29716 } else {
29717 setVisibility(fullTrace, nextVisibility);
29718 }
29719 } else if(mode === 'toggleothers') {
29720 // Compute the clicked index. expandedIndex does what we want for expanded traces
29721 // but also culls hidden traces. That means we have some work to do.
29722 var isClicked, isInGroup, notInLegend, otherState;
29723 var isIsolated = true;
29724 for(i = 0; i < fullData.length; i++) {
29725 isClicked = fullData[i] === fullTrace;
29726 notInLegend = fullData[i].showlegend !== true;
29727 if(isClicked || notInLegend) continue;
29728
29729 isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup);
29730
29731 if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
29732 isIsolated = false;
29733 break;
29734 }
29735 }
29736
29737 for(i = 0; i < fullData.length; i++) {
29738 // False is sticky; we don't change it.
29739 if(fullData[i].visible === false) continue;
29740
29741 if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
29742 continue;
29743 }
29744
29745 switch(fullTrace.visible) {
29746 case 'legendonly':
29747 setVisibility(fullData[i], true);
29748 break;
29749 case true:
29750 otherState = isIsolated ? true : 'legendonly';
29751 isClicked = fullData[i] === fullTrace;
29752 // N.B. consider traces that have a set legendgroup as toggleable
29753 notInLegend = (fullData[i].showlegend !== true && !fullData[i].legendgroup);
29754 isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup);
29755 setVisibility(fullData[i], (isInGroup || notInLegend) ? true : otherState);
29756 break;
29757 }
29758 }
29759 }
29760
29761 for(i = 0; i < carrs.length; i++) {
29762 kcont = carrs[i];
29763 if(!kcont) continue;
29764 var update = kcont.constructUpdate();
29765
29766 var updateKeys = Object.keys(update);
29767 for(j = 0; j < updateKeys.length; j++) {
29768 key = updateKeys[j];
29769 val = attrUpdate[key] = attrUpdate[key] || [];
29770 val[carrIdx[i]] = update[key];
29771 }
29772 }
29773
29774 // The length of the value arrays should be equal and any unspecified
29775 // values should be explicitly undefined for them to get properly culled
29776 // as updates and not accidentally reset to the default value. This fills
29777 // out sparse arrays with the required number of undefined values:
29778 keys = Object.keys(attrUpdate);
29779 for(i = 0; i < keys.length; i++) {
29780 key = keys[i];
29781 for(j = 0; j < attrIndices.length; j++) {
29782 // Use hasOwnPropety to protect against falsey values:
29783 if(!attrUpdate[key].hasOwnProperty(j)) {
29784 attrUpdate[key][j] = undefined;
29785 }
29786 }
29787 }
29788
29789 Registry.call('_guiRestyle', gd, attrUpdate, attrIndices);
29790 }
29791};
29792
29793},{"../../lib":177,"../../registry":272}],106:[function(_dereq_,module,exports){
29794/**
29795* Copyright 2012-2020, Plotly, Inc.
29796* All rights reserved.
29797*
29798* This source code is licensed under the MIT license found in the
29799* LICENSE file in the root directory of this source tree.
29800*/
29801
29802
29803'use strict';
29804
29805exports.isGrouped = function isGrouped(legendLayout) {
29806 return (legendLayout.traceorder || '').indexOf('grouped') !== -1;
29807};
29808
29809exports.isVertical = function isVertical(legendLayout) {
29810 return legendLayout.orientation !== 'h';
29811};
29812
29813exports.isReversed = function isReversed(legendLayout) {
29814 return (legendLayout.traceorder || '').indexOf('reversed') !== -1;
29815};
29816
29817},{}],107:[function(_dereq_,module,exports){
29818/**
29819* Copyright 2012-2020, Plotly, Inc.
29820* All rights reserved.
29821*
29822* This source code is licensed under the MIT license found in the
29823* LICENSE file in the root directory of this source tree.
29824*/
29825
29826
29827'use strict';
29828
29829
29830module.exports = {
29831 moduleType: 'component',
29832 name: 'legend',
29833
29834 layoutAttributes: _dereq_('./attributes'),
29835 supplyLayoutDefaults: _dereq_('./defaults'),
29836
29837 draw: _dereq_('./draw'),
29838 style: _dereq_('./style')
29839};
29840
29841},{"./attributes":100,"./defaults":102,"./draw":103,"./style":108}],108:[function(_dereq_,module,exports){
29842/**
29843* Copyright 2012-2020, Plotly, Inc.
29844* All rights reserved.
29845*
29846* This source code is licensed under the MIT license found in the
29847* LICENSE file in the root directory of this source tree.
29848*/
29849
29850'use strict';
29851
29852var d3 = _dereq_('d3');
29853
29854var Registry = _dereq_('../../registry');
29855var Lib = _dereq_('../../lib');
29856var Drawing = _dereq_('../drawing');
29857var Color = _dereq_('../color');
29858var extractOpts = _dereq_('../colorscale/helpers').extractOpts;
29859
29860var subTypes = _dereq_('../../traces/scatter/subtypes');
29861var stylePie = _dereq_('../../traces/pie/style_one');
29862var pieCastOption = _dereq_('../../traces/pie/helpers').castOption;
29863
29864var CST_MARKER_SIZE = 12;
29865var CST_LINE_WIDTH = 5;
29866var CST_MARKER_LINE_WIDTH = 2;
29867var MAX_LINE_WIDTH = 10;
29868var MAX_MARKER_LINE_WIDTH = 5;
29869
29870module.exports = function style(s, gd, legend) {
29871 var fullLayout = gd._fullLayout;
29872 if(!legend) legend = fullLayout.legend;
29873 var constantItemSizing = legend.itemsizing === 'constant';
29874
29875 var boundLineWidth = function(mlw, cont, max, cst) {
29876 var v;
29877 if(mlw + 1) {
29878 v = mlw;
29879 } else if(cont && cont.width > 0) {
29880 v = cont.width;
29881 } else {
29882 return 0;
29883 }
29884 return constantItemSizing ? cst : Math.min(v, max);
29885 };
29886
29887 s.each(function(d) {
29888 var traceGroup = d3.select(this);
29889
29890 var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
29891 layers.style('opacity', d[0].trace.opacity);
29892
29893 var valign = legend.valign;
29894 var lineHeight = d[0].lineHeight;
29895 var height = d[0].height;
29896
29897 if(valign === 'middle' || !lineHeight || !height) {
29898 layers.attr('transform', null);
29899 } else {
29900 var factor = {top: 1, bottom: -1}[valign];
29901 var markerOffsetY = factor * (0.5 * (lineHeight - height + 3));
29902 layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
29903 }
29904
29905 var fill = layers
29906 .selectAll('g.legendfill')
29907 .data([d]);
29908 fill.enter().append('g')
29909 .classed('legendfill', true);
29910
29911 var line = layers
29912 .selectAll('g.legendlines')
29913 .data([d]);
29914 line.enter().append('g')
29915 .classed('legendlines', true);
29916
29917 var symbol = layers
29918 .selectAll('g.legendsymbols')
29919 .data([d]);
29920 symbol.enter().append('g')
29921 .classed('legendsymbols', true);
29922
29923 symbol.selectAll('g.legendpoints')
29924 .data([d])
29925 .enter().append('g')
29926 .classed('legendpoints', true);
29927 })
29928 .each(styleSpatial)
29929 .each(styleWaterfalls)
29930 .each(styleFunnels)
29931 .each(styleBars)
29932 .each(styleBoxes)
29933 .each(styleFunnelareas)
29934 .each(stylePies)
29935 .each(styleLines)
29936 .each(stylePoints)
29937 .each(styleCandles)
29938 .each(styleOHLC);
29939
29940 function styleLines(d) {
29941 var d0 = d[0];
29942 var trace = d0.trace;
29943 var showFill = trace.visible && trace.fill && trace.fill !== 'none';
29944 var showLine = subTypes.hasLines(trace);
29945 var contours = trace.contours;
29946 var showGradientLine = false;
29947 var showGradientFill = false;
29948 var dMod, tMod;
29949
29950 var cOpts = extractOpts(trace);
29951 var colorscale = cOpts.colorscale;
29952 var reversescale = cOpts.reversescale;
29953
29954 var fillGradient = function(s) {
29955 if(s.size()) {
29956 var gradientID = 'legendfill-' + trace.uid;
29957 Drawing.gradient(s, gd, gradientID,
29958 getGradientDirection(reversescale),
29959 colorscale, 'fill');
29960 }
29961 };
29962
29963 var lineGradient = function(s) {
29964 if(s.size()) {
29965 var gradientID = 'legendline-' + trace.uid;
29966 Drawing.lineGroupStyle(s);
29967 Drawing.gradient(s, gd, gradientID,
29968 getGradientDirection(reversescale),
29969 colorscale, 'stroke');
29970 }
29971 };
29972
29973 if(contours) {
29974 var coloring = contours.coloring;
29975
29976 if(coloring === 'lines') {
29977 showGradientLine = true;
29978 } else {
29979 showLine = coloring === 'none' || coloring === 'heatmap' || contours.showlines;
29980 }
29981
29982 if(contours.type === 'constraint') {
29983 showFill = contours._operation !== '=';
29984 } else if(coloring === 'fill' || coloring === 'heatmap') {
29985 showGradientFill = true;
29986 }
29987 }
29988
29989 // with fill and no markers or text, move the line and fill up a bit
29990 // so it's more centered
29991 var markersOrText = subTypes.hasMarkers(trace) || subTypes.hasText(trace);
29992 var anyFill = showFill || showGradientFill;
29993 var anyLine = showLine || showGradientLine;
29994 var pathStart = (markersOrText || !anyFill) ? 'M5,0' :
29995 // with a line leave it slightly below center, to leave room for the
29996 // line thickness and because the line is usually more prominent
29997 anyLine ? 'M5,-2' : 'M5,-3';
29998
29999 var this3 = d3.select(this);
30000
30001 var fill = this3.select('.legendfill').selectAll('path')
30002 .data(showFill || showGradientFill ? [d] : []);
30003 fill.enter().append('path').classed('js-fill', true);
30004 fill.exit().remove();
30005 fill.attr('d', pathStart + 'h30v6h-30z')
30006 .call(showFill ? Drawing.fillGroupStyle : fillGradient);
30007
30008 if(showLine || showGradientLine) {
30009 var lw = boundLineWidth(undefined, trace.line, MAX_LINE_WIDTH, CST_LINE_WIDTH);
30010 tMod = Lib.minExtend(trace, {line: {width: lw}});
30011 dMod = [Lib.minExtend(d0, {trace: tMod})];
30012 }
30013
30014 var line = this3.select('.legendlines').selectAll('path')
30015 .data(showLine || showGradientLine ? [dMod] : []);
30016 line.enter().append('path').classed('js-line', true);
30017 line.exit().remove();
30018
30019 // this is ugly... but you can't apply a gradient to a perfectly
30020 // horizontal or vertical line. Presumably because then
30021 // the system doesn't know how to scale vertical variation, even
30022 // though there *is* no vertical variation in this case.
30023 // so add an invisibly small angle to the line
30024 // This issue (and workaround) exist across (Mac) Chrome, FF, and Safari
30025 line.attr('d', pathStart + (showGradientLine ? 'l30,0.0001' : 'h30'))
30026 .call(showLine ? Drawing.lineGroupStyle : lineGradient);
30027 }
30028
30029 function stylePoints(d) {
30030 var d0 = d[0];
30031 var trace = d0.trace;
30032 var showMarkers = subTypes.hasMarkers(trace);
30033 var showText = subTypes.hasText(trace);
30034 var showLines = subTypes.hasLines(trace);
30035 var dMod, tMod;
30036
30037 // 'scatter3d' don't use gd.calcdata,
30038 // use d0.trace to infer arrayOk attributes
30039
30040 function boundVal(attrIn, arrayToValFn, bounds, cst) {
30041 var valIn = Lib.nestedProperty(trace, attrIn).get();
30042 var valToBound = (Lib.isArrayOrTypedArray(valIn) && arrayToValFn) ?
30043 arrayToValFn(valIn) :
30044 valIn;
30045
30046 if(constantItemSizing && valToBound && cst !== undefined) {
30047 valToBound = cst;
30048 }
30049
30050 if(bounds) {
30051 if(valToBound < bounds[0]) return bounds[0];
30052 else if(valToBound > bounds[1]) return bounds[1];
30053 }
30054 return valToBound;
30055 }
30056
30057 function pickFirst(array) {
30058 if(d0._distinct && d0.index && array[d0.index]) return array[d0.index];
30059 return array[0];
30060 }
30061
30062 // constrain text, markers, etc so they'll fit on the legend
30063 if(showMarkers || showText || showLines) {
30064 var dEdit = {};
30065 var tEdit = {};
30066
30067 if(showMarkers) {
30068 dEdit.mc = boundVal('marker.color', pickFirst);
30069 dEdit.mx = boundVal('marker.symbol', pickFirst);
30070 dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
30071 dEdit.mlc = boundVal('marker.line.color', pickFirst);
30072 dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5], CST_MARKER_LINE_WIDTH);
30073 tEdit.marker = {
30074 sizeref: 1,
30075 sizemin: 1,
30076 sizemode: 'diameter'
30077 };
30078
30079 var ms = boundVal('marker.size', Lib.mean, [2, 16], CST_MARKER_SIZE);
30080 dEdit.ms = ms;
30081 tEdit.marker.size = ms;
30082 }
30083
30084 if(showLines) {
30085 tEdit.line = {
30086 width: boundVal('line.width', pickFirst, [0, 10], CST_LINE_WIDTH)
30087 };
30088 }
30089
30090 if(showText) {
30091 dEdit.tx = 'Aa';
30092 dEdit.tp = boundVal('textposition', pickFirst);
30093 dEdit.ts = 10;
30094 dEdit.tc = boundVal('textfont.color', pickFirst);
30095 dEdit.tf = boundVal('textfont.family', pickFirst);
30096 }
30097
30098 dMod = [Lib.minExtend(d0, dEdit)];
30099 tMod = Lib.minExtend(trace, tEdit);
30100
30101 // always show legend items in base state
30102 tMod.selectedpoints = null;
30103
30104 // never show texttemplate
30105 tMod.texttemplate = null;
30106 }
30107
30108 var ptgroup = d3.select(this).select('g.legendpoints');
30109
30110 var pts = ptgroup.selectAll('path.scatterpts')
30111 .data(showMarkers ? dMod : []);
30112 // make sure marker is on the bottom, in case it enters after text
30113 pts.enter().insert('path', ':first-child')
30114 .classed('scatterpts', true)
30115 .attr('transform', 'translate(20,0)');
30116 pts.exit().remove();
30117 pts.call(Drawing.pointStyle, tMod, gd);
30118
30119 // 'mrc' is set in pointStyle and used in textPointStyle:
30120 // constrain it here
30121 if(showMarkers) dMod[0].mrc = 3;
30122
30123 var txt = ptgroup.selectAll('g.pointtext')
30124 .data(showText ? dMod : []);
30125 txt.enter()
30126 .append('g').classed('pointtext', true)
30127 .append('text').attr('transform', 'translate(20,0)');
30128 txt.exit().remove();
30129 txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
30130 }
30131
30132 function styleWaterfalls(d) {
30133 var trace = d[0].trace;
30134 var isWaterfall = trace.type === 'waterfall';
30135
30136 if(d[0]._distinct && isWaterfall) {
30137 var cont = d[0].trace[d[0].dir].marker;
30138 d[0].mc = cont.color;
30139 d[0].mlw = cont.line.width;
30140 d[0].mlc = cont.line.color;
30141 return styleBarLike(d, this, 'waterfall');
30142 }
30143
30144 var ptsData = [];
30145 if(trace.visible && isWaterfall) {
30146 ptsData = d[0].hasTotals ?
30147 [['increasing', 'M-6,-6V6H0Z'], ['totals', 'M6,6H0L-6,-6H-0Z'], ['decreasing', 'M6,6V-6H0Z']] :
30148 [['increasing', 'M-6,-6V6H6Z'], ['decreasing', 'M6,6V-6H-6Z']];
30149 }
30150
30151 var pts = d3.select(this).select('g.legendpoints')
30152 .selectAll('path.legendwaterfall')
30153 .data(ptsData);
30154 pts.enter().append('path').classed('legendwaterfall', true)
30155 .attr('transform', 'translate(20,0)')
30156 .style('stroke-miterlimit', 1);
30157 pts.exit().remove();
30158
30159 pts.each(function(dd) {
30160 var pt = d3.select(this);
30161 var cont = trace[dd[0]].marker;
30162 var lw = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30163
30164 pt.attr('d', dd[1])
30165 .style('stroke-width', lw + 'px')
30166 .call(Color.fill, cont.color);
30167
30168 if(lw) {
30169 pt.call(Color.stroke, cont.line.color);
30170 }
30171 });
30172 }
30173
30174 function styleBars(d) {
30175 styleBarLike(d, this);
30176 }
30177
30178 function styleFunnels(d) {
30179 styleBarLike(d, this, 'funnel');
30180 }
30181
30182 function styleBarLike(d, lThis, desiredType) {
30183 var trace = d[0].trace;
30184 var marker = trace.marker || {};
30185 var markerLine = marker.line || {};
30186
30187 var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') :
30188 (trace.visible && trace.type === desiredType);
30189
30190 var barpath = d3.select(lThis).select('g.legendpoints')
30191 .selectAll('path.legend' + desiredType)
30192 .data(isVisible ? [d] : []);
30193 barpath.enter().append('path').classed('legend' + desiredType, true)
30194 .attr('d', 'M6,6H-6V-6H6Z')
30195 .attr('transform', 'translate(20,0)');
30196 barpath.exit().remove();
30197
30198 barpath.each(function(d) {
30199 var p = d3.select(this);
30200 var d0 = d[0];
30201 var w = boundLineWidth(d0.mlw, marker.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30202
30203 p.style('stroke-width', w + 'px')
30204 .call(Color.fill, d0.mc || marker.color);
30205
30206 if(w) Color.stroke(p, d0.mlc || markerLine.color);
30207 });
30208 }
30209
30210 function styleBoxes(d) {
30211 var trace = d[0].trace;
30212
30213 var pts = d3.select(this).select('g.legendpoints')
30214 .selectAll('path.legendbox')
30215 .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []);
30216 pts.enter().append('path').classed('legendbox', true)
30217 // if we want the median bar, prepend M6,0H-6
30218 .attr('d', 'M6,6H-6V-6H6Z')
30219 .attr('transform', 'translate(20,0)');
30220 pts.exit().remove();
30221
30222 pts.each(function() {
30223 var p = d3.select(this);
30224
30225 if((trace.boxpoints === 'all' || trace.points === 'all') &&
30226 Color.opacity(trace.fillcolor) === 0 && Color.opacity((trace.line || {}).color) === 0
30227 ) {
30228 var tMod = Lib.minExtend(trace, {
30229 marker: {
30230 size: constantItemSizing ? CST_MARKER_SIZE : Lib.constrain(trace.marker.size, 2, 16),
30231 sizeref: 1,
30232 sizemin: 1,
30233 sizemode: 'diameter'
30234 }
30235 });
30236 pts.call(Drawing.pointStyle, tMod, gd);
30237 } else {
30238 var w = boundLineWidth(undefined, trace.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30239
30240 p.style('stroke-width', w + 'px')
30241 .call(Color.fill, trace.fillcolor);
30242
30243 if(w) Color.stroke(p, trace.line.color);
30244 }
30245 });
30246 }
30247
30248 function styleCandles(d) {
30249 var trace = d[0].trace;
30250
30251 var pts = d3.select(this).select('g.legendpoints')
30252 .selectAll('path.legendcandle')
30253 .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []);
30254 pts.enter().append('path').classed('legendcandle', true)
30255 .attr('d', function(_, i) {
30256 if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
30257 return 'M15,0H8M8,-6V6H-8Z'; // decreasing
30258 })
30259 .attr('transform', 'translate(20,0)')
30260 .style('stroke-miterlimit', 1);
30261 pts.exit().remove();
30262
30263 pts.each(function(_, i) {
30264 var p = d3.select(this);
30265 var cont = trace[i ? 'increasing' : 'decreasing'];
30266 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30267
30268 p.style('stroke-width', w + 'px')
30269 .call(Color.fill, cont.fillcolor);
30270
30271 if(w) Color.stroke(p, cont.line.color);
30272 });
30273 }
30274
30275 function styleOHLC(d) {
30276 var trace = d[0].trace;
30277
30278 var pts = d3.select(this).select('g.legendpoints')
30279 .selectAll('path.legendohlc')
30280 .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []);
30281 pts.enter().append('path').classed('legendohlc', true)
30282 .attr('d', function(_, i) {
30283 if(i) return 'M-15,0H0M-8,-6V0'; // increasing
30284 return 'M15,0H0M8,6V0'; // decreasing
30285 })
30286 .attr('transform', 'translate(20,0)')
30287 .style('stroke-miterlimit', 1);
30288 pts.exit().remove();
30289
30290 pts.each(function(_, i) {
30291 var p = d3.select(this);
30292 var cont = trace[i ? 'increasing' : 'decreasing'];
30293 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30294
30295 p.style('fill', 'none')
30296 .call(Drawing.dashLine, cont.line.dash, w);
30297
30298 if(w) Color.stroke(p, cont.line.color);
30299 });
30300 }
30301
30302 function stylePies(d) {
30303 stylePieLike(d, this, 'pie');
30304 }
30305
30306 function styleFunnelareas(d) {
30307 stylePieLike(d, this, 'funnelarea');
30308 }
30309
30310 function stylePieLike(d, lThis, desiredType) {
30311 var d0 = d[0];
30312 var trace = d0.trace;
30313
30314 var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) :
30315 (trace.visible && trace.type === desiredType);
30316
30317 var pts = d3.select(lThis).select('g.legendpoints')
30318 .selectAll('path.legend' + desiredType)
30319 .data(isVisible ? [d] : []);
30320 pts.enter().append('path').classed('legend' + desiredType, true)
30321 .attr('d', 'M6,6H-6V-6H6Z')
30322 .attr('transform', 'translate(20,0)');
30323 pts.exit().remove();
30324
30325 if(pts.size()) {
30326 var cont = (trace.marker || {}).line;
30327 var lw = boundLineWidth(pieCastOption(cont.width, d0.pts), cont, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
30328
30329 var tMod = Lib.minExtend(trace, {marker: {line: {width: lw}}});
30330 // since minExtend do not slice more than 3 items we need to patch line.color here
30331 tMod.marker.line.color = cont.color;
30332
30333 var d0Mod = Lib.minExtend(d0, {trace: tMod});
30334
30335 stylePie(pts, d0Mod, tMod);
30336 }
30337 }
30338
30339 function styleSpatial(d) { // i.e. maninly traces having z and colorscale
30340 var trace = d[0].trace;
30341
30342 var useGradient;
30343 var ptsData = [];
30344 if(trace.visible) {
30345 switch(trace.type) {
30346 case 'histogram2d' :
30347 case 'heatmap' :
30348 ptsData = [
30349 ['M-15,-2V4H15V-2Z'] // similar to contour
30350 ];
30351 useGradient = true;
30352 break;
30353 case 'choropleth' :
30354 case 'choroplethmapbox' :
30355 ptsData = [
30356 ['M-6,-6V6H6V-6Z']
30357 ];
30358 useGradient = true;
30359 break;
30360 case 'densitymapbox' :
30361 ptsData = [
30362 ['M-6,0 a6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0']
30363 ];
30364 useGradient = 'radial';
30365 break;
30366 case 'cone' :
30367 ptsData = [
30368 ['M-6,2 A2,2 0 0,0 -6,6 V6L6,4Z'],
30369 ['M-6,-6 A2,2 0 0,0 -6,-2 L6,-4Z'],
30370 ['M-6,-2 A2,2 0 0,0 -6,2 L6,0Z']
30371 ];
30372 useGradient = false;
30373 break;
30374 case 'streamtube' :
30375 ptsData = [
30376 ['M-6,2 A2,2 0 0,0 -6,6 H6 A2,2 0 0,1 6,2 Z'],
30377 ['M-6,-6 A2,2 0 0,0 -6,-2 H6 A2,2 0 0,1 6,-6 Z'],
30378 ['M-6,-2 A2,2 0 0,0 -6,2 H6 A2,2 0 0,1 6,-2 Z']
30379 ];
30380 useGradient = false;
30381 break;
30382 case 'surface' :
30383 ptsData = [
30384 ['M-6,-6 A2,3 0 0,0 -6,0 H6 A2,3 0 0,1 6,-6 Z'],
30385 ['M-6,1 A2,3 0 0,1 -6,6 H6 A2,3 0 0,0 6,0 Z']
30386 ];
30387 useGradient = true;
30388 break;
30389 case 'mesh3d' :
30390 ptsData = [
30391 ['M-6,6H0L-6,-6Z'],
30392 ['M6,6H0L6,-6Z'],
30393 ['M-6,-6H6L0,6Z']
30394 ];
30395 useGradient = false;
30396 break;
30397 case 'volume' :
30398 ptsData = [
30399 ['M-6,6H0L-6,-6Z'],
30400 ['M6,6H0L6,-6Z'],
30401 ['M-6,-6H6L0,6Z']
30402 ];
30403 useGradient = true;
30404 break;
30405 case 'isosurface':
30406 ptsData = [
30407 ['M-6,6H0L-6,-6Z'],
30408 ['M6,6H0L6,-6Z'],
30409 ['M-6,-6 A12,24 0 0,0 6,-6 L0,6Z']
30410 ];
30411 useGradient = false;
30412 break;
30413 }
30414 }
30415
30416 var pts = d3.select(this).select('g.legendpoints')
30417 .selectAll('path.legend3dandfriends')
30418 .data(ptsData);
30419 pts.enter().append('path').classed('legend3dandfriends', true)
30420 .attr('transform', 'translate(20,0)')
30421 .style('stroke-miterlimit', 1);
30422 pts.exit().remove();
30423
30424 pts.each(function(dd, i) {
30425 var pt = d3.select(this);
30426
30427 var cOpts = extractOpts(trace);
30428 var colorscale = cOpts.colorscale;
30429 var reversescale = cOpts.reversescale;
30430 var fillGradient = function(s) {
30431 if(s.size()) {
30432 var gradientID = 'legendfill-' + trace.uid;
30433 Drawing.gradient(s, gd, gradientID,
30434 getGradientDirection(reversescale, useGradient === 'radial'),
30435 colorscale, 'fill');
30436 }
30437 };
30438
30439 var fillColor;
30440 if(!colorscale) {
30441 var color = trace.vertexcolor || trace.facecolor || trace.color;
30442 fillColor = Lib.isArrayOrTypedArray(color) ? (color[i] || color[0]) : color;
30443 } else {
30444 if(!useGradient) {
30445 var len = colorscale.length;
30446 fillColor =
30447 i === 0 ? colorscale[reversescale ? len - 1 : 0][1] : // minimum
30448 i === 1 ? colorscale[reversescale ? 0 : len - 1][1] : // maximum
30449 colorscale[Math.floor((len - 1) / 2)][1]; // middle
30450 }
30451 }
30452
30453 pt.attr('d', dd[0]);
30454 if(fillColor) {
30455 pt.call(Color.fill, fillColor);
30456 } else {
30457 pt.call(fillGradient);
30458 }
30459 });
30460 }
30461};
30462
30463function getGradientDirection(reversescale, isRadial) {
30464 var str = isRadial ? 'radial' : 'horizontal';
30465 return str + (reversescale ? '' : 'reversed');
30466}
30467
30468},{"../../lib":177,"../../registry":272,"../../traces/pie/helpers":291,"../../traces/pie/style_one":292,"../../traces/scatter/subtypes":318,"../color":50,"../colorscale/helpers":61,"../drawing":72,"d3":13}],109:[function(_dereq_,module,exports){
30469/**
30470* Copyright 2012-2020, Plotly, Inc.
30471* All rights reserved.
30472*
30473* This source code is licensed under the MIT license found in the
30474* LICENSE file in the root directory of this source tree.
30475*/
30476
30477'use strict';
30478
30479var Registry = _dereq_('../../registry');
30480var Plots = _dereq_('../../plots/plots');
30481var axisIds = _dereq_('../../plots/cartesian/axis_ids');
30482var Icons = _dereq_('../../fonts/ploticon');
30483var eraseActiveShape = _dereq_('../shapes/draw').eraseActiveShape;
30484var Lib = _dereq_('../../lib');
30485var _ = Lib._;
30486
30487var modeBarButtons = module.exports = {};
30488
30489/**
30490 * ModeBar buttons configuration
30491 *
30492 * @param {string} name
30493 * name / id of the buttons (for tracking)
30494 * @param {string} title
30495 * text that appears while hovering over the button,
30496 * enter null, false or '' for no hover text
30497 * @param {string} icon
30498 * svg icon object associated with the button
30499 * can be linked to Plotly.Icons to use the default plotly icons
30500 * @param {string} [gravity]
30501 * icon positioning
30502 * @param {function} click
30503 * click handler associated with the button, a function of
30504 * 'gd' (the main graph object) and
30505 * 'ev' (the event object)
30506 * @param {string} [attr]
30507 * attribute associated with button,
30508 * use this with 'val' to keep track of the state
30509 * @param {*} [val]
30510 * initial 'attr' value, can be a function of gd
30511 * @param {boolean} [toggle]
30512 * is the button a toggle button?
30513 */
30514modeBarButtons.toImage = {
30515 name: 'toImage',
30516 title: function(gd) {
30517 var opts = gd._context.toImageButtonOptions || {};
30518 var format = opts.format || 'png';
30519 return format === 'png' ?
30520 _(gd, 'Download plot as a png') : // legacy text
30521 _(gd, 'Download plot'); // generic non-PNG text
30522 },
30523 icon: Icons.camera,
30524 click: function(gd) {
30525 var toImageButtonOptions = gd._context.toImageButtonOptions;
30526 var opts = {format: toImageButtonOptions.format || 'png'};
30527
30528 Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long');
30529
30530 if(opts.format !== 'svg' && Lib.isIE()) {
30531 Lib.notifier(_(gd, 'IE only supports svg. Changing format to svg.'), 'long');
30532 opts.format = 'svg';
30533 }
30534
30535 ['filename', 'width', 'height', 'scale'].forEach(function(key) {
30536 if(key in toImageButtonOptions) {
30537 opts[key] = toImageButtonOptions[key];
30538 }
30539 });
30540
30541 Registry.call('downloadImage', gd, opts)
30542 .then(function(filename) {
30543 Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long');
30544 })
30545 .catch(function() {
30546 Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long');
30547 });
30548 }
30549};
30550
30551modeBarButtons.sendDataToCloud = {
30552 name: 'sendDataToCloud',
30553 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
30554 icon: Icons.disk,
30555 click: function(gd) {
30556 Plots.sendDataToCloud(gd);
30557 }
30558};
30559
30560modeBarButtons.editInChartStudio = {
30561 name: 'editInChartStudio',
30562 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
30563 icon: Icons.pencil,
30564 click: function(gd) {
30565 Plots.sendDataToCloud(gd);
30566 }
30567};
30568
30569modeBarButtons.zoom2d = {
30570 name: 'zoom2d',
30571 title: function(gd) { return _(gd, 'Zoom'); },
30572 attr: 'dragmode',
30573 val: 'zoom',
30574 icon: Icons.zoombox,
30575 click: handleCartesian
30576};
30577
30578modeBarButtons.pan2d = {
30579 name: 'pan2d',
30580 title: function(gd) { return _(gd, 'Pan'); },
30581 attr: 'dragmode',
30582 val: 'pan',
30583 icon: Icons.pan,
30584 click: handleCartesian
30585};
30586
30587modeBarButtons.select2d = {
30588 name: 'select2d',
30589 title: function(gd) { return _(gd, 'Box Select'); },
30590 attr: 'dragmode',
30591 val: 'select',
30592 icon: Icons.selectbox,
30593 click: handleCartesian
30594};
30595
30596modeBarButtons.lasso2d = {
30597 name: 'lasso2d',
30598 title: function(gd) { return _(gd, 'Lasso Select'); },
30599 attr: 'dragmode',
30600 val: 'lasso',
30601 icon: Icons.lasso,
30602 click: handleCartesian
30603};
30604
30605modeBarButtons.drawclosedpath = {
30606 name: 'drawclosedpath',
30607 title: function(gd) { return _(gd, 'Draw closed freeform'); },
30608 attr: 'dragmode',
30609 val: 'drawclosedpath',
30610 icon: Icons.drawclosedpath,
30611 click: handleCartesian
30612};
30613
30614modeBarButtons.drawopenpath = {
30615 name: 'drawopenpath',
30616 title: function(gd) { return _(gd, 'Draw open freeform'); },
30617 attr: 'dragmode',
30618 val: 'drawopenpath',
30619 icon: Icons.drawopenpath,
30620 click: handleCartesian
30621};
30622
30623modeBarButtons.drawline = {
30624 name: 'drawline',
30625 title: function(gd) { return _(gd, 'Draw line'); },
30626 attr: 'dragmode',
30627 val: 'drawline',
30628 icon: Icons.drawline,
30629 click: handleCartesian
30630};
30631
30632modeBarButtons.drawrect = {
30633 name: 'drawrect',
30634 title: function(gd) { return _(gd, 'Draw rectangle'); },
30635 attr: 'dragmode',
30636 val: 'drawrect',
30637 icon: Icons.drawrect,
30638 click: handleCartesian
30639};
30640
30641modeBarButtons.drawcircle = {
30642 name: 'drawcircle',
30643 title: function(gd) { return _(gd, 'Draw circle'); },
30644 attr: 'dragmode',
30645 val: 'drawcircle',
30646 icon: Icons.drawcircle,
30647 click: handleCartesian
30648};
30649
30650modeBarButtons.eraseshape = {
30651 name: 'eraseshape',
30652 title: function(gd) { return _(gd, 'Erase active shape'); },
30653 icon: Icons.eraseshape,
30654 click: eraseActiveShape
30655};
30656
30657modeBarButtons.zoomIn2d = {
30658 name: 'zoomIn2d',
30659 title: function(gd) { return _(gd, 'Zoom in'); },
30660 attr: 'zoom',
30661 val: 'in',
30662 icon: Icons.zoom_plus,
30663 click: handleCartesian
30664};
30665
30666modeBarButtons.zoomOut2d = {
30667 name: 'zoomOut2d',
30668 title: function(gd) { return _(gd, 'Zoom out'); },
30669 attr: 'zoom',
30670 val: 'out',
30671 icon: Icons.zoom_minus,
30672 click: handleCartesian
30673};
30674
30675modeBarButtons.autoScale2d = {
30676 name: 'autoScale2d',
30677 title: function(gd) { return _(gd, 'Autoscale'); },
30678 attr: 'zoom',
30679 val: 'auto',
30680 icon: Icons.autoscale,
30681 click: handleCartesian
30682};
30683
30684modeBarButtons.resetScale2d = {
30685 name: 'resetScale2d',
30686 title: function(gd) { return _(gd, 'Reset axes'); },
30687 attr: 'zoom',
30688 val: 'reset',
30689 icon: Icons.home,
30690 click: handleCartesian
30691};
30692
30693modeBarButtons.hoverClosestCartesian = {
30694 name: 'hoverClosestCartesian',
30695 title: function(gd) { return _(gd, 'Show closest data on hover'); },
30696 attr: 'hovermode',
30697 val: 'closest',
30698 icon: Icons.tooltip_basic,
30699 gravity: 'ne',
30700 click: handleCartesian
30701};
30702
30703modeBarButtons.hoverCompareCartesian = {
30704 name: 'hoverCompareCartesian',
30705 title: function(gd) { return _(gd, 'Compare data on hover'); },
30706 attr: 'hovermode',
30707 val: function(gd) {
30708 return gd._fullLayout._isHoriz ? 'y' : 'x';
30709 },
30710 icon: Icons.tooltip_compare,
30711 gravity: 'ne',
30712 click: handleCartesian
30713};
30714
30715function handleCartesian(gd, ev) {
30716 var button = ev.currentTarget;
30717 var astr = button.getAttribute('data-attr');
30718 var val = button.getAttribute('data-val') || true;
30719 var fullLayout = gd._fullLayout;
30720 var aobj = {};
30721 var axList = axisIds.list(gd, null, true);
30722 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
30723
30724 var ax, i;
30725
30726 if(astr === 'zoom') {
30727 var mag = (val === 'in') ? 0.5 : 2;
30728 var r0 = (1 + mag) / 2;
30729 var r1 = (1 - mag) / 2;
30730 var axName;
30731
30732 for(i = 0; i < axList.length; i++) {
30733 ax = axList[i];
30734
30735 if(!ax.fixedrange) {
30736 axName = ax._name;
30737 if(val === 'auto') {
30738 aobj[axName + '.autorange'] = true;
30739 } else if(val === 'reset') {
30740 if(ax._rangeInitial === undefined) {
30741 aobj[axName + '.autorange'] = true;
30742 } else {
30743 var rangeInitial = ax._rangeInitial.slice();
30744 aobj[axName + '.range[0]'] = rangeInitial[0];
30745 aobj[axName + '.range[1]'] = rangeInitial[1];
30746 }
30747
30748 // N.B. "reset" also resets showspikes
30749 if(ax._showSpikeInitial !== undefined) {
30750 aobj[axName + '.showspikes'] = ax._showSpikeInitial;
30751 if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
30752 allSpikesEnabled = 'off';
30753 }
30754 }
30755 } else {
30756 var rangeNow = [
30757 ax.r2l(ax.range[0]),
30758 ax.r2l(ax.range[1]),
30759 ];
30760
30761 var rangeNew = [
30762 r0 * rangeNow[0] + r1 * rangeNow[1],
30763 r0 * rangeNow[1] + r1 * rangeNow[0]
30764 ];
30765
30766 aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]);
30767 aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]);
30768 }
30769 }
30770 }
30771 } else {
30772 // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
30773 if(astr === 'hovermode' && (val === 'x' || val === 'y')) {
30774 val = fullLayout._isHoriz ? 'y' : 'x';
30775 button.setAttribute('data-val', val);
30776 }
30777
30778 aobj[astr] = val;
30779 }
30780
30781 fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
30782
30783 Registry.call('_guiRelayout', gd, aobj);
30784}
30785
30786modeBarButtons.zoom3d = {
30787 name: 'zoom3d',
30788 title: function(gd) { return _(gd, 'Zoom'); },
30789 attr: 'scene.dragmode',
30790 val: 'zoom',
30791 icon: Icons.zoombox,
30792 click: handleDrag3d
30793};
30794
30795modeBarButtons.pan3d = {
30796 name: 'pan3d',
30797 title: function(gd) { return _(gd, 'Pan'); },
30798 attr: 'scene.dragmode',
30799 val: 'pan',
30800 icon: Icons.pan,
30801 click: handleDrag3d
30802};
30803
30804modeBarButtons.orbitRotation = {
30805 name: 'orbitRotation',
30806 title: function(gd) { return _(gd, 'Orbital rotation'); },
30807 attr: 'scene.dragmode',
30808 val: 'orbit',
30809 icon: Icons['3d_rotate'],
30810 click: handleDrag3d
30811};
30812
30813modeBarButtons.tableRotation = {
30814 name: 'tableRotation',
30815 title: function(gd) { return _(gd, 'Turntable rotation'); },
30816 attr: 'scene.dragmode',
30817 val: 'turntable',
30818 icon: Icons['z-axis'],
30819 click: handleDrag3d
30820};
30821
30822function handleDrag3d(gd, ev) {
30823 var button = ev.currentTarget;
30824 var attr = button.getAttribute('data-attr');
30825 var val = button.getAttribute('data-val') || true;
30826 var sceneIds = gd._fullLayout._subplots.gl3d || [];
30827 var layoutUpdate = {};
30828
30829 var parts = attr.split('.');
30830
30831 for(var i = 0; i < sceneIds.length; i++) {
30832 layoutUpdate[sceneIds[i] + '.' + parts[1]] = val;
30833 }
30834
30835 // for multi-type subplots
30836 var val2d = (val === 'pan') ? val : 'zoom';
30837 layoutUpdate.dragmode = val2d;
30838
30839 Registry.call('_guiRelayout', gd, layoutUpdate);
30840}
30841
30842modeBarButtons.resetCameraDefault3d = {
30843 name: 'resetCameraDefault3d',
30844 title: function(gd) { return _(gd, 'Reset camera to default'); },
30845 attr: 'resetDefault',
30846 icon: Icons.home,
30847 click: handleCamera3d
30848};
30849
30850modeBarButtons.resetCameraLastSave3d = {
30851 name: 'resetCameraLastSave3d',
30852 title: function(gd) { return _(gd, 'Reset camera to last save'); },
30853 attr: 'resetLastSave',
30854 icon: Icons.movie,
30855 click: handleCamera3d
30856};
30857
30858function handleCamera3d(gd, ev) {
30859 var button = ev.currentTarget;
30860 var attr = button.getAttribute('data-attr');
30861 var fullLayout = gd._fullLayout;
30862 var sceneIds = fullLayout._subplots.gl3d || [];
30863 var aobj = {};
30864
30865 for(var i = 0; i < sceneIds.length; i++) {
30866 var sceneId = sceneIds[i];
30867 var camera = sceneId + '.camera';
30868 var aspectratio = sceneId + '.aspectratio';
30869 var aspectmode = sceneId + '.aspectmode';
30870 var scene = fullLayout[sceneId]._scene;
30871 var didUpdate;
30872
30873 if(attr === 'resetLastSave') {
30874 aobj[camera + '.up'] = scene.viewInitial.up;
30875 aobj[camera + '.eye'] = scene.viewInitial.eye;
30876 aobj[camera + '.center'] = scene.viewInitial.center;
30877 didUpdate = true;
30878 } else if(attr === 'resetDefault') {
30879 aobj[camera + '.up'] = null;
30880 aobj[camera + '.eye'] = null;
30881 aobj[camera + '.center'] = null;
30882 didUpdate = true;
30883 }
30884
30885 if(didUpdate) {
30886 aobj[aspectratio + '.x'] = scene.viewInitial.aspectratio.x;
30887 aobj[aspectratio + '.y'] = scene.viewInitial.aspectratio.y;
30888 aobj[aspectratio + '.z'] = scene.viewInitial.aspectratio.z;
30889 aobj[aspectmode] = scene.viewInitial.aspectmode;
30890 }
30891 }
30892
30893 Registry.call('_guiRelayout', gd, aobj);
30894}
30895
30896modeBarButtons.hoverClosest3d = {
30897 name: 'hoverClosest3d',
30898 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
30899 attr: 'hovermode',
30900 val: null,
30901 toggle: true,
30902 icon: Icons.tooltip_basic,
30903 gravity: 'ne',
30904 click: handleHover3d
30905};
30906
30907function getNextHover3d(gd, ev) {
30908 var button = ev.currentTarget;
30909 var val = button._previousVal;
30910 var fullLayout = gd._fullLayout;
30911 var sceneIds = fullLayout._subplots.gl3d || [];
30912
30913 var axes = ['xaxis', 'yaxis', 'zaxis'];
30914
30915 // initialize 'current spike' object to be stored in the DOM
30916 var currentSpikes = {};
30917 var layoutUpdate = {};
30918
30919 if(val) {
30920 layoutUpdate = val;
30921 button._previousVal = null;
30922 } else {
30923 for(var i = 0; i < sceneIds.length; i++) {
30924 var sceneId = sceneIds[i];
30925 var sceneLayout = fullLayout[sceneId];
30926
30927 var hovermodeAStr = sceneId + '.hovermode';
30928 currentSpikes[hovermodeAStr] = sceneLayout.hovermode;
30929 layoutUpdate[hovermodeAStr] = false;
30930
30931 // copy all the current spike attrs
30932 for(var j = 0; j < 3; j++) {
30933 var axis = axes[j];
30934 var spikeAStr = sceneId + '.' + axis + '.showspikes';
30935 layoutUpdate[spikeAStr] = false;
30936 currentSpikes[spikeAStr] = sceneLayout[axis].showspikes;
30937 }
30938 }
30939
30940 button._previousVal = currentSpikes;
30941 }
30942 return layoutUpdate;
30943}
30944
30945function handleHover3d(gd, ev) {
30946 var layoutUpdate = getNextHover3d(gd, ev);
30947 Registry.call('_guiRelayout', gd, layoutUpdate);
30948}
30949
30950modeBarButtons.zoomInGeo = {
30951 name: 'zoomInGeo',
30952 title: function(gd) { return _(gd, 'Zoom in'); },
30953 attr: 'zoom',
30954 val: 'in',
30955 icon: Icons.zoom_plus,
30956 click: handleGeo
30957};
30958
30959modeBarButtons.zoomOutGeo = {
30960 name: 'zoomOutGeo',
30961 title: function(gd) { return _(gd, 'Zoom out'); },
30962 attr: 'zoom',
30963 val: 'out',
30964 icon: Icons.zoom_minus,
30965 click: handleGeo
30966};
30967
30968modeBarButtons.resetGeo = {
30969 name: 'resetGeo',
30970 title: function(gd) { return _(gd, 'Reset'); },
30971 attr: 'reset',
30972 val: null,
30973 icon: Icons.autoscale,
30974 click: handleGeo
30975};
30976
30977modeBarButtons.hoverClosestGeo = {
30978 name: 'hoverClosestGeo',
30979 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
30980 attr: 'hovermode',
30981 val: null,
30982 toggle: true,
30983 icon: Icons.tooltip_basic,
30984 gravity: 'ne',
30985 click: toggleHover
30986};
30987
30988function handleGeo(gd, ev) {
30989 var button = ev.currentTarget;
30990 var attr = button.getAttribute('data-attr');
30991 var val = button.getAttribute('data-val') || true;
30992 var fullLayout = gd._fullLayout;
30993 var geoIds = fullLayout._subplots.geo || [];
30994
30995 for(var i = 0; i < geoIds.length; i++) {
30996 var id = geoIds[i];
30997 var geoLayout = fullLayout[id];
30998
30999 if(attr === 'zoom') {
31000 var scale = geoLayout.projection.scale;
31001 var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
31002
31003 Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
31004 }
31005 }
31006
31007 if(attr === 'reset') {
31008 resetView(gd, 'geo');
31009 }
31010}
31011
31012modeBarButtons.hoverClosestGl2d = {
31013 name: 'hoverClosestGl2d',
31014 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
31015 attr: 'hovermode',
31016 val: null,
31017 toggle: true,
31018 icon: Icons.tooltip_basic,
31019 gravity: 'ne',
31020 click: toggleHover
31021};
31022
31023modeBarButtons.hoverClosestPie = {
31024 name: 'hoverClosestPie',
31025 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
31026 attr: 'hovermode',
31027 val: 'closest',
31028 icon: Icons.tooltip_basic,
31029 gravity: 'ne',
31030 click: toggleHover
31031};
31032
31033function getNextHover(gd) {
31034 var fullLayout = gd._fullLayout;
31035
31036 if(fullLayout.hovermode) return false;
31037
31038 if(fullLayout._has('cartesian')) {
31039 return fullLayout._isHoriz ? 'y' : 'x';
31040 }
31041 return 'closest';
31042}
31043
31044function toggleHover(gd) {
31045 var newHover = getNextHover(gd);
31046 Registry.call('_guiRelayout', gd, 'hovermode', newHover);
31047}
31048
31049modeBarButtons.resetViewSankey = {
31050 name: 'resetSankeyGroup',
31051 title: function(gd) { return _(gd, 'Reset view'); },
31052 icon: Icons.home,
31053 click: function(gd) {
31054 var aObj = {
31055 'node.groups': [],
31056 'node.x': [],
31057 'node.y': []
31058 };
31059 for(var i = 0; i < gd._fullData.length; i++) {
31060 var viewInitial = gd._fullData[i]._viewInitial;
31061 aObj['node.groups'].push(viewInitial.node.groups.slice());
31062 aObj['node.x'].push(viewInitial.node.x.slice());
31063 aObj['node.y'].push(viewInitial.node.y.slice());
31064 }
31065 Registry.call('restyle', gd, aObj);
31066 }
31067};
31068
31069// buttons when more then one plot types are present
31070
31071modeBarButtons.toggleHover = {
31072 name: 'toggleHover',
31073 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
31074 attr: 'hovermode',
31075 val: null,
31076 toggle: true,
31077 icon: Icons.tooltip_basic,
31078 gravity: 'ne',
31079 click: function(gd, ev) {
31080 var layoutUpdate = getNextHover3d(gd, ev);
31081 layoutUpdate.hovermode = getNextHover(gd);
31082
31083 Registry.call('_guiRelayout', gd, layoutUpdate);
31084 }
31085};
31086
31087modeBarButtons.resetViews = {
31088 name: 'resetViews',
31089 title: function(gd) { return _(gd, 'Reset views'); },
31090 icon: Icons.home,
31091 click: function(gd, ev) {
31092 var button = ev.currentTarget;
31093
31094 button.setAttribute('data-attr', 'zoom');
31095 button.setAttribute('data-val', 'reset');
31096 handleCartesian(gd, ev);
31097
31098 button.setAttribute('data-attr', 'resetLastSave');
31099 handleCamera3d(gd, ev);
31100
31101 resetView(gd, 'geo');
31102 resetView(gd, 'mapbox');
31103 }
31104};
31105
31106modeBarButtons.toggleSpikelines = {
31107 name: 'toggleSpikelines',
31108 title: function(gd) { return _(gd, 'Toggle Spike Lines'); },
31109 icon: Icons.spikeline,
31110 attr: '_cartesianSpikesEnabled',
31111 val: 'on',
31112 click: function(gd) {
31113 var fullLayout = gd._fullLayout;
31114 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
31115
31116 fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on';
31117 Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd));
31118 }
31119};
31120
31121function setSpikelineVisibility(gd) {
31122 var fullLayout = gd._fullLayout;
31123 var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on';
31124 var axList = axisIds.list(gd, null, true);
31125 var aobj = {};
31126
31127 for(var i = 0; i < axList.length; i++) {
31128 var ax = axList[i];
31129 aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial;
31130 }
31131
31132 return aobj;
31133}
31134
31135modeBarButtons.resetViewMapbox = {
31136 name: 'resetViewMapbox',
31137 title: function(gd) { return _(gd, 'Reset view'); },
31138 attr: 'reset',
31139 icon: Icons.home,
31140 click: function(gd) {
31141 resetView(gd, 'mapbox');
31142 }
31143};
31144
31145modeBarButtons.zoomInMapbox = {
31146 name: 'zoomInMapbox',
31147 title: function(gd) { return _(gd, 'Zoom in'); },
31148 attr: 'zoom',
31149 val: 'in',
31150 icon: Icons.zoom_plus,
31151 click: handleMapboxZoom
31152};
31153
31154modeBarButtons.zoomOutMapbox = {
31155 name: 'zoomOutMapbox',
31156 title: function(gd) { return _(gd, 'Zoom out'); },
31157 attr: 'zoom',
31158 val: 'out',
31159 icon: Icons.zoom_minus,
31160 click: handleMapboxZoom
31161};
31162
31163function handleMapboxZoom(gd, ev) {
31164 var button = ev.currentTarget;
31165 var val = button.getAttribute('data-val');
31166 var fullLayout = gd._fullLayout;
31167 var subplotIds = fullLayout._subplots.mapbox || [];
31168 var scalar = 1.05;
31169 var aObj = {};
31170
31171 for(var i = 0; i < subplotIds.length; i++) {
31172 var id = subplotIds[i];
31173 var current = fullLayout[id].zoom;
31174 var next = (val === 'in') ? scalar * current : current / scalar;
31175 aObj[id + '.zoom'] = next;
31176 }
31177
31178 Registry.call('_guiRelayout', gd, aObj);
31179}
31180
31181function resetView(gd, subplotType) {
31182 var fullLayout = gd._fullLayout;
31183 var subplotIds = fullLayout._subplots[subplotType] || [];
31184 var aObj = {};
31185
31186 for(var i = 0; i < subplotIds.length; i++) {
31187 var id = subplotIds[i];
31188 var subplotObj = fullLayout[id]._subplot;
31189 var viewInitial = subplotObj.viewInitial;
31190 var viewKeys = Object.keys(viewInitial);
31191
31192 for(var j = 0; j < viewKeys.length; j++) {
31193 var key = viewKeys[j];
31194 aObj[id + '.' + key] = viewInitial[key];
31195 }
31196 }
31197
31198 Registry.call('_guiRelayout', gd, aObj);
31199}
31200
31201},{"../../fonts/ploticon":159,"../../lib":177,"../../plots/cartesian/axis_ids":225,"../../plots/plots":263,"../../registry":272,"../shapes/draw":131}],110:[function(_dereq_,module,exports){
31202/**
31203* Copyright 2012-2020, Plotly, Inc.
31204* All rights reserved.
31205*
31206* This source code is licensed under the MIT license found in the
31207* LICENSE file in the root directory of this source tree.
31208*/
31209
31210
31211'use strict';
31212
31213exports.manage = _dereq_('./manage');
31214
31215},{"./manage":111}],111:[function(_dereq_,module,exports){
31216/**
31217* Copyright 2012-2020, Plotly, Inc.
31218* All rights reserved.
31219*
31220* This source code is licensed under the MIT license found in the
31221* LICENSE file in the root directory of this source tree.
31222*/
31223
31224
31225'use strict';
31226
31227var axisIds = _dereq_('../../plots/cartesian/axis_ids');
31228var scatterSubTypes = _dereq_('../../traces/scatter/subtypes');
31229var Registry = _dereq_('../../registry');
31230var isUnifiedHover = _dereq_('../fx/helpers').isUnifiedHover;
31231
31232var createModeBar = _dereq_('./modebar');
31233var modeBarButtons = _dereq_('./buttons');
31234
31235/**
31236 * ModeBar wrapper around 'create' and 'update',
31237 * chooses buttons to pass to ModeBar constructor based on
31238 * plot type and plot config.
31239 *
31240 * @param {object} gd main plot object
31241 *
31242 */
31243module.exports = function manageModeBar(gd) {
31244 var fullLayout = gd._fullLayout;
31245 var context = gd._context;
31246 var modeBar = fullLayout._modeBar;
31247
31248 if(!context.displayModeBar && !context.watermark) {
31249 if(modeBar) {
31250 modeBar.destroy();
31251 delete fullLayout._modeBar;
31252 }
31253 return;
31254 }
31255
31256 if(!Array.isArray(context.modeBarButtonsToRemove)) {
31257 throw new Error([
31258 '*modeBarButtonsToRemove* configuration options',
31259 'must be an array.'
31260 ].join(' '));
31261 }
31262
31263 if(!Array.isArray(context.modeBarButtonsToAdd)) {
31264 throw new Error([
31265 '*modeBarButtonsToAdd* configuration options',
31266 'must be an array.'
31267 ].join(' '));
31268 }
31269
31270 var customButtons = context.modeBarButtons;
31271 var buttonGroups;
31272
31273 if(Array.isArray(customButtons) && customButtons.length) {
31274 buttonGroups = fillCustomButton(customButtons);
31275 } else if(!context.displayModeBar && context.watermark) {
31276 buttonGroups = [];
31277 } else {
31278 buttonGroups = getButtonGroups(gd);
31279 }
31280
31281 if(modeBar) modeBar.update(gd, buttonGroups);
31282 else fullLayout._modeBar = createModeBar(gd, buttonGroups);
31283};
31284
31285var DRAW_MODES = [
31286 'drawline',
31287 'drawopenpath',
31288 'drawclosedpath',
31289 'drawcircle',
31290 'drawrect',
31291 'eraseshape'
31292];
31293
31294// logic behind which buttons are displayed by default
31295function getButtonGroups(gd) {
31296 var fullLayout = gd._fullLayout;
31297 var fullData = gd._fullData;
31298 var context = gd._context;
31299 var buttonsToRemove = context.modeBarButtonsToRemove;
31300 var buttonsToAdd = context.modeBarButtonsToAdd;
31301
31302 var hasCartesian = fullLayout._has('cartesian');
31303 var hasGL3D = fullLayout._has('gl3d');
31304 var hasGeo = fullLayout._has('geo');
31305 var hasPie = fullLayout._has('pie');
31306 var hasFunnelarea = fullLayout._has('funnelarea');
31307 var hasGL2D = fullLayout._has('gl2d');
31308 var hasTernary = fullLayout._has('ternary');
31309 var hasMapbox = fullLayout._has('mapbox');
31310 var hasPolar = fullLayout._has('polar');
31311 var hasSankey = fullLayout._has('sankey');
31312 var allAxesFixed = areAllAxesFixed(fullLayout);
31313 var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode);
31314
31315 var groups = [];
31316
31317 function addGroup(newGroup) {
31318 if(!newGroup.length) return;
31319
31320 var out = [];
31321
31322 for(var i = 0; i < newGroup.length; i++) {
31323 var button = newGroup[i];
31324 if(buttonsToRemove.indexOf(button) !== -1) continue;
31325 out.push(modeBarButtons[button]);
31326 }
31327
31328 groups.push(out);
31329 }
31330
31331 // buttons common to all plot types
31332 var commonGroup = ['toImage'];
31333 if(context.showEditInChartStudio) commonGroup.push('editInChartStudio');
31334 else if(context.showSendToCloud) commonGroup.push('sendDataToCloud');
31335 addGroup(commonGroup);
31336
31337 var zoomGroup = [];
31338 var hoverGroup = [];
31339 var resetGroup = [];
31340 var dragModeGroup = [];
31341
31342 if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
31343 // graphs with more than one plot types get 'union buttons'
31344 // which reset the view or toggle hover labels across all subplots.
31345 hoverGroup = ['toggleHover'];
31346 resetGroup = ['resetViews'];
31347 } else if(hasGeo) {
31348 zoomGroup = ['zoomInGeo', 'zoomOutGeo'];
31349 hoverGroup = ['hoverClosestGeo'];
31350 resetGroup = ['resetGeo'];
31351 } else if(hasGL3D) {
31352 hoverGroup = ['hoverClosest3d'];
31353 resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d'];
31354 } else if(hasMapbox) {
31355 zoomGroup = ['zoomInMapbox', 'zoomOutMapbox'];
31356 hoverGroup = ['toggleHover'];
31357 resetGroup = ['resetViewMapbox'];
31358 } else if(hasGL2D) {
31359 hoverGroup = ['hoverClosestGl2d'];
31360 } else if(hasPie) {
31361 hoverGroup = ['hoverClosestPie'];
31362 } else if(hasSankey) {
31363 hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian'];
31364 resetGroup = ['resetViewSankey'];
31365 } else { // hasPolar, hasTernary
31366 // always show at least one hover icon.
31367 hoverGroup = ['toggleHover'];
31368 }
31369 // if we have cartesian, allow switching between closest and compare
31370 // regardless of what other types are on the plot, since they'll all
31371 // just treat any truthy hovermode as 'closest'
31372 if(hasCartesian) {
31373 hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'];
31374 }
31375 if(hasNoHover(fullData) || hasUnifiedHoverLabel) {
31376 hoverGroup = [];
31377 }
31378
31379 if((hasCartesian || hasGL2D) && !allAxesFixed) {
31380 zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d'];
31381 if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d'];
31382 }
31383
31384 if(hasGL3D) {
31385 dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'];
31386 } else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
31387 dragModeGroup = ['zoom2d', 'pan2d'];
31388 } else if(hasMapbox || hasGeo) {
31389 dragModeGroup = ['pan2d'];
31390 } else if(hasPolar) {
31391 dragModeGroup = ['zoom2d'];
31392 }
31393 if(isSelectable(fullData)) {
31394 dragModeGroup.push('select2d', 'lasso2d');
31395 }
31396
31397 // accept pre-defined buttons as string
31398 if(Array.isArray(buttonsToAdd)) {
31399 var newList = [];
31400 for(var i = 0; i < buttonsToAdd.length; i++) {
31401 var b = buttonsToAdd[i];
31402 if(typeof b === 'string') {
31403 if(DRAW_MODES.indexOf(b) !== -1) {
31404 if(
31405 fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch)
31406 fullLayout._has('cartesian') // draw shapes in data coordinate
31407 ) {
31408 dragModeGroup.push(b);
31409 }
31410 }
31411 } else newList.push(b);
31412 }
31413 buttonsToAdd = newList;
31414 }
31415
31416 addGroup(dragModeGroup);
31417 addGroup(zoomGroup.concat(resetGroup));
31418 addGroup(hoverGroup);
31419
31420 return appendButtonsToGroups(groups, buttonsToAdd);
31421}
31422
31423function areAllAxesFixed(fullLayout) {
31424 var axList = axisIds.list({_fullLayout: fullLayout}, null, true);
31425
31426 for(var i = 0; i < axList.length; i++) {
31427 if(!axList[i].fixedrange) {
31428 return false;
31429 }
31430 }
31431
31432 return true;
31433}
31434
31435// look for traces that support selection
31436// to be updated as we add more selectPoints handlers
31437function isSelectable(fullData) {
31438 var selectable = false;
31439
31440 for(var i = 0; i < fullData.length; i++) {
31441 if(selectable) break;
31442
31443 var trace = fullData[i];
31444
31445 if(!trace._module || !trace._module.selectPoints) continue;
31446
31447 if(Registry.traceIs(trace, 'scatter-like')) {
31448 if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
31449 selectable = true;
31450 }
31451 } else if(Registry.traceIs(trace, 'box-violin')) {
31452 if(trace.boxpoints === 'all' || trace.points === 'all') {
31453 selectable = true;
31454 }
31455 } else {
31456 // assume that in general if the trace module has selectPoints,
31457 // then it's selectable. Scatter is an exception to this because it must
31458 // have markers or text, not just be a scatter type.
31459
31460 selectable = true;
31461 }
31462 }
31463
31464 return selectable;
31465}
31466
31467// check whether all trace are 'noHover'
31468function hasNoHover(fullData) {
31469 for(var i = 0; i < fullData.length; i++) {
31470 if(!Registry.traceIs(fullData[i], 'noHover')) return false;
31471 }
31472 return true;
31473}
31474
31475function appendButtonsToGroups(groups, buttons) {
31476 if(buttons.length) {
31477 if(Array.isArray(buttons[0])) {
31478 for(var i = 0; i < buttons.length; i++) {
31479 groups.push(buttons[i]);
31480 }
31481 } else groups.push(buttons);
31482 }
31483
31484 return groups;
31485}
31486
31487// fill in custom buttons referring to default mode bar buttons
31488function fillCustomButton(customButtons) {
31489 for(var i = 0; i < customButtons.length; i++) {
31490 var buttonGroup = customButtons[i];
31491
31492 for(var j = 0; j < buttonGroup.length; j++) {
31493 var button = buttonGroup[j];
31494
31495 if(typeof button === 'string') {
31496 if(modeBarButtons[button] !== undefined) {
31497 customButtons[i][j] = modeBarButtons[button];
31498 } else {
31499 throw new Error([
31500 '*modeBarButtons* configuration options',
31501 'invalid button name'
31502 ].join(' '));
31503 }
31504 }
31505 }
31506 }
31507
31508 return customButtons;
31509}
31510
31511},{"../../plots/cartesian/axis_ids":225,"../../registry":272,"../../traces/scatter/subtypes":318,"../fx/helpers":86,"./buttons":109,"./modebar":112}],112:[function(_dereq_,module,exports){
31512/**
31513* Copyright 2012-2020, Plotly, Inc.
31514* All rights reserved.
31515*
31516* This source code is licensed under the MIT license found in the
31517* LICENSE file in the root directory of this source tree.
31518*/
31519
31520
31521'use strict';
31522
31523var d3 = _dereq_('d3');
31524var isNumeric = _dereq_('fast-isnumeric');
31525
31526var Lib = _dereq_('../../lib');
31527var Icons = _dereq_('../../fonts/ploticon');
31528var Parser = new DOMParser();
31529
31530/**
31531 * UI controller for interactive plots
31532 * @Class
31533 * @Param {object} opts
31534 * @Param {object} opts.buttons nested arrays of grouped buttons config objects
31535 * @Param {object} opts.container container div to append modeBar
31536 * @Param {object} opts.graphInfo primary plot object containing data and layout
31537 */
31538function ModeBar(opts) {
31539 this.container = opts.container;
31540 this.element = document.createElement('div');
31541
31542 this.update(opts.graphInfo, opts.buttons);
31543
31544 this.container.appendChild(this.element);
31545}
31546
31547var proto = ModeBar.prototype;
31548
31549/**
31550 * Update modeBar (buttons and logo)
31551 *
31552 * @param {object} graphInfo primary plot object containing data and layout
31553 * @param {array of arrays} buttons nested arrays of grouped buttons to initialize
31554 *
31555 */
31556proto.update = function(graphInfo, buttons) {
31557 this.graphInfo = graphInfo;
31558
31559 var context = this.graphInfo._context;
31560 var fullLayout = this.graphInfo._fullLayout;
31561 var modeBarId = 'modebar-' + fullLayout._uid;
31562
31563 this.element.setAttribute('id', modeBarId);
31564 this._uid = modeBarId;
31565
31566 this.element.className = 'modebar';
31567 if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg';
31568
31569 if(fullLayout.modebar.orientation === 'v') {
31570 this.element.className += ' vertical';
31571 buttons = buttons.reverse();
31572 }
31573
31574 var style = fullLayout.modebar;
31575 var bgSelector = context.displayModeBar === 'hover' ? '.js-plotly-plot .plotly:hover ' : '';
31576
31577 Lib.deleteRelatedStyleRule(modeBarId);
31578 Lib.addRelatedStyleRule(modeBarId, bgSelector + '#' + modeBarId + ' .modebar-group', 'background-color: ' + style.bgcolor);
31579 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + style.color);
31580 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + style.activecolor);
31581 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + style.activecolor);
31582
31583 // if buttons or logo have changed, redraw modebar interior
31584 var needsNewButtons = !this.hasButtons(buttons);
31585 var needsNewLogo = (this.hasLogo !== context.displaylogo);
31586 var needsNewLocale = (this.locale !== context.locale);
31587
31588 this.locale = context.locale;
31589
31590 if(needsNewButtons || needsNewLogo || needsNewLocale) {
31591 this.removeAllButtons();
31592
31593 this.updateButtons(buttons);
31594
31595 if(context.watermark || context.displaylogo) {
31596 var logoGroup = this.getLogo();
31597 if(context.watermark) {
31598 logoGroup.className = logoGroup.className + ' watermark';
31599 }
31600
31601 if(fullLayout.modebar.orientation === 'v') {
31602 this.element.insertBefore(logoGroup, this.element.childNodes[0]);
31603 } else {
31604 this.element.appendChild(logoGroup);
31605 }
31606
31607 this.hasLogo = true;
31608 }
31609 }
31610
31611 this.updateActiveButton();
31612};
31613
31614proto.updateButtons = function(buttons) {
31615 var _this = this;
31616
31617 this.buttons = buttons;
31618 this.buttonElements = [];
31619 this.buttonsNames = [];
31620
31621 this.buttons.forEach(function(buttonGroup) {
31622 var group = _this.createGroup();
31623
31624 buttonGroup.forEach(function(buttonConfig) {
31625 var buttonName = buttonConfig.name;
31626 if(!buttonName) {
31627 throw new Error('must provide button \'name\' in button config');
31628 }
31629 if(_this.buttonsNames.indexOf(buttonName) !== -1) {
31630 throw new Error('button name \'' + buttonName + '\' is taken');
31631 }
31632 _this.buttonsNames.push(buttonName);
31633
31634 var button = _this.createButton(buttonConfig);
31635 _this.buttonElements.push(button);
31636 group.appendChild(button);
31637 });
31638
31639 _this.element.appendChild(group);
31640 });
31641};
31642
31643/**
31644 * Empty div for containing a group of buttons
31645 * @Return {HTMLelement}
31646 */
31647proto.createGroup = function() {
31648 var group = document.createElement('div');
31649 group.className = 'modebar-group';
31650 return group;
31651};
31652
31653/**
31654 * Create a new button div and set constant and configurable attributes
31655 * @Param {object} config (see ./buttons.js for more info)
31656 * @Return {HTMLelement}
31657 */
31658proto.createButton = function(config) {
31659 var _this = this;
31660 var button = document.createElement('a');
31661
31662 button.setAttribute('rel', 'tooltip');
31663 button.className = 'modebar-btn';
31664
31665 var title = config.title;
31666 if(title === undefined) title = config.name;
31667 // for localization: allow title to be a callable that takes gd as arg
31668 else if(typeof title === 'function') title = title(this.graphInfo);
31669
31670 if(title || title === 0) button.setAttribute('data-title', title);
31671
31672 if(config.attr !== undefined) button.setAttribute('data-attr', config.attr);
31673
31674 var val = config.val;
31675 if(val !== undefined) {
31676 if(typeof val === 'function') val = val(this.graphInfo);
31677 button.setAttribute('data-val', val);
31678 }
31679
31680 var click = config.click;
31681 if(typeof click !== 'function') {
31682 throw new Error('must provide button \'click\' function in button config');
31683 } else {
31684 button.addEventListener('click', function(ev) {
31685 config.click(_this.graphInfo, ev);
31686
31687 // only needed for 'hoverClosestGeo' which does not call relayout
31688 _this.updateActiveButton(ev.currentTarget);
31689 });
31690 }
31691
31692 button.setAttribute('data-toggle', config.toggle || false);
31693 if(config.toggle) d3.select(button).classed('active', true);
31694
31695 var icon = config.icon;
31696 if(typeof icon === 'function') {
31697 button.appendChild(icon());
31698 } else {
31699 button.appendChild(this.createIcon(icon || Icons.question));
31700 }
31701 button.setAttribute('data-gravity', config.gravity || 'n');
31702
31703 return button;
31704};
31705
31706/**
31707 * Add an icon to a button
31708 * @Param {object} thisIcon
31709 * @Param {number} thisIcon.width
31710 * @Param {string} thisIcon.path
31711 * @Param {string} thisIcon.color
31712 * @Return {HTMLelement}
31713 */
31714proto.createIcon = function(thisIcon) {
31715 var iconHeight = isNumeric(thisIcon.height) ?
31716 Number(thisIcon.height) :
31717 thisIcon.ascent - thisIcon.descent;
31718 var svgNS = 'http://www.w3.org/2000/svg';
31719 var icon;
31720
31721 if(thisIcon.path) {
31722 icon = document.createElementNS(svgNS, 'svg');
31723 icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
31724 icon.setAttribute('class', 'icon');
31725
31726 var path = document.createElementNS(svgNS, 'path');
31727 path.setAttribute('d', thisIcon.path);
31728
31729 if(thisIcon.transform) {
31730 path.setAttribute('transform', thisIcon.transform);
31731 } else if(thisIcon.ascent !== undefined) {
31732 // Legacy icon transform calculation
31733 path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
31734 }
31735
31736 icon.appendChild(path);
31737 }
31738
31739 if(thisIcon.svg) {
31740 var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml');
31741 icon = svgDoc.childNodes[0];
31742 }
31743
31744 icon.setAttribute('height', '1em');
31745 icon.setAttribute('width', '1em');
31746
31747 return icon;
31748};
31749
31750/**
31751 * Updates active button with attribute specified in layout
31752 * @Param {object} graphInfo plot object containing data and layout
31753 * @Return {HTMLelement}
31754 */
31755proto.updateActiveButton = function(buttonClicked) {
31756 var fullLayout = this.graphInfo._fullLayout;
31757 var dataAttrClicked = (buttonClicked !== undefined) ?
31758 buttonClicked.getAttribute('data-attr') :
31759 null;
31760
31761 this.buttonElements.forEach(function(button) {
31762 var thisval = button.getAttribute('data-val') || true;
31763 var dataAttr = button.getAttribute('data-attr');
31764 var isToggleButton = (button.getAttribute('data-toggle') === 'true');
31765 var button3 = d3.select(button);
31766
31767 // Use 'data-toggle' and 'buttonClicked' to toggle buttons
31768 // that have no one-to-one equivalent in fullLayout
31769 if(isToggleButton) {
31770 if(dataAttr === dataAttrClicked) {
31771 button3.classed('active', !button3.classed('active'));
31772 }
31773 } else {
31774 var val = (dataAttr === null) ?
31775 dataAttr :
31776 Lib.nestedProperty(fullLayout, dataAttr).get();
31777
31778 button3.classed('active', val === thisval);
31779 }
31780 });
31781};
31782
31783/**
31784 * Check if modeBar is configured as button configuration argument
31785 *
31786 * @Param {object} buttons 2d array of grouped button config objects
31787 * @Return {boolean}
31788 */
31789proto.hasButtons = function(buttons) {
31790 var currentButtons = this.buttons;
31791
31792 if(!currentButtons) return false;
31793
31794 if(buttons.length !== currentButtons.length) return false;
31795
31796 for(var i = 0; i < buttons.length; ++i) {
31797 if(buttons[i].length !== currentButtons[i].length) return false;
31798 for(var j = 0; j < buttons[i].length; j++) {
31799 if(buttons[i][j].name !== currentButtons[i][j].name) return false;
31800 }
31801 }
31802
31803 return true;
31804};
31805
31806/**
31807 * @return {HTMLDivElement} The logo image wrapped in a group
31808 */
31809proto.getLogo = function() {
31810 var group = this.createGroup();
31811 var a = document.createElement('a');
31812
31813 a.href = 'https://plotly.com/';
31814 a.target = '_blank';
31815 a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly'));
31816 a.className = 'modebar-btn plotlyjsicon modebar-btn--logo';
31817
31818 a.appendChild(this.createIcon(Icons.newplotlylogo));
31819
31820 group.appendChild(a);
31821 return group;
31822};
31823
31824proto.removeAllButtons = function() {
31825 while(this.element.firstChild) {
31826 this.element.removeChild(this.element.firstChild);
31827 }
31828
31829 this.hasLogo = false;
31830};
31831
31832proto.destroy = function() {
31833 Lib.removeElement(this.container.querySelector('.modebar'));
31834 Lib.deleteRelatedStyleRule(this._uid);
31835};
31836
31837function createModeBar(gd, buttons) {
31838 var fullLayout = gd._fullLayout;
31839
31840 var modeBar = new ModeBar({
31841 graphInfo: gd,
31842 container: fullLayout._modebardiv.node(),
31843 buttons: buttons
31844 });
31845
31846 if(fullLayout._privateplot) {
31847 d3.select(modeBar.element).append('span')
31848 .classed('badge-private float--left', true)
31849 .text('PRIVATE');
31850 }
31851
31852 return modeBar;
31853}
31854
31855module.exports = createModeBar;
31856
31857},{"../../fonts/ploticon":159,"../../lib":177,"d3":13,"fast-isnumeric":15}],113:[function(_dereq_,module,exports){
31858/**
31859* Copyright 2012-2020, Plotly, Inc.
31860* All rights reserved.
31861*
31862* This source code is licensed under the MIT license found in the
31863* LICENSE file in the root directory of this source tree.
31864*/
31865
31866'use strict';
31867
31868var fontAttrs = _dereq_('../../plots/font_attributes');
31869var colorAttrs = _dereq_('../color/attributes');
31870var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
31871
31872var buttonAttrs = templatedArray('button', {
31873 visible: {
31874 valType: 'boolean',
31875
31876 dflt: true,
31877 editType: 'plot',
31878
31879 },
31880 step: {
31881 valType: 'enumerated',
31882
31883 values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'],
31884 dflt: 'month',
31885 editType: 'plot',
31886
31887 },
31888 stepmode: {
31889 valType: 'enumerated',
31890
31891 values: ['backward', 'todate'],
31892 dflt: 'backward',
31893 editType: 'plot',
31894
31895 },
31896 count: {
31897 valType: 'number',
31898
31899 min: 0,
31900 dflt: 1,
31901 editType: 'plot',
31902
31903 },
31904 label: {
31905 valType: 'string',
31906
31907 editType: 'plot',
31908
31909 },
31910 editType: 'plot',
31911
31912});
31913
31914module.exports = {
31915 visible: {
31916 valType: 'boolean',
31917
31918 editType: 'plot',
31919
31920 },
31921
31922 buttons: buttonAttrs,
31923
31924 x: {
31925 valType: 'number',
31926 min: -2,
31927 max: 3,
31928
31929 editType: 'plot',
31930
31931 },
31932 xanchor: {
31933 valType: 'enumerated',
31934 values: ['auto', 'left', 'center', 'right'],
31935 dflt: 'left',
31936
31937 editType: 'plot',
31938
31939 },
31940 y: {
31941 valType: 'number',
31942 min: -2,
31943 max: 3,
31944
31945 editType: 'plot',
31946
31947 },
31948 yanchor: {
31949 valType: 'enumerated',
31950 values: ['auto', 'top', 'middle', 'bottom'],
31951 dflt: 'bottom',
31952
31953 editType: 'plot',
31954
31955 },
31956
31957 font: fontAttrs({
31958 editType: 'plot',
31959
31960 }),
31961
31962 bgcolor: {
31963 valType: 'color',
31964 dflt: colorAttrs.lightLine,
31965
31966 editType: 'plot',
31967
31968 },
31969 activecolor: {
31970 valType: 'color',
31971
31972 editType: 'plot',
31973
31974 },
31975 bordercolor: {
31976 valType: 'color',
31977 dflt: colorAttrs.defaultLine,
31978
31979 editType: 'plot',
31980
31981 },
31982 borderwidth: {
31983 valType: 'number',
31984 min: 0,
31985 dflt: 0,
31986
31987 editType: 'plot',
31988
31989 },
31990 editType: 'plot'
31991};
31992
31993},{"../../plot_api/plot_template":212,"../../plots/font_attributes":250,"../color/attributes":49}],114:[function(_dereq_,module,exports){
31994/**
31995* Copyright 2012-2020, Plotly, Inc.
31996* All rights reserved.
31997*
31998* This source code is licensed under the MIT license found in the
31999* LICENSE file in the root directory of this source tree.
32000*/
32001
32002'use strict';
32003
32004
32005module.exports = {
32006
32007 // 'y' position pad above counter axis domain
32008 yPad: 0.02,
32009
32010 // minimum button width (regardless of text size)
32011 minButtonWidth: 30,
32012
32013 // buttons rect radii
32014 rx: 3,
32015 ry: 3,
32016
32017 // light fraction used to compute the 'activecolor' default
32018 lightAmount: 25,
32019 darkAmount: 10
32020};
32021
32022},{}],115:[function(_dereq_,module,exports){
32023/**
32024* Copyright 2012-2020, Plotly, Inc.
32025* All rights reserved.
32026*
32027* This source code is licensed under the MIT license found in the
32028* LICENSE file in the root directory of this source tree.
32029*/
32030
32031'use strict';
32032
32033var Lib = _dereq_('../../lib');
32034var Color = _dereq_('../color');
32035var Template = _dereq_('../../plot_api/plot_template');
32036var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
32037
32038var attributes = _dereq_('./attributes');
32039var constants = _dereq_('./constants');
32040
32041
32042module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) {
32043 var selectorIn = containerIn.rangeselector || {};
32044 var selectorOut = Template.newContainer(containerOut, 'rangeselector');
32045
32046 function coerce(attr, dflt) {
32047 return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt);
32048 }
32049
32050 var buttons = handleArrayContainerDefaults(selectorIn, selectorOut, {
32051 name: 'buttons',
32052 handleItemDefaults: buttonDefaults,
32053 calendar: calendar
32054 });
32055
32056 var visible = coerce('visible', buttons.length > 0);
32057 if(visible) {
32058 var posDflt = getPosDflt(containerOut, layout, counterAxes);
32059 coerce('x', posDflt[0]);
32060 coerce('y', posDflt[1]);
32061 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
32062
32063 coerce('xanchor');
32064 coerce('yanchor');
32065
32066 Lib.coerceFont(coerce, 'font', layout.font);
32067
32068 var bgColor = coerce('bgcolor');
32069 coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount));
32070 coerce('bordercolor');
32071 coerce('borderwidth');
32072 }
32073};
32074
32075function buttonDefaults(buttonIn, buttonOut, selectorOut, opts) {
32076 var calendar = opts.calendar;
32077
32078 function coerce(attr, dflt) {
32079 return Lib.coerce(buttonIn, buttonOut, attributes.buttons, attr, dflt);
32080 }
32081
32082 var visible = coerce('visible');
32083
32084 if(visible) {
32085 var step = coerce('step');
32086 if(step !== 'all') {
32087 if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) {
32088 buttonOut.stepmode = 'backward';
32089 } else {
32090 coerce('stepmode');
32091 }
32092
32093 coerce('count');
32094 }
32095
32096 coerce('label');
32097 }
32098}
32099
32100function getPosDflt(containerOut, layout, counterAxes) {
32101 var anchoredList = counterAxes.filter(function(ax) {
32102 return layout[ax].anchor === containerOut._id;
32103 });
32104
32105 var posY = 0;
32106 for(var i = 0; i < anchoredList.length; i++) {
32107 var domain = layout[anchoredList[i]].domain;
32108 if(domain) posY = Math.max(domain[1], posY);
32109 }
32110
32111 return [containerOut.domain[0], posY + constants.yPad];
32112}
32113
32114},{"../../lib":177,"../../plot_api/plot_template":212,"../../plots/array_container_defaults":218,"../color":50,"./attributes":113,"./constants":114}],116:[function(_dereq_,module,exports){
32115/**
32116* Copyright 2012-2020, Plotly, Inc.
32117* All rights reserved.
32118*
32119* This source code is licensed under the MIT license found in the
32120* LICENSE file in the root directory of this source tree.
32121*/
32122
32123'use strict';
32124
32125var d3 = _dereq_('d3');
32126
32127var Registry = _dereq_('../../registry');
32128var Plots = _dereq_('../../plots/plots');
32129var Color = _dereq_('../color');
32130var Drawing = _dereq_('../drawing');
32131var Lib = _dereq_('../../lib');
32132var svgTextUtils = _dereq_('../../lib/svg_text_utils');
32133var axisIds = _dereq_('../../plots/cartesian/axis_ids');
32134
32135var alignmentConstants = _dereq_('../../constants/alignment');
32136var LINE_SPACING = alignmentConstants.LINE_SPACING;
32137var FROM_TL = alignmentConstants.FROM_TL;
32138var FROM_BR = alignmentConstants.FROM_BR;
32139
32140var constants = _dereq_('./constants');
32141var getUpdateObject = _dereq_('./get_update_object');
32142
32143module.exports = function draw(gd) {
32144 var fullLayout = gd._fullLayout;
32145
32146 var selectors = fullLayout._infolayer.selectAll('.rangeselector')
32147 .data(makeSelectorData(gd), selectorKeyFunc);
32148
32149 selectors.enter().append('g')
32150 .classed('rangeselector', true);
32151
32152 selectors.exit().remove();
32153
32154 selectors.style({
32155 cursor: 'pointer',
32156 'pointer-events': 'all'
32157 });
32158
32159 selectors.each(function(d) {
32160 var selector = d3.select(this);
32161 var axisLayout = d;
32162 var selectorLayout = axisLayout.rangeselector;
32163
32164 var buttons = selector.selectAll('g.button')
32165 .data(Lib.filterVisible(selectorLayout.buttons));
32166
32167 buttons.enter().append('g')
32168 .classed('button', true);
32169
32170 buttons.exit().remove();
32171
32172 buttons.each(function(d) {
32173 var button = d3.select(this);
32174 var update = getUpdateObject(axisLayout, d);
32175
32176 d._isActive = isActive(axisLayout, d, update);
32177
32178 button.call(drawButtonRect, selectorLayout, d);
32179 button.call(drawButtonText, selectorLayout, d, gd);
32180
32181 button.on('click', function() {
32182 if(gd._dragged) return;
32183
32184 Registry.call('_guiRelayout', gd, update);
32185 });
32186
32187 button.on('mouseover', function() {
32188 d._isHovered = true;
32189 button.call(drawButtonRect, selectorLayout, d);
32190 });
32191
32192 button.on('mouseout', function() {
32193 d._isHovered = false;
32194 button.call(drawButtonRect, selectorLayout, d);
32195 });
32196 });
32197
32198 reposition(gd, buttons, selectorLayout, axisLayout._name, selector);
32199 });
32200};
32201
32202function makeSelectorData(gd) {
32203 var axes = axisIds.list(gd, 'x', true);
32204 var data = [];
32205
32206 for(var i = 0; i < axes.length; i++) {
32207 var axis = axes[i];
32208
32209 if(axis.rangeselector && axis.rangeselector.visible) {
32210 data.push(axis);
32211 }
32212 }
32213
32214 return data;
32215}
32216
32217function selectorKeyFunc(d) {
32218 return d._id;
32219}
32220
32221function isActive(axisLayout, opts, update) {
32222 if(opts.step === 'all') {
32223 return axisLayout.autorange === true;
32224 } else {
32225 var keys = Object.keys(update);
32226
32227 return (
32228 axisLayout.range[0] === update[keys[0]] &&
32229 axisLayout.range[1] === update[keys[1]]
32230 );
32231 }
32232}
32233
32234function drawButtonRect(button, selectorLayout, d) {
32235 var rect = Lib.ensureSingle(button, 'rect', 'selector-rect', function(s) {
32236 s.attr('shape-rendering', 'crispEdges');
32237 });
32238
32239 rect.attr({
32240 'rx': constants.rx,
32241 'ry': constants.ry
32242 });
32243
32244 rect.call(Color.stroke, selectorLayout.bordercolor)
32245 .call(Color.fill, getFillColor(selectorLayout, d))
32246 .style('stroke-width', selectorLayout.borderwidth + 'px');
32247}
32248
32249function getFillColor(selectorLayout, d) {
32250 return (d._isActive || d._isHovered) ?
32251 selectorLayout.activecolor :
32252 selectorLayout.bgcolor;
32253}
32254
32255function drawButtonText(button, selectorLayout, d, gd) {
32256 function textLayout(s) {
32257 svgTextUtils.convertToTspans(s, gd);
32258 }
32259
32260 var text = Lib.ensureSingle(button, 'text', 'selector-text', function(s) {
32261 s.classed('user-select-none', true)
32262 .attr('text-anchor', 'middle');
32263 });
32264
32265 text.call(Drawing.font, selectorLayout.font)
32266 .text(getLabel(d, gd._fullLayout._meta))
32267 .call(textLayout);
32268}
32269
32270function getLabel(opts, _meta) {
32271 if(opts.label) {
32272 return _meta ?
32273 Lib.templateString(opts.label, _meta) :
32274 opts.label;
32275 }
32276
32277 if(opts.step === 'all') return 'all';
32278
32279 return opts.count + opts.step.charAt(0);
32280}
32281
32282function reposition(gd, buttons, opts, axName, selector) {
32283 var width = 0;
32284 var height = 0;
32285
32286 var borderWidth = opts.borderwidth;
32287
32288 buttons.each(function() {
32289 var button = d3.select(this);
32290 var text = button.select('.selector-text');
32291
32292 var tHeight = opts.font.size * LINE_SPACING;
32293 var hEff = Math.max(tHeight * svgTextUtils.lineCount(text), 16) + 3;
32294
32295 height = Math.max(height, hEff);
32296 });
32297
32298 buttons.each(function() {
32299 var button = d3.select(this);
32300 var rect = button.select('.selector-rect');
32301 var text = button.select('.selector-text');
32302
32303 var tWidth = text.node() && Drawing.bBox(text.node()).width;
32304 var tHeight = opts.font.size * LINE_SPACING;
32305 var tLines = svgTextUtils.lineCount(text);
32306
32307 var wEff = Math.max(tWidth + 10, constants.minButtonWidth);
32308
32309 // TODO add MathJax support
32310
32311 // TODO add buttongap attribute
32312
32313 button.attr('transform', 'translate(' +
32314 (borderWidth + width) + ',' + borderWidth +
32315 ')');
32316
32317 rect.attr({
32318 x: 0,
32319 y: 0,
32320 width: wEff,
32321 height: height
32322 });
32323
32324 svgTextUtils.positionText(text, wEff / 2,
32325 height / 2 - ((tLines - 1) * tHeight / 2) + 3);
32326
32327 width += wEff + 5;
32328 });
32329
32330 var graphSize = gd._fullLayout._size;
32331 var lx = graphSize.l + graphSize.w * opts.x;
32332 var ly = graphSize.t + graphSize.h * (1 - opts.y);
32333
32334 var xanchor = 'left';
32335 if(Lib.isRightAnchor(opts)) {
32336 lx -= width;
32337 xanchor = 'right';
32338 }
32339 if(Lib.isCenterAnchor(opts)) {
32340 lx -= width / 2;
32341 xanchor = 'center';
32342 }
32343
32344 var yanchor = 'top';
32345 if(Lib.isBottomAnchor(opts)) {
32346 ly -= height;
32347 yanchor = 'bottom';
32348 }
32349 if(Lib.isMiddleAnchor(opts)) {
32350 ly -= height / 2;
32351 yanchor = 'middle';
32352 }
32353
32354 width = Math.ceil(width);
32355 height = Math.ceil(height);
32356 lx = Math.round(lx);
32357 ly = Math.round(ly);
32358
32359 Plots.autoMargin(gd, axName + '-range-selector', {
32360 x: opts.x,
32361 y: opts.y,
32362 l: width * FROM_TL[xanchor],
32363 r: width * FROM_BR[xanchor],
32364 b: height * FROM_BR[yanchor],
32365 t: height * FROM_TL[yanchor]
32366 });
32367
32368 selector.attr('transform', 'translate(' + lx + ',' + ly + ')');
32369}
32370
32371},{"../../constants/alignment":152,"../../lib":177,"../../lib/svg_text_utils":198,"../../plots/cartesian/axis_ids":225,"../../plots/plots":263,"../../registry":272,"../color":50,"../drawing":72,"./constants":114,"./get_update_object":117,"d3":13}],117:[function(_dereq_,module,exports){
32372/**
32373* Copyright 2012-2020, Plotly, Inc.
32374* All rights reserved.
32375*
32376* This source code is licensed under the MIT license found in the
32377* LICENSE file in the root directory of this source tree.
32378*/
32379
32380
32381'use strict';
32382
32383var d3 = _dereq_('d3');
32384
32385module.exports = function getUpdateObject(axisLayout, buttonLayout) {
32386 var axName = axisLayout._name;
32387 var update = {};
32388
32389 if(buttonLayout.step === 'all') {
32390 update[axName + '.autorange'] = true;
32391 } else {
32392 var xrange = getXRange(axisLayout, buttonLayout);
32393
32394 update[axName + '.range[0]'] = xrange[0];
32395 update[axName + '.range[1]'] = xrange[1];
32396 }
32397
32398 return update;
32399};
32400
32401function getXRange(axisLayout, buttonLayout) {
32402 var currentRange = axisLayout.range;
32403 var base = new Date(axisLayout.r2l(currentRange[1]));
32404 var step = buttonLayout.step;
32405 var count = buttonLayout.count;
32406 var range0;
32407
32408 switch(buttonLayout.stepmode) {
32409 case 'backward':
32410 range0 = axisLayout.l2r(+d3.time[step].utc.offset(base, -count));
32411 break;
32412
32413 case 'todate':
32414 var base2 = d3.time[step].utc.offset(base, -count);
32415
32416 range0 = axisLayout.l2r(+d3.time[step].utc.ceil(base2));
32417 break;
32418 }
32419
32420 var range1 = currentRange[1];
32421
32422 return [range0, range1];
32423}
32424
32425},{"d3":13}],118:[function(_dereq_,module,exports){
32426/**
32427* Copyright 2012-2020, Plotly, Inc.
32428* All rights reserved.
32429*
32430* This source code is licensed under the MIT license found in the
32431* LICENSE file in the root directory of this source tree.
32432*/
32433
32434'use strict';
32435
32436module.exports = {
32437 moduleType: 'component',
32438 name: 'rangeselector',
32439
32440 schema: {
32441 subplots: {
32442 xaxis: {rangeselector: _dereq_('./attributes')}
32443 }
32444 },
32445
32446 layoutAttributes: _dereq_('./attributes'),
32447 handleDefaults: _dereq_('./defaults'),
32448
32449 draw: _dereq_('./draw')
32450};
32451
32452},{"./attributes":113,"./defaults":115,"./draw":116}],119:[function(_dereq_,module,exports){
32453/**
32454* Copyright 2012-2020, Plotly, Inc.
32455* All rights reserved.
32456*
32457* This source code is licensed under the MIT license found in the
32458* LICENSE file in the root directory of this source tree.
32459*/
32460
32461'use strict';
32462
32463var colorAttributes = _dereq_('../color/attributes');
32464
32465module.exports = {
32466 bgcolor: {
32467 valType: 'color',
32468 dflt: colorAttributes.background,
32469
32470 editType: 'plot',
32471
32472 },
32473 bordercolor: {
32474 valType: 'color',
32475 dflt: colorAttributes.defaultLine,
32476
32477 editType: 'plot',
32478
32479 },
32480 borderwidth: {
32481 valType: 'integer',
32482 dflt: 0,
32483 min: 0,
32484
32485 editType: 'plot',
32486
32487 },
32488 autorange: {
32489 valType: 'boolean',
32490 dflt: true,
32491
32492 editType: 'calc',
32493 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
32494
32495 },
32496 range: {
32497 valType: 'info_array',
32498
32499 items: [
32500 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}},
32501 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}}
32502 ],
32503 editType: 'calc',
32504 impliedEdits: {'autorange': false},
32505
32506 },
32507 thickness: {
32508 valType: 'number',
32509 dflt: 0.15,
32510 min: 0,
32511 max: 1,
32512
32513 editType: 'plot',
32514
32515 },
32516 visible: {
32517 valType: 'boolean',
32518 dflt: true,
32519
32520 editType: 'calc',
32521
32522 },
32523 editType: 'calc'
32524};
32525
32526},{"../color/attributes":49}],120:[function(_dereq_,module,exports){
32527/**
32528* Copyright 2012-2020, Plotly, Inc.
32529* All rights reserved.
32530*
32531* This source code is licensed under the MIT license found in the
32532* LICENSE file in the root directory of this source tree.
32533*/
32534
32535'use strict';
32536
32537var listAxes = _dereq_('../../plots/cartesian/axis_ids').list;
32538var getAutoRange = _dereq_('../../plots/cartesian/autorange').getAutoRange;
32539var constants = _dereq_('./constants');
32540
32541module.exports = function calcAutorange(gd) {
32542 var axes = listAxes(gd, 'x', true);
32543
32544 // Compute new slider range using axis autorange if necessary.
32545 //
32546 // Copy back range to input range slider container to skip
32547 // this step in subsequent draw calls.
32548
32549 for(var i = 0; i < axes.length; i++) {
32550 var ax = axes[i];
32551 var opts = ax[constants.name];
32552
32553 if(opts && opts.visible && opts.autorange) {
32554 opts._input.autorange = true;
32555 opts._input.range = opts.range = getAutoRange(gd, ax);
32556 }
32557 }
32558};
32559
32560},{"../../plots/cartesian/autorange":221,"../../plots/cartesian/axis_ids":225,"./constants":121}],121:[function(_dereq_,module,exports){
32561/**
32562* Copyright 2012-2020, Plotly, Inc.
32563* All rights reserved.
32564*
32565* This source code is licensed under the MIT license found in the
32566* LICENSE file in the root directory of this source tree.
32567*/
32568
32569'use strict';
32570
32571module.exports = {
32572
32573 // attribute container name
32574 name: 'rangeslider',
32575
32576 // class names
32577
32578 containerClassName: 'rangeslider-container',
32579 bgClassName: 'rangeslider-bg',
32580 rangePlotClassName: 'rangeslider-rangeplot',
32581
32582 maskMinClassName: 'rangeslider-mask-min',
32583 maskMaxClassName: 'rangeslider-mask-max',
32584 slideBoxClassName: 'rangeslider-slidebox',
32585
32586 grabberMinClassName: 'rangeslider-grabber-min',
32587 grabAreaMinClassName: 'rangeslider-grabarea-min',
32588 handleMinClassName: 'rangeslider-handle-min',
32589
32590 grabberMaxClassName: 'rangeslider-grabber-max',
32591 grabAreaMaxClassName: 'rangeslider-grabarea-max',
32592 handleMaxClassName: 'rangeslider-handle-max',
32593
32594 maskMinOppAxisClassName: 'rangeslider-mask-min-opp-axis',
32595 maskMaxOppAxisClassName: 'rangeslider-mask-max-opp-axis',
32596
32597 // style constants
32598
32599 maskColor: 'rgba(0,0,0,0.4)',
32600 maskOppAxisColor: 'rgba(0,0,0,0.2)',
32601
32602 slideBoxFill: 'transparent',
32603 slideBoxCursor: 'ew-resize',
32604
32605 grabAreaFill: 'transparent',
32606 grabAreaCursor: 'col-resize',
32607 grabAreaWidth: 10,
32608
32609 handleWidth: 4,
32610 handleRadius: 1,
32611 handleStrokeWidth: 1,
32612
32613 extraPad: 15
32614};
32615
32616},{}],122:[function(_dereq_,module,exports){
32617/**
32618* Copyright 2012-2020, Plotly, Inc.
32619* All rights reserved.
32620*
32621* This source code is licensed under the MIT license found in the
32622* LICENSE file in the root directory of this source tree.
32623*/
32624
32625'use strict';
32626
32627var Lib = _dereq_('../../lib');
32628var Template = _dereq_('../../plot_api/plot_template');
32629var axisIds = _dereq_('../../plots/cartesian/axis_ids');
32630
32631var attributes = _dereq_('./attributes');
32632var oppAxisAttrs = _dereq_('./oppaxis_attributes');
32633
32634module.exports = function handleDefaults(layoutIn, layoutOut, axName) {
32635 var axIn = layoutIn[axName];
32636 var axOut = layoutOut[axName];
32637
32638 if(!(axIn.rangeslider || layoutOut._requestRangeslider[axOut._id])) return;
32639
32640 // not super proud of this (maybe store _ in axis object instead
32641 if(!Lib.isPlainObject(axIn.rangeslider)) {
32642 axIn.rangeslider = {};
32643 }
32644
32645 var containerIn = axIn.rangeslider;
32646 var containerOut = Template.newContainer(axOut, 'rangeslider');
32647
32648 function coerce(attr, dflt) {
32649 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
32650 }
32651
32652 var rangeContainerIn, rangeContainerOut;
32653 function coerceRange(attr, dflt) {
32654 return Lib.coerce(rangeContainerIn, rangeContainerOut, oppAxisAttrs, attr, dflt);
32655 }
32656
32657 var visible = coerce('visible');
32658 if(!visible) return;
32659
32660 coerce('bgcolor', layoutOut.plot_bgcolor);
32661 coerce('bordercolor');
32662 coerce('borderwidth');
32663 coerce('thickness');
32664
32665 coerce('autorange', !axOut.isValidRange(containerIn.range));
32666 coerce('range');
32667
32668 var subplots = layoutOut._subplots;
32669 if(subplots) {
32670 var yIds = subplots.cartesian
32671 .filter(function(subplotId) {
32672 return subplotId.substr(0, subplotId.indexOf('y')) === axisIds.name2id(axName);
32673 })
32674 .map(function(subplotId) {
32675 return subplotId.substr(subplotId.indexOf('y'), subplotId.length);
32676 });
32677 var yNames = Lib.simpleMap(yIds, axisIds.id2name);
32678 for(var i = 0; i < yNames.length; i++) {
32679 var yName = yNames[i];
32680
32681 rangeContainerIn = containerIn[yName] || {};
32682 rangeContainerOut = Template.newContainer(containerOut, yName, 'yaxis');
32683
32684 var yAxOut = layoutOut[yName];
32685
32686 var rangemodeDflt;
32687 if(rangeContainerIn.range && yAxOut.isValidRange(rangeContainerIn.range)) {
32688 rangemodeDflt = 'fixed';
32689 }
32690
32691 var rangeMode = coerceRange('rangemode', rangemodeDflt);
32692 if(rangeMode !== 'match') {
32693 coerceRange('range', yAxOut.range.slice());
32694 }
32695 }
32696 }
32697
32698 // to map back range slider (auto) range
32699 containerOut._input = containerIn;
32700};
32701
32702},{"../../lib":177,"../../plot_api/plot_template":212,"../../plots/cartesian/axis_ids":225,"./attributes":119,"./oppaxis_attributes":126}],123:[function(_dereq_,module,exports){
32703/**
32704* Copyright 2012-2020, Plotly, Inc.
32705* All rights reserved.
32706*
32707* This source code is licensed under the MIT license found in the
32708* LICENSE file in the root directory of this source tree.
32709*/
32710
32711'use strict';
32712
32713var d3 = _dereq_('d3');
32714
32715var Registry = _dereq_('../../registry');
32716var Plots = _dereq_('../../plots/plots');
32717
32718var Lib = _dereq_('../../lib');
32719var Drawing = _dereq_('../drawing');
32720var Color = _dereq_('../color');
32721var Titles = _dereq_('../titles');
32722
32723var Cartesian = _dereq_('../../plots/cartesian');
32724var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
32725
32726var dragElement = _dereq_('../dragelement');
32727var setCursor = _dereq_('../../lib/setcursor');
32728
32729var constants = _dereq_('./constants');
32730
32731module.exports = function(gd) {
32732 var fullLayout = gd._fullLayout;
32733 var rangeSliderData = fullLayout._rangeSliderData;
32734 for(var i = 0; i < rangeSliderData.length; i++) {
32735 var opts = rangeSliderData[i][constants.name];
32736 // fullLayout._uid may not exist when we call makeData
32737 opts._clipId = opts._id + '-' + fullLayout._uid;
32738 }
32739
32740 /*
32741 * <g container />
32742 * <rect bg />
32743 * < .... range plot />
32744 * <rect mask-min />
32745 * <rect mask-max />
32746 * <rect slidebox />
32747 * <g grabber-min />
32748 * <rect handle-min />
32749 * <rect grabare-min />
32750 * <g grabber-max />
32751 * <rect handle-max />
32752 * <rect grabare-max />
32753 *
32754 * ...
32755 */
32756
32757 function keyFunction(axisOpts) {
32758 return axisOpts._name;
32759 }
32760
32761 var rangeSliders = fullLayout._infolayer
32762 .selectAll('g.' + constants.containerClassName)
32763 .data(rangeSliderData, keyFunction);
32764
32765 // remove exiting sliders and their corresponding clip paths
32766 rangeSliders.exit().each(function(axisOpts) {
32767 var opts = axisOpts[constants.name];
32768 fullLayout._topdefs.select('#' + opts._clipId).remove();
32769 }).remove();
32770
32771 // return early if no range slider is visible
32772 if(rangeSliderData.length === 0) return;
32773
32774 rangeSliders.enter().append('g')
32775 .classed(constants.containerClassName, true)
32776 .attr('pointer-events', 'all');
32777
32778 // for all present range sliders
32779 rangeSliders.each(function(axisOpts) {
32780 var rangeSlider = d3.select(this);
32781 var opts = axisOpts[constants.name];
32782 var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)];
32783 var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)];
32784
32785 // update range
32786 // Expand slider range to the axis range
32787 if(opts.range) {
32788 var rng = Lib.simpleMap(opts.range, axisOpts.r2l);
32789 var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l);
32790 var newRng;
32791
32792 if(axRng[0] < axRng[1]) {
32793 newRng = [
32794 Math.min(rng[0], axRng[0]),
32795 Math.max(rng[1], axRng[1])
32796 ];
32797 } else {
32798 newRng = [
32799 Math.max(rng[0], axRng[0]),
32800 Math.min(rng[1], axRng[1])
32801 ];
32802 }
32803
32804 opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r);
32805 }
32806
32807 axisOpts.cleanRange('rangeslider.range');
32808
32809 // update range slider dimensions
32810
32811 var gs = fullLayout._size;
32812 var domain = axisOpts.domain;
32813
32814 opts._width = gs.w * (domain[1] - domain[0]);
32815
32816 var x = Math.round(gs.l + (gs.w * domain[0]));
32817
32818 var y = Math.round(
32819 gs.t + gs.h * (1 - axisOpts._counterDomainMin) +
32820 (axisOpts.side === 'bottom' ? axisOpts._depth : 0) +
32821 opts._offsetShift + constants.extraPad
32822 );
32823
32824 rangeSlider.attr('transform', 'translate(' + x + ',' + y + ')');
32825
32826 // update data <--> pixel coordinate conversion methods
32827
32828 opts._rl = Lib.simpleMap(opts.range, axisOpts.r2l);
32829 var rl0 = opts._rl[0];
32830 var rl1 = opts._rl[1];
32831 var drl = rl1 - rl0;
32832
32833 opts.p2d = function(v) {
32834 return (v / opts._width) * drl + rl0;
32835 };
32836
32837 opts.d2p = function(v) {
32838 return (v - rl0) / drl * opts._width;
32839 };
32840
32841 if(axisOpts.rangebreaks) {
32842 var rsBreaks = axisOpts.locateBreaks(rl0, rl1);
32843
32844 if(rsBreaks.length) {
32845 var j, brk;
32846
32847 var lBreaks = 0;
32848 for(j = 0; j < rsBreaks.length; j++) {
32849 brk = rsBreaks[j];
32850 lBreaks += (brk.max - brk.min);
32851 }
32852
32853 // TODO fix for reversed-range axes !!!
32854
32855 // compute slope and piecewise offsets
32856 var m2 = opts._width / (rl1 - rl0 - lBreaks);
32857 var _B = [-m2 * rl0];
32858 for(j = 0; j < rsBreaks.length; j++) {
32859 brk = rsBreaks[j];
32860 _B.push(_B[_B.length - 1] - m2 * (brk.max - brk.min));
32861 }
32862
32863 opts.d2p = function(v) {
32864 var b = _B[0];
32865 for(var j = 0; j < rsBreaks.length; j++) {
32866 var brk = rsBreaks[j];
32867 if(v >= brk.max) b = _B[j + 1];
32868 else if(v < brk.min) break;
32869 }
32870 return b + m2 * v;
32871 };
32872
32873 // fill pixel (i.e. 'p') min/max here,
32874 // to not have to loop through the _rangebreaks twice during `p2d`
32875 for(j = 0; j < rsBreaks.length; j++) {
32876 brk = rsBreaks[j];
32877 brk.pmin = opts.d2p(brk.min);
32878 brk.pmax = opts.d2p(brk.max);
32879 }
32880
32881 opts.p2d = function(v) {
32882 var b = _B[0];
32883 for(var j = 0; j < rsBreaks.length; j++) {
32884 var brk = rsBreaks[j];
32885 if(v >= brk.pmax) b = _B[j + 1];
32886 else if(v < brk.pmin) break;
32887 }
32888 return (v - b) / m2;
32889 };
32890 }
32891 }
32892
32893 if(oppAxisRangeOpts.rangemode !== 'match') {
32894 var range0OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[0]);
32895 var range1OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[1]);
32896 var distOppAxis = range1OppAxis - range0OppAxis;
32897
32898 opts.d2pOppAxis = function(v) {
32899 return (v - range0OppAxis) / distOppAxis * opts._height;
32900 };
32901 }
32902
32903 // update inner nodes
32904
32905 rangeSlider
32906 .call(drawBg, gd, axisOpts, opts)
32907 .call(addClipPath, gd, axisOpts, opts)
32908 .call(drawRangePlot, gd, axisOpts, opts)
32909 .call(drawMasks, gd, axisOpts, opts, oppAxisRangeOpts)
32910 .call(drawSlideBox, gd, axisOpts, opts)
32911 .call(drawGrabbers, gd, axisOpts, opts);
32912
32913 // setup drag element
32914 setupDragElement(rangeSlider, gd, axisOpts, opts);
32915
32916 // update current range
32917 setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts);
32918
32919 // title goes next to range slider instead of tick labels, so
32920 // just take it over and draw it from here
32921 if(axisOpts.side === 'bottom') {
32922 Titles.draw(gd, axisOpts._id + 'title', {
32923 propContainer: axisOpts,
32924 propName: axisOpts._name + '.title',
32925 placeholder: fullLayout._dfltTitle.x,
32926 attributes: {
32927 x: axisOpts._offset + axisOpts._length / 2,
32928 y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.title.font.size,
32929 'text-anchor': 'middle'
32930 }
32931 });
32932 }
32933 });
32934};
32935
32936function setupDragElement(rangeSlider, gd, axisOpts, opts) {
32937 var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node();
32938 var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node();
32939 var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node();
32940
32941 rangeSlider.on('mousedown', function() {
32942 var event = d3.event;
32943 var target = event.target;
32944 var startX = event.clientX;
32945 var offsetX = startX - rangeSlider.node().getBoundingClientRect().left;
32946 var minVal = opts.d2p(axisOpts._rl[0]);
32947 var maxVal = opts.d2p(axisOpts._rl[1]);
32948
32949 var dragCover = dragElement.coverSlip();
32950
32951 dragCover.addEventListener('mousemove', mouseMove);
32952 dragCover.addEventListener('mouseup', mouseUp);
32953
32954 function mouseMove(e) {
32955 var delta = +e.clientX - startX;
32956 var pixelMin, pixelMax, cursor;
32957
32958 switch(target) {
32959 case slideBox:
32960 cursor = 'ew-resize';
32961 pixelMin = minVal + delta;
32962 pixelMax = maxVal + delta;
32963 break;
32964
32965 case grabAreaMin:
32966 cursor = 'col-resize';
32967 pixelMin = minVal + delta;
32968 pixelMax = maxVal;
32969 break;
32970
32971 case grabAreaMax:
32972 cursor = 'col-resize';
32973 pixelMin = minVal;
32974 pixelMax = maxVal + delta;
32975 break;
32976
32977 default:
32978 cursor = 'ew-resize';
32979 pixelMin = offsetX;
32980 pixelMax = offsetX + delta;
32981 break;
32982 }
32983
32984 if(pixelMax < pixelMin) {
32985 var tmp = pixelMax;
32986 pixelMax = pixelMin;
32987 pixelMin = tmp;
32988 }
32989
32990 opts._pixelMin = pixelMin;
32991 opts._pixelMax = pixelMax;
32992
32993 setCursor(d3.select(dragCover), cursor);
32994 setDataRange(rangeSlider, gd, axisOpts, opts);
32995 }
32996
32997 function mouseUp() {
32998 dragCover.removeEventListener('mousemove', mouseMove);
32999 dragCover.removeEventListener('mouseup', mouseUp);
33000 Lib.removeElement(dragCover);
33001 }
33002 });
33003}
33004
33005function setDataRange(rangeSlider, gd, axisOpts, opts) {
33006 function clamp(v) {
33007 return axisOpts.l2r(Lib.constrain(v, opts._rl[0], opts._rl[1]));
33008 }
33009
33010 var dataMin = clamp(opts.p2d(opts._pixelMin));
33011 var dataMax = clamp(opts.p2d(opts._pixelMax));
33012
33013 window.requestAnimationFrame(function() {
33014 Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
33015 });
33016}
33017
33018function setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts) {
33019 var hw2 = constants.handleWidth / 2;
33020
33021 function clamp(v) {
33022 return Lib.constrain(v, 0, opts._width);
33023 }
33024
33025 function clampOppAxis(v) {
33026 return Lib.constrain(v, 0, opts._height);
33027 }
33028
33029 function clampHandle(v) {
33030 return Lib.constrain(v, -hw2, opts._width + hw2);
33031 }
33032
33033 var pixelMin = clamp(opts.d2p(axisOpts._rl[0]));
33034 var pixelMax = clamp(opts.d2p(axisOpts._rl[1]));
33035
33036 rangeSlider.select('rect.' + constants.slideBoxClassName)
33037 .attr('x', pixelMin)
33038 .attr('width', pixelMax - pixelMin);
33039
33040 rangeSlider.select('rect.' + constants.maskMinClassName)
33041 .attr('width', pixelMin);
33042
33043 rangeSlider.select('rect.' + constants.maskMaxClassName)
33044 .attr('x', pixelMax)
33045 .attr('width', opts._width - pixelMax);
33046
33047 if(oppAxisRangeOpts.rangemode !== 'match') {
33048 var pixelMinOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[1]));
33049 var pixelMaxOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[0]));
33050
33051 rangeSlider.select('rect.' + constants.maskMinOppAxisClassName)
33052 .attr('x', pixelMin)
33053 .attr('height', pixelMinOppAxis)
33054 .attr('width', pixelMax - pixelMin);
33055
33056 rangeSlider.select('rect.' + constants.maskMaxOppAxisClassName)
33057 .attr('x', pixelMin)
33058 .attr('y', pixelMaxOppAxis)
33059 .attr('height', opts._height - pixelMaxOppAxis)
33060 .attr('width', pixelMax - pixelMin);
33061
33062 rangeSlider.select('rect.' + constants.slideBoxClassName)
33063 .attr('y', pixelMinOppAxis)
33064 .attr('height', pixelMaxOppAxis - pixelMinOppAxis);
33065 }
33066
33067 // add offset for crispier corners
33068 // https://github.com/plotly/plotly.js/pull/1409
33069 var offset = 0.5;
33070
33071 var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset;
33072 var xMax = Math.round(clampHandle(pixelMax - hw2)) + offset;
33073
33074 rangeSlider.select('g.' + constants.grabberMinClassName)
33075 .attr('transform', 'translate(' + xMin + ',' + offset + ')');
33076
33077 rangeSlider.select('g.' + constants.grabberMaxClassName)
33078 .attr('transform', 'translate(' + xMax + ',' + offset + ')');
33079}
33080
33081function drawBg(rangeSlider, gd, axisOpts, opts) {
33082 var bg = Lib.ensureSingle(rangeSlider, 'rect', constants.bgClassName, function(s) {
33083 s.attr({
33084 x: 0,
33085 y: 0,
33086 'shape-rendering': 'crispEdges'
33087 });
33088 });
33089
33090 var borderCorrect = (opts.borderwidth % 2) === 0 ?
33091 opts.borderwidth :
33092 opts.borderwidth - 1;
33093
33094 var offsetShift = -opts._offsetShift;
33095 var lw = Drawing.crispRound(gd, opts.borderwidth);
33096
33097 bg.attr({
33098 width: opts._width + borderCorrect,
33099 height: opts._height + borderCorrect,
33100 transform: 'translate(' + offsetShift + ',' + offsetShift + ')',
33101 fill: opts.bgcolor,
33102 stroke: opts.bordercolor,
33103 'stroke-width': lw
33104 });
33105}
33106
33107function addClipPath(rangeSlider, gd, axisOpts, opts) {
33108 var fullLayout = gd._fullLayout;
33109
33110 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', opts._clipId, function(s) {
33111 s.append('rect').attr({ x: 0, y: 0 });
33112 });
33113
33114 clipPath.select('rect').attr({
33115 width: opts._width,
33116 height: opts._height
33117 });
33118}
33119
33120function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
33121 var calcData = gd.calcdata;
33122
33123 var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName)
33124 .data(axisOpts._subplotsWith, Lib.identity);
33125
33126 rangePlots.enter().append('g')
33127 .attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
33128 .call(Drawing.setClipUrl, opts._clipId, gd);
33129
33130 rangePlots.order();
33131
33132 rangePlots.exit().remove();
33133
33134 var mainplotinfo;
33135
33136 rangePlots.each(function(id, i) {
33137 var plotgroup = d3.select(this);
33138 var isMainPlot = (i === 0);
33139
33140 var oppAxisOpts = axisIDs.getFromId(gd, id, 'y');
33141 var oppAxisName = oppAxisOpts._name;
33142 var oppAxisRangeOpts = opts[oppAxisName];
33143
33144 var mockFigure = {
33145 data: [],
33146 layout: {
33147 xaxis: {
33148 type: axisOpts.type,
33149 domain: [0, 1],
33150 range: opts.range.slice(),
33151 calendar: axisOpts.calendar
33152 },
33153 width: opts._width,
33154 height: opts._height,
33155 margin: { t: 0, b: 0, l: 0, r: 0 }
33156 },
33157 _context: gd._context
33158 };
33159
33160 if(axisOpts.rangebreaks) {
33161 mockFigure.layout.xaxis.rangebreaks = axisOpts.rangebreaks;
33162 }
33163
33164 mockFigure.layout[oppAxisName] = {
33165 type: oppAxisOpts.type,
33166 domain: [0, 1],
33167 range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(),
33168 calendar: oppAxisOpts.calendar
33169 };
33170
33171 if(oppAxisOpts.rangebreaks) {
33172 mockFigure.layout[oppAxisName].rangebreaks = oppAxisOpts.rangebreaks;
33173 }
33174
33175 Plots.supplyDefaults(mockFigure);
33176
33177 var xa = mockFigure._fullLayout.xaxis;
33178 var ya = mockFigure._fullLayout[oppAxisName];
33179
33180 xa.clearCalc();
33181 xa.setScale();
33182 ya.clearCalc();
33183 ya.setScale();
33184
33185 var plotinfo = {
33186 id: id,
33187 plotgroup: plotgroup,
33188 xaxis: xa,
33189 yaxis: ya,
33190 isRangePlot: true
33191 };
33192
33193 if(isMainPlot) mainplotinfo = plotinfo;
33194 else {
33195 plotinfo.mainplot = 'xy';
33196 plotinfo.mainplotinfo = mainplotinfo;
33197 }
33198
33199 Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id));
33200 });
33201}
33202
33203function filterRangePlotCalcData(calcData, subplotId) {
33204 var out = [];
33205
33206 for(var i = 0; i < calcData.length; i++) {
33207 var calcTrace = calcData[i];
33208 var trace = calcTrace[0].trace;
33209
33210 if(trace.xaxis + trace.yaxis === subplotId) {
33211 out.push(calcTrace);
33212 }
33213 }
33214
33215 return out;
33216}
33217
33218function drawMasks(rangeSlider, gd, axisOpts, opts, oppAxisRangeOpts) {
33219 var maskMin = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinClassName, function(s) {
33220 s.attr({
33221 x: 0,
33222 y: 0,
33223 'shape-rendering': 'crispEdges'
33224 });
33225 });
33226
33227 maskMin
33228 .attr('height', opts._height)
33229 .call(Color.fill, constants.maskColor);
33230
33231 var maskMax = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxClassName, function(s) {
33232 s.attr({
33233 y: 0,
33234 'shape-rendering': 'crispEdges'
33235 });
33236 });
33237
33238 maskMax
33239 .attr('height', opts._height)
33240 .call(Color.fill, constants.maskColor);
33241
33242 // masks used for oppAxis zoom
33243 if(oppAxisRangeOpts.rangemode !== 'match') {
33244 var maskMinOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinOppAxisClassName, function(s) {
33245 s.attr({
33246 y: 0,
33247 'shape-rendering': 'crispEdges'
33248 });
33249 });
33250
33251 maskMinOppAxis
33252 .attr('width', opts._width)
33253 .call(Color.fill, constants.maskOppAxisColor);
33254
33255 var maskMaxOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxOppAxisClassName, function(s) {
33256 s.attr({
33257 y: 0,
33258 'shape-rendering': 'crispEdges'
33259 });
33260 });
33261
33262 maskMaxOppAxis
33263 .attr('width', opts._width)
33264 .style('border-top', constants.maskOppBorder)
33265 .call(Color.fill, constants.maskOppAxisColor);
33266 }
33267}
33268
33269function drawSlideBox(rangeSlider, gd, axisOpts, opts) {
33270 if(gd._context.staticPlot) return;
33271
33272 var slideBox = Lib.ensureSingle(rangeSlider, 'rect', constants.slideBoxClassName, function(s) {
33273 s.attr({
33274 y: 0,
33275 cursor: constants.slideBoxCursor,
33276 'shape-rendering': 'crispEdges'
33277 });
33278 });
33279
33280 slideBox.attr({
33281 height: opts._height,
33282 fill: constants.slideBoxFill
33283 });
33284}
33285
33286function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
33287 // <g grabber />
33288 var grabberMin = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMinClassName);
33289 var grabberMax = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMaxClassName);
33290
33291 // <g handle />
33292 var handleFixAttrs = {
33293 x: 0,
33294 width: constants.handleWidth,
33295 rx: constants.handleRadius,
33296 fill: Color.background,
33297 stroke: Color.defaultLine,
33298 'stroke-width': constants.handleStrokeWidth,
33299 'shape-rendering': 'crispEdges'
33300 };
33301 var handleDynamicAttrs = {
33302 y: Math.round(opts._height / 4),
33303 height: Math.round(opts._height / 2),
33304 };
33305 var handleMin = Lib.ensureSingle(grabberMin, 'rect', constants.handleMinClassName, function(s) {
33306 s.attr(handleFixAttrs);
33307 });
33308 handleMin.attr(handleDynamicAttrs);
33309
33310 var handleMax = Lib.ensureSingle(grabberMax, 'rect', constants.handleMaxClassName, function(s) {
33311 s.attr(handleFixAttrs);
33312 });
33313 handleMax.attr(handleDynamicAttrs);
33314
33315 // <g grabarea />
33316 if(gd._context.staticPlot) return;
33317
33318 var grabAreaFixAttrs = {
33319 width: constants.grabAreaWidth,
33320 x: 0,
33321 y: 0,
33322 fill: constants.grabAreaFill,
33323 cursor: constants.grabAreaCursor
33324 };
33325
33326 var grabAreaMin = Lib.ensureSingle(grabberMin, 'rect', constants.grabAreaMinClassName, function(s) {
33327 s.attr(grabAreaFixAttrs);
33328 });
33329 grabAreaMin.attr('height', opts._height);
33330
33331 var grabAreaMax = Lib.ensureSingle(grabberMax, 'rect', constants.grabAreaMaxClassName, function(s) {
33332 s.attr(grabAreaFixAttrs);
33333 });
33334 grabAreaMax.attr('height', opts._height);
33335}
33336
33337},{"../../lib":177,"../../lib/setcursor":196,"../../plots/cartesian":235,"../../plots/cartesian/axis_ids":225,"../../plots/plots":263,"../../registry":272,"../color":50,"../dragelement":69,"../drawing":72,"../titles":145,"./constants":121,"d3":13}],124:[function(_dereq_,module,exports){
33338/**
33339* Copyright 2012-2020, Plotly, Inc.
33340* All rights reserved.
33341*
33342* This source code is licensed under the MIT license found in the
33343* LICENSE file in the root directory of this source tree.
33344*/
33345
33346'use strict';
33347
33348var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
33349var svgTextUtils = _dereq_('../../lib/svg_text_utils');
33350var constants = _dereq_('./constants');
33351var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
33352var name = constants.name;
33353
33354function isVisible(ax) {
33355 var rangeSlider = ax && ax[name];
33356 return rangeSlider && rangeSlider.visible;
33357}
33358exports.isVisible = isVisible;
33359
33360exports.makeData = function(fullLayout) {
33361 var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true);
33362 var margin = fullLayout.margin;
33363 var rangeSliderData = [];
33364
33365 if(!fullLayout._has('gl2d')) {
33366 for(var i = 0; i < axes.length; i++) {
33367 var ax = axes[i];
33368
33369 if(isVisible(ax)) {
33370 rangeSliderData.push(ax);
33371
33372 var opts = ax[name];
33373 opts._id = name + ax._id;
33374 opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
33375 opts._offsetShift = Math.floor(opts.borderwidth / 2);
33376 }
33377 }
33378 }
33379
33380 fullLayout._rangeSliderData = rangeSliderData;
33381};
33382
33383exports.autoMarginOpts = function(gd, ax) {
33384 var fullLayout = gd._fullLayout;
33385 var opts = ax[name];
33386 var axLetter = ax._id.charAt(0);
33387
33388 var bottomDepth = 0;
33389 var titleHeight = 0;
33390 if(ax.side === 'bottom') {
33391 bottomDepth = ax._depth;
33392 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
33393 // as in rangeslider/draw.js
33394 titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift;
33395 // multi-line extra bump
33396 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
33397 titleHeight += extraLines * ax.title.font.size * LINE_SPACING;
33398 }
33399 }
33400
33401 return {
33402 x: 0,
33403 y: ax._counterDomainMin,
33404 l: 0,
33405 r: 0,
33406 t: 0,
33407 b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight),
33408 pad: constants.extraPad + opts._offsetShift * 2
33409 };
33410};
33411
33412},{"../../constants/alignment":152,"../../lib/svg_text_utils":198,"../../plots/cartesian/axis_ids":225,"./constants":121}],125:[function(_dereq_,module,exports){
33413/**
33414* Copyright 2012-2020, Plotly, Inc.
33415* All rights reserved.
33416*
33417* This source code is licensed under the MIT license found in the
33418* LICENSE file in the root directory of this source tree.
33419*/
33420
33421'use strict';
33422
33423var Lib = _dereq_('../../lib');
33424var attrs = _dereq_('./attributes');
33425var oppAxisAttrs = _dereq_('./oppaxis_attributes');
33426var helpers = _dereq_('./helpers');
33427
33428module.exports = {
33429 moduleType: 'component',
33430 name: 'rangeslider',
33431
33432 schema: {
33433 subplots: {
33434 xaxis: {
33435 rangeslider: Lib.extendFlat({}, attrs, {
33436 yaxis: oppAxisAttrs
33437 })
33438 }
33439 }
33440 },
33441
33442 layoutAttributes: _dereq_('./attributes'),
33443 handleDefaults: _dereq_('./defaults'),
33444 calcAutorange: _dereq_('./calc_autorange'),
33445 draw: _dereq_('./draw'),
33446 isVisible: helpers.isVisible,
33447 makeData: helpers.makeData,
33448 autoMarginOpts: helpers.autoMarginOpts
33449};
33450
33451},{"../../lib":177,"./attributes":119,"./calc_autorange":120,"./defaults":122,"./draw":123,"./helpers":124,"./oppaxis_attributes":126}],126:[function(_dereq_,module,exports){
33452/**
33453* Copyright 2012-2020, Plotly, Inc.
33454* All rights reserved.
33455*
33456* This source code is licensed under the MIT license found in the
33457* LICENSE file in the root directory of this source tree.
33458*/
33459
33460'use strict';
33461
33462module.exports = {
33463 // not really a 'subplot' attribute container,
33464 // but this is the flag we use to denote attributes that
33465 // support yaxis, yaxis2, yaxis3, ... counters
33466 _isSubplotObj: true,
33467
33468 rangemode: {
33469 valType: 'enumerated',
33470 values: ['auto', 'fixed', 'match'],
33471 dflt: 'match',
33472
33473 editType: 'calc',
33474
33475 },
33476 range: {
33477 valType: 'info_array',
33478
33479 items: [
33480 {valType: 'any', editType: 'plot'},
33481 {valType: 'any', editType: 'plot'}
33482 ],
33483 editType: 'plot',
33484
33485 },
33486 editType: 'calc'
33487};
33488
33489},{}],127:[function(_dereq_,module,exports){
33490/**
33491* Copyright 2012-2020, Plotly, Inc.
33492* All rights reserved.
33493*
33494* This source code is licensed under the MIT license found in the
33495* LICENSE file in the root directory of this source tree.
33496*/
33497
33498'use strict';
33499
33500var annAttrs = _dereq_('../annotations/attributes');
33501var scatterLineAttrs = _dereq_('../../traces/scatter/attributes').line;
33502var dash = _dereq_('../drawing/attributes').dash;
33503var extendFlat = _dereq_('../../lib/extend').extendFlat;
33504var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
33505
33506module.exports = templatedArray('shape', {
33507 visible: {
33508 valType: 'boolean',
33509
33510 dflt: true,
33511 editType: 'calc+arraydraw',
33512
33513 },
33514
33515 type: {
33516 valType: 'enumerated',
33517 values: ['circle', 'rect', 'path', 'line'],
33518
33519 editType: 'calc+arraydraw',
33520
33521 },
33522
33523 layer: {
33524 valType: 'enumerated',
33525 values: ['below', 'above'],
33526 dflt: 'above',
33527
33528 editType: 'arraydraw',
33529
33530 },
33531
33532 xref: extendFlat({}, annAttrs.xref, {
33533
33534 }),
33535 xsizemode: {
33536 valType: 'enumerated',
33537 values: ['scaled', 'pixel'],
33538 dflt: 'scaled',
33539
33540 editType: 'calc+arraydraw',
33541
33542 },
33543 xanchor: {
33544 valType: 'any',
33545
33546 editType: 'calc+arraydraw',
33547
33548 },
33549 x0: {
33550 valType: 'any',
33551
33552 editType: 'calc+arraydraw',
33553
33554 },
33555 x1: {
33556 valType: 'any',
33557
33558 editType: 'calc+arraydraw',
33559
33560 },
33561
33562 yref: extendFlat({}, annAttrs.yref, {
33563
33564 }),
33565 ysizemode: {
33566 valType: 'enumerated',
33567 values: ['scaled', 'pixel'],
33568 dflt: 'scaled',
33569
33570 editType: 'calc+arraydraw',
33571
33572 },
33573 yanchor: {
33574 valType: 'any',
33575
33576 editType: 'calc+arraydraw',
33577
33578 },
33579 y0: {
33580 valType: 'any',
33581
33582 editType: 'calc+arraydraw',
33583
33584 },
33585 y1: {
33586 valType: 'any',
33587
33588 editType: 'calc+arraydraw',
33589
33590 },
33591
33592 path: {
33593 valType: 'string',
33594
33595 editType: 'calc+arraydraw',
33596
33597 },
33598
33599 opacity: {
33600 valType: 'number',
33601 min: 0,
33602 max: 1,
33603 dflt: 1,
33604
33605 editType: 'arraydraw',
33606
33607 },
33608 line: {
33609 color: extendFlat({}, scatterLineAttrs.color, {editType: 'arraydraw'}),
33610 width: extendFlat({}, scatterLineAttrs.width, {editType: 'calc+arraydraw'}),
33611 dash: extendFlat({}, dash, {editType: 'arraydraw'}),
33612
33613 editType: 'calc+arraydraw'
33614 },
33615 fillcolor: {
33616 valType: 'color',
33617 dflt: 'rgba(0,0,0,0)',
33618
33619 editType: 'arraydraw',
33620
33621 },
33622 fillrule: {
33623 valType: 'enumerated',
33624 values: ['evenodd', 'nonzero'],
33625 dflt: 'evenodd',
33626
33627 editType: 'arraydraw',
33628
33629 },
33630 editable: {
33631 valType: 'boolean',
33632
33633 dflt: false,
33634 editType: 'calc+arraydraw',
33635
33636 },
33637
33638 editType: 'arraydraw'
33639});
33640
33641},{"../../lib/extend":170,"../../plot_api/plot_template":212,"../../traces/scatter/attributes":294,"../annotations/attributes":35,"../drawing/attributes":71}],128:[function(_dereq_,module,exports){
33642/**
33643* Copyright 2012-2020, Plotly, Inc.
33644* All rights reserved.
33645*
33646* This source code is licensed under the MIT license found in the
33647* LICENSE file in the root directory of this source tree.
33648*/
33649
33650'use strict';
33651
33652var Lib = _dereq_('../../lib');
33653var Axes = _dereq_('../../plots/cartesian/axes');
33654
33655var constants = _dereq_('./constants');
33656var helpers = _dereq_('./helpers');
33657
33658
33659module.exports = function calcAutorange(gd) {
33660 var fullLayout = gd._fullLayout;
33661 var shapeList = Lib.filterVisible(fullLayout.shapes);
33662
33663 if(!shapeList.length || !gd._fullData.length) return;
33664
33665 for(var i = 0; i < shapeList.length; i++) {
33666 var shape = shapeList[i];
33667 shape._extremes = {};
33668
33669 var ax, bounds;
33670
33671 if(shape.xref !== 'paper') {
33672 var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0;
33673 var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1;
33674 ax = Axes.getFromId(gd, shape.xref);
33675
33676 bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX);
33677 if(bounds) {
33678 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape));
33679 }
33680 }
33681
33682 if(shape.yref !== 'paper') {
33683 var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0;
33684 var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1;
33685 ax = Axes.getFromId(gd, shape.yref);
33686
33687 bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY);
33688 if(bounds) {
33689 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape));
33690 }
33691 }
33692 }
33693};
33694
33695function calcXPaddingOptions(shape) {
33696 return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false);
33697}
33698
33699function calcYPaddingOptions(shape) {
33700 return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true);
33701}
33702
33703function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
33704 var ppad = lineWidth / 2;
33705 var axisDirectionReverted = isYAxis;
33706
33707 if(sizeMode === 'pixel') {
33708 var coords = path ?
33709 helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) :
33710 [v0, v1];
33711 var maxValue = Lib.aggNums(Math.max, null, coords);
33712 var minValue = Lib.aggNums(Math.min, null, coords);
33713 var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad;
33714 var afterPad = maxValue > 0 ? maxValue + ppad : ppad;
33715
33716 return {
33717 ppad: ppad,
33718 ppadplus: axisDirectionReverted ? beforePad : afterPad,
33719 ppadminus: axisDirectionReverted ? afterPad : beforePad
33720 };
33721 } else {
33722 return {ppad: ppad};
33723 }
33724}
33725
33726function shapeBounds(ax, v0, v1, path, paramsToUse) {
33727 var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c;
33728
33729 if(v0 !== undefined) return [convertVal(v0), convertVal(v1)];
33730 if(!path) return;
33731
33732 var min = Infinity;
33733 var max = -Infinity;
33734 var segments = path.match(constants.segmentRE);
33735 var i;
33736 var segment;
33737 var drawnParam;
33738 var params;
33739 var val;
33740
33741 if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal);
33742
33743 for(i = 0; i < segments.length; i++) {
33744 segment = segments[i];
33745 drawnParam = paramsToUse[segment.charAt(0)].drawn;
33746 if(drawnParam === undefined) continue;
33747
33748 params = segments[i].substr(1).match(constants.paramRE);
33749 if(!params || params.length < drawnParam) continue;
33750
33751 val = convertVal(params[drawnParam]);
33752 if(val < min) min = val;
33753 if(val > max) max = val;
33754 }
33755 if(max >= min) return [min, max];
33756}
33757
33758},{"../../lib":177,"../../plots/cartesian/axes":222,"./constants":129,"./helpers":138}],129:[function(_dereq_,module,exports){
33759/**
33760* Copyright 2012-2020, Plotly, Inc.
33761* All rights reserved.
33762*
33763* This source code is licensed under the MIT license found in the
33764* LICENSE file in the root directory of this source tree.
33765*/
33766
33767
33768'use strict';
33769
33770
33771module.exports = {
33772 segmentRE: /[MLHVQCTSZ][^MLHVQCTSZ]*/g,
33773 paramRE: /[^\s,]+/g,
33774
33775 // which numbers in each path segment are x (or y) values
33776 // drawn is which param is a drawn point, as opposed to a
33777 // control point (which doesn't count toward autorange.
33778 // TODO: this means curved paths could extend beyond the
33779 // autorange bounds. This is a bit tricky to get right
33780 // unless we revert to bounding boxes, but perhaps there's
33781 // a calculation we could do...)
33782 paramIsX: {
33783 M: {0: true, drawn: 0},
33784 L: {0: true, drawn: 0},
33785 H: {0: true, drawn: 0},
33786 V: {},
33787 Q: {0: true, 2: true, drawn: 2},
33788 C: {0: true, 2: true, 4: true, drawn: 4},
33789 T: {0: true, drawn: 0},
33790 S: {0: true, 2: true, drawn: 2},
33791 // A: {0: true, 5: true},
33792 Z: {}
33793 },
33794
33795 paramIsY: {
33796 M: {1: true, drawn: 1},
33797 L: {1: true, drawn: 1},
33798 H: {},
33799 V: {0: true, drawn: 0},
33800 Q: {1: true, 3: true, drawn: 3},
33801 C: {1: true, 3: true, 5: true, drawn: 5},
33802 T: {1: true, drawn: 1},
33803 S: {1: true, 3: true, drawn: 5},
33804 // A: {1: true, 6: true},
33805 Z: {}
33806 },
33807
33808 numParams: {
33809 M: 2,
33810 L: 2,
33811 H: 1,
33812 V: 1,
33813 Q: 4,
33814 C: 6,
33815 T: 2,
33816 S: 4,
33817 // A: 7,
33818 Z: 0
33819 }
33820};
33821
33822},{}],130:[function(_dereq_,module,exports){
33823/**
33824* Copyright 2012-2020, Plotly, Inc.
33825* All rights reserved.
33826*
33827* This source code is licensed under the MIT license found in the
33828* LICENSE file in the root directory of this source tree.
33829*/
33830
33831
33832'use strict';
33833
33834var Lib = _dereq_('../../lib');
33835var Axes = _dereq_('../../plots/cartesian/axes');
33836var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
33837
33838var attributes = _dereq_('./attributes');
33839var helpers = _dereq_('./helpers');
33840
33841
33842module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
33843 handleArrayContainerDefaults(layoutIn, layoutOut, {
33844 name: 'shapes',
33845 handleItemDefaults: handleShapeDefaults
33846 });
33847};
33848
33849function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
33850 function coerce(attr, dflt) {
33851 return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
33852 }
33853
33854 var visible = coerce('visible');
33855 if(!visible) return;
33856
33857 var path = coerce('path');
33858 var dfltType = path ? 'path' : 'rect';
33859 var shapeType = coerce('type', dfltType);
33860 if(shapeOut.type !== 'path') delete shapeOut.path;
33861
33862 coerce('editable');
33863 coerce('layer');
33864 coerce('opacity');
33865 coerce('fillcolor');
33866 coerce('fillrule');
33867 var lineWidth = coerce('line.width');
33868 if(lineWidth) {
33869 coerce('line.color');
33870 coerce('line.dash');
33871 }
33872
33873 var xSizeMode = coerce('xsizemode');
33874 var ySizeMode = coerce('ysizemode');
33875
33876 // positioning
33877 var axLetters = ['x', 'y'];
33878 for(var i = 0; i < 2; i++) {
33879 var axLetter = axLetters[i];
33880 var attrAnchor = axLetter + 'anchor';
33881 var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
33882 var gdMock = {_fullLayout: fullLayout};
33883 var ax;
33884 var pos2r;
33885 var r2pos;
33886
33887 // xref, yref
33888 var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, '', 'paper');
33889
33890 if(axRef !== 'paper') {
33891 ax = Axes.getFromId(gdMock, axRef);
33892 ax._shapeIndices.push(shapeOut._index);
33893 r2pos = helpers.rangeToShapePosition(ax);
33894 pos2r = helpers.shapePositionToRange(ax);
33895 } else {
33896 pos2r = r2pos = Lib.identity;
33897 }
33898
33899 // Coerce x0, x1, y0, y1
33900 if(shapeType !== 'path') {
33901 var dflt0 = 0.25;
33902 var dflt1 = 0.75;
33903
33904 // hack until V2.0 when log has regular range behavior - make it look like other
33905 // ranges to send to coerce, then put it back after
33906 // this is all to give reasonable default position behavior on log axes, which is
33907 // a pretty unimportant edge case so we could just ignore this.
33908 var attr0 = axLetter + '0';
33909 var attr1 = axLetter + '1';
33910 var in0 = shapeIn[attr0];
33911 var in1 = shapeIn[attr1];
33912 shapeIn[attr0] = pos2r(shapeIn[attr0], true);
33913 shapeIn[attr1] = pos2r(shapeIn[attr1], true);
33914
33915 if(sizeMode === 'pixel') {
33916 coerce(attr0, 0);
33917 coerce(attr1, 10);
33918 } else {
33919 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
33920 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
33921 }
33922
33923 // hack part 2
33924 shapeOut[attr0] = r2pos(shapeOut[attr0]);
33925 shapeOut[attr1] = r2pos(shapeOut[attr1]);
33926 shapeIn[attr0] = in0;
33927 shapeIn[attr1] = in1;
33928 }
33929
33930 // Coerce xanchor and yanchor
33931 if(sizeMode === 'pixel') {
33932 // Hack for log axis described above
33933 var inAnchor = shapeIn[attrAnchor];
33934 shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
33935
33936 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
33937
33938 // Hack part 2
33939 shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
33940 shapeIn[attrAnchor] = inAnchor;
33941 }
33942 }
33943
33944 if(shapeType === 'path') {
33945 coerce('path');
33946 } else {
33947 Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
33948 }
33949}
33950
33951},{"../../lib":177,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":127,"./helpers":138}],131:[function(_dereq_,module,exports){
33952/**
33953* Copyright 2012-2020, Plotly, Inc.
33954* All rights reserved.
33955*
33956* This source code is licensed under the MIT license found in the
33957* LICENSE file in the root directory of this source tree.
33958*/
33959
33960
33961'use strict';
33962
33963var Registry = _dereq_('../../registry');
33964var Lib = _dereq_('../../lib');
33965var Axes = _dereq_('../../plots/cartesian/axes');
33966
33967var readPaths = _dereq_('./draw_newshape/helpers').readPaths;
33968var displayOutlines = _dereq_('./draw_newshape/display_outlines');
33969
33970var clearOutlineControllers = _dereq_('../../plots/cartesian/handle_outline').clearOutlineControllers;
33971
33972var Color = _dereq_('../color');
33973var Drawing = _dereq_('../drawing');
33974var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
33975
33976var dragElement = _dereq_('../dragelement');
33977var setCursor = _dereq_('../../lib/setcursor');
33978
33979var constants = _dereq_('./constants');
33980var helpers = _dereq_('./helpers');
33981
33982
33983// Shapes are stored in gd.layout.shapes, an array of objects
33984// index can point to one item in this array,
33985// or non-numeric to simply add a new one
33986// or -1 to modify all existing
33987// opt can be the full options object, or one key (to be set to value)
33988// or undefined to simply redraw
33989// if opt is blank, val can be 'add' or a full options object to add a new
33990// annotation at that point in the array, or 'remove' to delete this one
33991
33992module.exports = {
33993 draw: draw,
33994 drawOne: drawOne,
33995 eraseActiveShape: eraseActiveShape
33996};
33997
33998function draw(gd) {
33999 var fullLayout = gd._fullLayout;
34000
34001 // Remove previous shapes before drawing new in shapes in fullLayout.shapes
34002 fullLayout._shapeUpperLayer.selectAll('path').remove();
34003 fullLayout._shapeLowerLayer.selectAll('path').remove();
34004
34005 for(var k in fullLayout._plots) {
34006 var shapelayer = fullLayout._plots[k].shapelayer;
34007 if(shapelayer) shapelayer.selectAll('path').remove();
34008 }
34009
34010 for(var i = 0; i < fullLayout.shapes.length; i++) {
34011 if(fullLayout.shapes[i].visible) {
34012 drawOne(gd, i);
34013 }
34014 }
34015
34016 // may need to resurrect this if we put text (LaTeX) in shapes
34017 // return Plots.previousPromises(gd);
34018}
34019
34020function shouldSkipEdits(gd) {
34021 return !!gd._fullLayout._drawing;
34022}
34023
34024function couldHaveActiveShape(gd) {
34025 // for now keep config.editable: true as it was before shape-drawing PR
34026 return !gd._context.edits.shapePosition;
34027}
34028
34029function drawOne(gd, index) {
34030 // remove the existing shape if there is one.
34031 // because indices can change, we need to look in all shape layers
34032 gd._fullLayout._paperdiv
34033 .selectAll('.shapelayer [data-index="' + index + '"]')
34034 .remove();
34035
34036 var o = helpers.makeOptionsAndPlotinfo(gd, index);
34037 var options = o.options;
34038 var plotinfo = o.plotinfo;
34039
34040 // this shape is gone - quit now after deleting it
34041 // TODO: use d3 idioms instead of deleting and redrawing every time
34042 if(!options._input || options.visible === false) return;
34043
34044 if(options.layer !== 'below') {
34045 drawShape(gd._fullLayout._shapeUpperLayer);
34046 } else if(options.xref === 'paper' || options.yref === 'paper') {
34047 drawShape(gd._fullLayout._shapeLowerLayer);
34048 } else {
34049 if(plotinfo._hadPlotinfo) {
34050 var mainPlot = plotinfo.mainplotinfo || plotinfo;
34051 drawShape(mainPlot.shapelayer);
34052 } else {
34053 // Fall back to _shapeLowerLayer in case the requested subplot doesn't exist.
34054 // This can happen if you reference the shape to an x / y axis combination
34055 // that doesn't have any data on it (and layer is below)
34056 drawShape(gd._fullLayout._shapeLowerLayer);
34057 }
34058 }
34059
34060 function drawShape(shapeLayer) {
34061 var d = getPathString(gd, options);
34062 var attrs = {
34063 'data-index': index,
34064 'fill-rule': options.fillrule,
34065 d: d
34066 };
34067
34068 var opacity = options.opacity;
34069 var fillColor = options.fillcolor;
34070 var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)';
34071 var lineWidth = options.line.width;
34072 var lineDash = options.line.dash;
34073 if(!lineWidth && options.editable === true) {
34074 // ensure invisible border to activate the shape
34075 lineWidth = 5;
34076 lineDash = 'solid';
34077 }
34078
34079 var isOpen = d[d.length - 1] !== 'Z';
34080
34081 var isActiveShape = couldHaveActiveShape(gd) &&
34082 options.editable && gd._fullLayout._activeShapeIndex === index;
34083
34084 if(isActiveShape) {
34085 fillColor = isOpen ? 'rgba(0,0,0,0)' :
34086 gd._fullLayout.activeshape.fillcolor;
34087
34088 opacity = gd._fullLayout.activeshape.opacity;
34089 }
34090
34091 var path = shapeLayer.append('path')
34092 .attr(attrs)
34093 .style('opacity', opacity)
34094 .call(Color.stroke, lineColor)
34095 .call(Color.fill, fillColor)
34096 .call(Drawing.dashLine, lineDash, lineWidth);
34097
34098 setClipPath(path, gd, options);
34099
34100 var editHelpers;
34101 if(isActiveShape || gd._context.edits.shapePosition) editHelpers = arrayEditor(gd.layout, 'shapes', options);
34102
34103 if(isActiveShape) {
34104 path.style({
34105 'cursor': 'move',
34106 });
34107
34108 var dragOptions = {
34109 element: path.node(),
34110 plotinfo: plotinfo,
34111 gd: gd,
34112 editHelpers: editHelpers,
34113 isActiveShape: true // i.e. to enable controllers
34114 };
34115
34116 var polygons = readPaths(d, gd);
34117 // display polygons on the screen
34118 displayOutlines(polygons, path, dragOptions);
34119 } else {
34120 if(gd._context.edits.shapePosition) {
34121 setupDragElement(gd, path, options, index, shapeLayer, editHelpers);
34122 } else if(options.editable === true) {
34123 path.style('pointer-events',
34124 (isOpen || Color.opacity(fillColor) * opacity <= 0.5) ? 'stroke' : 'all'
34125 );
34126 }
34127 }
34128
34129 path.node().addEventListener('click', function() { return activateShape(gd, path); });
34130 }
34131}
34132
34133function setClipPath(shapePath, gd, shapeOptions) {
34134 // note that for layer="below" the clipAxes can be different from the
34135 // subplot we're drawing this in. This could cause problems if the shape
34136 // spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
34137 var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '');
34138
34139 Drawing.setClipUrl(
34140 shapePath,
34141 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
34142 gd
34143 );
34144}
34145
34146function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHelpers) {
34147 var MINWIDTH = 10;
34148 var MINHEIGHT = 10;
34149
34150 var xPixelSized = shapeOptions.xsizemode === 'pixel';
34151 var yPixelSized = shapeOptions.ysizemode === 'pixel';
34152 var isLine = shapeOptions.type === 'line';
34153 var isPath = shapeOptions.type === 'path';
34154
34155 var modifyItem = editHelpers.modifyItem;
34156
34157 var x0, y0, x1, y1, xAnchor, yAnchor;
34158 var n0, s0, w0, e0, optN, optS, optW, optE;
34159 var pathIn;
34160
34161 // setup conversion functions
34162 var xa = Axes.getFromId(gd, shapeOptions.xref);
34163 var ya = Axes.getFromId(gd, shapeOptions.yref);
34164 var x2p = helpers.getDataToPixel(gd, xa);
34165 var y2p = helpers.getDataToPixel(gd, ya, true);
34166 var p2x = helpers.getPixelToData(gd, xa);
34167 var p2y = helpers.getPixelToData(gd, ya, true);
34168
34169 var sensoryElement = obtainSensoryElement();
34170 var dragOptions = {
34171 element: sensoryElement.node(),
34172 gd: gd,
34173 prepFn: startDrag,
34174 doneFn: endDrag,
34175 clickFn: abortDrag
34176 };
34177 var dragMode;
34178
34179 dragElement.init(dragOptions);
34180
34181 sensoryElement.node().onmousemove = updateDragMode;
34182
34183 function obtainSensoryElement() {
34184 return isLine ? createLineDragHandles() : shapePath;
34185 }
34186
34187 function createLineDragHandles() {
34188 var minSensoryWidth = 10;
34189 var sensoryWidth = Math.max(shapeOptions.line.width, minSensoryWidth);
34190
34191 // Helper shapes group
34192 // Note that by setting the `data-index` attr, it is ensured that
34193 // the helper group is purged in this modules `draw` function
34194 var g = shapeLayer.append('g')
34195 .attr('data-index', index);
34196
34197 // Helper path for moving
34198 g.append('path')
34199 .attr('d', shapePath.attr('d'))
34200 .style({
34201 'cursor': 'move',
34202 'stroke-width': sensoryWidth,
34203 'stroke-opacity': '0' // ensure not visible
34204 });
34205
34206 // Helper circles for resizing
34207 var circleStyle = {
34208 'fill-opacity': '0' // ensure not visible
34209 };
34210 var circleRadius = Math.max(sensoryWidth / 2, minSensoryWidth);
34211
34212 g.append('circle')
34213 .attr({
34214 'data-line-point': 'start-point',
34215 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0),
34216 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0),
34217 'r': circleRadius
34218 })
34219 .style(circleStyle)
34220 .classed('cursor-grab', true);
34221
34222 g.append('circle')
34223 .attr({
34224 'data-line-point': 'end-point',
34225 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1),
34226 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1),
34227 'r': circleRadius
34228 })
34229 .style(circleStyle)
34230 .classed('cursor-grab', true);
34231
34232 return g;
34233 }
34234
34235 function updateDragMode(evt) {
34236 if(shouldSkipEdits(gd)) {
34237 dragMode = null;
34238 return;
34239 }
34240
34241 if(isLine) {
34242 if(evt.target.tagName === 'path') {
34243 dragMode = 'move';
34244 } else {
34245 dragMode = evt.target.attributes['data-line-point'].value === 'start-point' ?
34246 'resize-over-start-point' : 'resize-over-end-point';
34247 }
34248 } else {
34249 // element might not be on screen at time of setup,
34250 // so obtain bounding box here
34251 var dragBBox = dragOptions.element.getBoundingClientRect();
34252
34253 // choose 'move' or 'resize'
34254 // based on initial position of cursor within the drag element
34255 var w = dragBBox.right - dragBBox.left;
34256 var h = dragBBox.bottom - dragBBox.top;
34257 var x = evt.clientX - dragBBox.left;
34258 var y = evt.clientY - dragBBox.top;
34259 var cursor = (!isPath && w > MINWIDTH && h > MINHEIGHT && !evt.shiftKey) ?
34260 dragElement.getCursor(x / w, 1 - y / h) :
34261 'move';
34262
34263 setCursor(shapePath, cursor);
34264
34265 // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
34266 dragMode = cursor.split('-')[0];
34267 }
34268 }
34269
34270 function startDrag(evt) {
34271 if(shouldSkipEdits(gd)) return;
34272
34273 // setup update strings and initial values
34274 if(xPixelSized) {
34275 xAnchor = x2p(shapeOptions.xanchor);
34276 }
34277 if(yPixelSized) {
34278 yAnchor = y2p(shapeOptions.yanchor);
34279 }
34280
34281 if(shapeOptions.type === 'path') {
34282 pathIn = shapeOptions.path;
34283 } else {
34284 x0 = xPixelSized ? shapeOptions.x0 : x2p(shapeOptions.x0);
34285 y0 = yPixelSized ? shapeOptions.y0 : y2p(shapeOptions.y0);
34286 x1 = xPixelSized ? shapeOptions.x1 : x2p(shapeOptions.x1);
34287 y1 = yPixelSized ? shapeOptions.y1 : y2p(shapeOptions.y1);
34288 }
34289
34290 if(x0 < x1) {
34291 w0 = x0;
34292 optW = 'x0';
34293 e0 = x1;
34294 optE = 'x1';
34295 } else {
34296 w0 = x1;
34297 optW = 'x1';
34298 e0 = x0;
34299 optE = 'x0';
34300 }
34301
34302 // For fixed size shapes take opposing direction of y-axis into account.
34303 // Hint: For data sized shapes this is done by the y2p function.
34304 if((!yPixelSized && y0 < y1) || (yPixelSized && y0 > y1)) {
34305 n0 = y0;
34306 optN = 'y0';
34307 s0 = y1;
34308 optS = 'y1';
34309 } else {
34310 n0 = y1;
34311 optN = 'y1';
34312 s0 = y0;
34313 optS = 'y0';
34314 }
34315
34316 // setup dragMode and the corresponding handler
34317 updateDragMode(evt);
34318 renderVisualCues(shapeLayer, shapeOptions);
34319 deactivateClipPathTemporarily(shapePath, shapeOptions, gd);
34320 dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
34321 dragOptions.altKey = evt.altKey;
34322 }
34323
34324 function endDrag() {
34325 if(shouldSkipEdits(gd)) return;
34326
34327 setCursor(shapePath);
34328 removeVisualCues(shapeLayer);
34329
34330 // Don't rely on clipPath being activated during re-layout
34331 setClipPath(shapePath, gd, shapeOptions);
34332 Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj());
34333 }
34334
34335 function abortDrag() {
34336 if(shouldSkipEdits(gd)) return;
34337
34338 removeVisualCues(shapeLayer);
34339 }
34340
34341 function moveShape(dx, dy) {
34342 if(shapeOptions.type === 'path') {
34343 var noOp = function(coord) { return coord; };
34344 var moveX = noOp;
34345 var moveY = noOp;
34346
34347 if(xPixelSized) {
34348 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
34349 } else {
34350 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
34351 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
34352 }
34353
34354 if(yPixelSized) {
34355 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
34356 } else {
34357 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
34358 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
34359 }
34360
34361 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
34362 } else {
34363 if(xPixelSized) {
34364 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
34365 } else {
34366 modifyItem('x0', shapeOptions.x0 = p2x(x0 + dx));
34367 modifyItem('x1', shapeOptions.x1 = p2x(x1 + dx));
34368 }
34369
34370 if(yPixelSized) {
34371 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
34372 } else {
34373 modifyItem('y0', shapeOptions.y0 = p2y(y0 + dy));
34374 modifyItem('y1', shapeOptions.y1 = p2y(y1 + dy));
34375 }
34376 }
34377
34378 shapePath.attr('d', getPathString(gd, shapeOptions));
34379 renderVisualCues(shapeLayer, shapeOptions);
34380 }
34381
34382 function resizeShape(dx, dy) {
34383 if(isPath) {
34384 // TODO: implement path resize, don't forget to update dragMode code
34385 var noOp = function(coord) { return coord; };
34386 var moveX = noOp;
34387 var moveY = noOp;
34388
34389 if(xPixelSized) {
34390 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
34391 } else {
34392 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
34393 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
34394 }
34395
34396 if(yPixelSized) {
34397 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
34398 } else {
34399 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
34400 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
34401 }
34402
34403 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
34404 } else if(isLine) {
34405 if(dragMode === 'resize-over-start-point') {
34406 var newX0 = x0 + dx;
34407 var newY0 = yPixelSized ? y0 - dy : y0 + dy;
34408 modifyItem('x0', shapeOptions.x0 = xPixelSized ? newX0 : p2x(newX0));
34409 modifyItem('y0', shapeOptions.y0 = yPixelSized ? newY0 : p2y(newY0));
34410 } else if(dragMode === 'resize-over-end-point') {
34411 var newX1 = x1 + dx;
34412 var newY1 = yPixelSized ? y1 - dy : y1 + dy;
34413 modifyItem('x1', shapeOptions.x1 = xPixelSized ? newX1 : p2x(newX1));
34414 modifyItem('y1', shapeOptions.y1 = yPixelSized ? newY1 : p2y(newY1));
34415 }
34416 } else {
34417 var has = function(str) { return dragMode.indexOf(str) !== -1; };
34418 var hasN = has('n');
34419 var hasS = has('s');
34420 var hasW = has('w');
34421 var hasE = has('e');
34422
34423 var newN = hasN ? n0 + dy : n0;
34424 var newS = hasS ? s0 + dy : s0;
34425 var newW = hasW ? w0 + dx : w0;
34426 var newE = hasE ? e0 + dx : e0;
34427
34428 if(yPixelSized) {
34429 // Do things in opposing direction for y-axis.
34430 // Hint: for data-sized shapes the reversal of axis direction is done in p2y.
34431 if(hasN) newN = n0 - dy;
34432 if(hasS) newS = s0 - dy;
34433 }
34434
34435 // Update shape eventually. Again, be aware of the
34436 // opposing direction of the y-axis of fixed size shapes.
34437 if(
34438 (!yPixelSized && newS - newN > MINHEIGHT) ||
34439 (yPixelSized && newN - newS > MINHEIGHT)
34440 ) {
34441 modifyItem(optN, shapeOptions[optN] = yPixelSized ? newN : p2y(newN));
34442 modifyItem(optS, shapeOptions[optS] = yPixelSized ? newS : p2y(newS));
34443 }
34444 if(newE - newW > MINWIDTH) {
34445 modifyItem(optW, shapeOptions[optW] = xPixelSized ? newW : p2x(newW));
34446 modifyItem(optE, shapeOptions[optE] = xPixelSized ? newE : p2x(newE));
34447 }
34448 }
34449
34450 shapePath.attr('d', getPathString(gd, shapeOptions));
34451 renderVisualCues(shapeLayer, shapeOptions);
34452 }
34453
34454 function renderVisualCues(shapeLayer, shapeOptions) {
34455 if(xPixelSized || yPixelSized) {
34456 renderAnchor();
34457 }
34458
34459 function renderAnchor() {
34460 var isNotPath = shapeOptions.type !== 'path';
34461
34462 // d3 join with dummy data to satisfy d3 data-binding
34463 var visualCues = shapeLayer.selectAll('.visual-cue').data([0]);
34464
34465 // Enter
34466 var strokeWidth = 1;
34467 visualCues.enter()
34468 .append('path')
34469 .attr({
34470 'fill': '#fff',
34471 'fill-rule': 'evenodd',
34472 'stroke': '#000',
34473 'stroke-width': strokeWidth
34474 })
34475 .classed('visual-cue', true);
34476
34477 // Update
34478 var posX = x2p(
34479 xPixelSized ?
34480 shapeOptions.xanchor :
34481 Lib.midRange(
34482 isNotPath ?
34483 [shapeOptions.x0, shapeOptions.x1] :
34484 helpers.extractPathCoords(shapeOptions.path, constants.paramIsX))
34485 );
34486 var posY = y2p(
34487 yPixelSized ?
34488 shapeOptions.yanchor :
34489 Lib.midRange(
34490 isNotPath ?
34491 [shapeOptions.y0, shapeOptions.y1] :
34492 helpers.extractPathCoords(shapeOptions.path, constants.paramIsY))
34493 );
34494
34495 posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth);
34496 posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth);
34497
34498 if(xPixelSized && yPixelSized) {
34499 var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
34500 'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
34501 visualCues.attr('d', crossPath);
34502 } else if(xPixelSized) {
34503 var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) +
34504 'v18 h2 v-18 Z';
34505 visualCues.attr('d', vBarPath);
34506 } else {
34507 var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
34508 'h18 v2 h-18 Z';
34509 visualCues.attr('d', hBarPath);
34510 }
34511 }
34512 }
34513
34514 function removeVisualCues(shapeLayer) {
34515 shapeLayer.selectAll('.visual-cue').remove();
34516 }
34517
34518 function deactivateClipPathTemporarily(shapePath, shapeOptions, gd) {
34519 var xref = shapeOptions.xref;
34520 var yref = shapeOptions.yref;
34521 var xa = Axes.getFromId(gd, xref);
34522 var ya = Axes.getFromId(gd, yref);
34523
34524 var clipAxes = '';
34525 if(xref !== 'paper' && !xa.autorange) clipAxes += xref;
34526 if(yref !== 'paper' && !ya.autorange) clipAxes += yref;
34527
34528 Drawing.setClipUrl(
34529 shapePath,
34530 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
34531 gd
34532 );
34533 }
34534}
34535
34536function getPathString(gd, options) {
34537 var type = options.type;
34538 var xa = Axes.getFromId(gd, options.xref);
34539 var ya = Axes.getFromId(gd, options.yref);
34540 var gs = gd._fullLayout._size;
34541 var x2r, x2p, y2r, y2p;
34542 var x0, x1, y0, y1;
34543
34544 if(xa) {
34545 x2r = helpers.shapePositionToRange(xa);
34546 x2p = function(v) { return xa._offset + xa.r2p(x2r(v, true)); };
34547 } else {
34548 x2p = function(v) { return gs.l + gs.w * v; };
34549 }
34550
34551 if(ya) {
34552 y2r = helpers.shapePositionToRange(ya);
34553 y2p = function(v) { return ya._offset + ya.r2p(y2r(v, true)); };
34554 } else {
34555 y2p = function(v) { return gs.t + gs.h * (1 - v); };
34556 }
34557
34558 if(type === 'path') {
34559 if(xa && xa.type === 'date') x2p = helpers.decodeDate(x2p);
34560 if(ya && ya.type === 'date') y2p = helpers.decodeDate(y2p);
34561 return convertPath(options, x2p, y2p);
34562 }
34563
34564 if(options.xsizemode === 'pixel') {
34565 var xAnchorPos = x2p(options.xanchor);
34566 x0 = xAnchorPos + options.x0;
34567 x1 = xAnchorPos + options.x1;
34568 } else {
34569 x0 = x2p(options.x0);
34570 x1 = x2p(options.x1);
34571 }
34572
34573 if(options.ysizemode === 'pixel') {
34574 var yAnchorPos = y2p(options.yanchor);
34575 y0 = yAnchorPos - options.y0;
34576 y1 = yAnchorPos - options.y1;
34577 } else {
34578 y0 = y2p(options.y0);
34579 y1 = y2p(options.y1);
34580 }
34581
34582 if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1;
34583 if(type === 'rect') return 'M' + x0 + ',' + y0 + 'H' + x1 + 'V' + y1 + 'H' + x0 + 'Z';
34584
34585 // circle
34586 var cx = (x0 + x1) / 2;
34587 var cy = (y0 + y1) / 2;
34588 var rx = Math.abs(cx - x0);
34589 var ry = Math.abs(cy - y0);
34590 var rArc = 'A' + rx + ',' + ry;
34591 var rightPt = (cx + rx) + ',' + cy;
34592 var topPt = cx + ',' + (cy - ry);
34593 return 'M' + rightPt + rArc + ' 0 1,1 ' + topPt +
34594 rArc + ' 0 0,1 ' + rightPt + 'Z';
34595}
34596
34597
34598function convertPath(options, x2p, y2p) {
34599 var pathIn = options.path;
34600 var xSizemode = options.xsizemode;
34601 var ySizemode = options.ysizemode;
34602 var xAnchor = options.xanchor;
34603 var yAnchor = options.yanchor;
34604
34605 return pathIn.replace(constants.segmentRE, function(segment) {
34606 var paramNumber = 0;
34607 var segmentType = segment.charAt(0);
34608 var xParams = constants.paramIsX[segmentType];
34609 var yParams = constants.paramIsY[segmentType];
34610 var nParams = constants.numParams[segmentType];
34611
34612 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
34613 if(xParams[paramNumber]) {
34614 if(xSizemode === 'pixel') param = x2p(xAnchor) + Number(param);
34615 else param = x2p(param);
34616 } else if(yParams[paramNumber]) {
34617 if(ySizemode === 'pixel') param = y2p(yAnchor) - Number(param);
34618 else param = y2p(param);
34619 }
34620 paramNumber++;
34621
34622 if(paramNumber > nParams) param = 'X';
34623 return param;
34624 });
34625
34626 if(paramNumber > nParams) {
34627 paramString = paramString.replace(/[\s,]*X.*/, '');
34628 Lib.log('Ignoring extra params in segment ' + segment);
34629 }
34630
34631 return segmentType + paramString;
34632 });
34633}
34634
34635function movePath(pathIn, moveX, moveY) {
34636 return pathIn.replace(constants.segmentRE, function(segment) {
34637 var paramNumber = 0;
34638 var segmentType = segment.charAt(0);
34639 var xParams = constants.paramIsX[segmentType];
34640 var yParams = constants.paramIsY[segmentType];
34641 var nParams = constants.numParams[segmentType];
34642
34643 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
34644 if(paramNumber >= nParams) return param;
34645
34646 if(xParams[paramNumber]) param = moveX(param);
34647 else if(yParams[paramNumber]) param = moveY(param);
34648
34649 paramNumber++;
34650
34651 return param;
34652 });
34653
34654 return segmentType + paramString;
34655 });
34656}
34657
34658function activateShape(gd, path) {
34659 if(!couldHaveActiveShape(gd)) return;
34660
34661 var element = path.node();
34662 var id = +element.getAttribute('data-index');
34663 if(id >= 0) {
34664 // deactivate if already active
34665 if(id === gd._fullLayout._activeShapeIndex) {
34666 deactivateShape(gd);
34667 return;
34668 }
34669
34670 gd._fullLayout._activeShapeIndex = id;
34671 gd._fullLayout._deactivateShape = deactivateShape;
34672 draw(gd);
34673 }
34674}
34675
34676function deactivateShape(gd) {
34677 if(!couldHaveActiveShape(gd)) return;
34678
34679 var id = gd._fullLayout._activeShapeIndex;
34680 if(id >= 0) {
34681 clearOutlineControllers(gd);
34682 delete gd._fullLayout._activeShapeIndex;
34683 draw(gd);
34684 }
34685}
34686
34687function eraseActiveShape(gd) {
34688 if(!couldHaveActiveShape(gd)) return;
34689
34690 clearOutlineControllers(gd);
34691
34692 var id = gd._fullLayout._activeShapeIndex;
34693 var shapes = (gd.layout || {}).shapes || [];
34694 if(id < shapes.length) {
34695 var newShapes = [];
34696 for(var q = 0; q < shapes.length; q++) {
34697 if(q !== id) {
34698 newShapes.push(shapes[q]);
34699 }
34700 }
34701
34702 delete gd._fullLayout._activeShapeIndex;
34703
34704 Registry.call('_guiRelayout', gd, {
34705 shapes: newShapes
34706 });
34707 }
34708}
34709
34710},{"../../lib":177,"../../lib/setcursor":196,"../../plot_api/plot_template":212,"../../plots/cartesian/axes":222,"../../plots/cartesian/handle_outline":232,"../../registry":272,"../color":50,"../dragelement":69,"../drawing":72,"./constants":129,"./draw_newshape/display_outlines":135,"./draw_newshape/helpers":136,"./helpers":138}],132:[function(_dereq_,module,exports){
34711/**
34712* Copyright 2012-2020, Plotly, Inc.
34713* All rights reserved.
34714*
34715* This source code is licensed under the MIT license found in the
34716* LICENSE file in the root directory of this source tree.
34717*/
34718
34719'use strict';
34720
34721var dash = _dereq_('../../drawing/attributes').dash;
34722var extendFlat = _dereq_('../../../lib/extend').extendFlat;
34723
34724module.exports = {
34725 newshape: {
34726 line: {
34727 color: {
34728 valType: 'color',
34729 editType: 'none',
34730
34731
34732 },
34733 width: {
34734 valType: 'number',
34735 min: 0,
34736 dflt: 4,
34737
34738 editType: 'none',
34739
34740 },
34741 dash: extendFlat({}, dash, {
34742 dflt: 'solid',
34743 editType: 'none'
34744 }),
34745
34746 editType: 'none'
34747 },
34748 fillcolor: {
34749 valType: 'color',
34750 dflt: 'rgba(0,0,0,0)',
34751
34752 editType: 'none',
34753
34754 },
34755 fillrule: {
34756 valType: 'enumerated',
34757 values: ['evenodd', 'nonzero'],
34758 dflt: 'evenodd',
34759
34760 editType: 'none',
34761
34762 },
34763 opacity: {
34764 valType: 'number',
34765 min: 0,
34766 max: 1,
34767 dflt: 1,
34768
34769 editType: 'none',
34770
34771 },
34772 layer: {
34773 valType: 'enumerated',
34774 values: ['below', 'above'],
34775 dflt: 'above',
34776
34777 editType: 'none',
34778
34779 },
34780 drawdirection: {
34781 valType: 'enumerated',
34782
34783 values: ['ortho', 'horizontal', 'vertical', 'diagonal'],
34784 dflt: 'diagonal',
34785 editType: 'none',
34786
34787 },
34788
34789 editType: 'none'
34790 },
34791
34792 activeshape: {
34793 fillcolor: {
34794 valType: 'color',
34795 dflt: 'rgb(255,0,255)',
34796
34797 editType: 'none',
34798
34799 },
34800 opacity: {
34801 valType: 'number',
34802 min: 0,
34803 max: 1,
34804 dflt: 0.5,
34805
34806 editType: 'none',
34807
34808 },
34809 editType: 'none'
34810 }
34811};
34812
34813},{"../../../lib/extend":170,"../../drawing/attributes":71}],133:[function(_dereq_,module,exports){
34814/**
34815* Copyright 2012-2020, Plotly, Inc.
34816* All rights reserved.
34817*
34818* This source code is licensed under the MIT license found in the
34819* LICENSE file in the root directory of this source tree.
34820*/
34821
34822'use strict';
34823
34824var CIRCLE_SIDES = 32; // should be divisible by 4
34825
34826module.exports = {
34827 CIRCLE_SIDES: CIRCLE_SIDES,
34828 i000: 0,
34829 i090: CIRCLE_SIDES / 4,
34830 i180: CIRCLE_SIDES / 2,
34831 i270: CIRCLE_SIDES / 4 * 3,
34832 cos45: Math.cos(Math.PI / 4),
34833 sin45: Math.sin(Math.PI / 4),
34834 SQRT2: Math.sqrt(2)
34835};
34836
34837},{}],134:[function(_dereq_,module,exports){
34838/**
34839* Copyright 2012-2020, Plotly, Inc.
34840* All rights reserved.
34841*
34842* This source code is licensed under the MIT license found in the
34843* LICENSE file in the root directory of this source tree.
34844*/
34845
34846
34847'use strict';
34848
34849var Color = _dereq_('../../color');
34850
34851
34852module.exports = function supplyDrawNewShapeDefaults(layoutIn, layoutOut, coerce) {
34853 coerce('newshape.drawdirection');
34854 coerce('newshape.layer');
34855 coerce('newshape.fillcolor');
34856 coerce('newshape.fillrule');
34857 coerce('newshape.opacity');
34858 var newshapeLineWidth = coerce('newshape.line.width');
34859 if(newshapeLineWidth) {
34860 var bgcolor = (layoutIn || {}).plot_bgcolor || '#FFF';
34861 coerce('newshape.line.color', Color.contrast(bgcolor));
34862 coerce('newshape.line.dash');
34863 }
34864
34865 coerce('activeshape.fillcolor');
34866 coerce('activeshape.opacity');
34867};
34868
34869},{"../../color":50}],135:[function(_dereq_,module,exports){
34870/**
34871* Copyright 2012-2020, Plotly, Inc.
34872* All rights reserved.
34873*
34874* This source code is licensed under the MIT license found in the
34875* LICENSE file in the root directory of this source tree.
34876*/
34877
34878
34879'use strict';
34880
34881var dragElement = _dereq_('../../dragelement');
34882var dragHelpers = _dereq_('../../dragelement/helpers');
34883var drawMode = dragHelpers.drawMode;
34884
34885var Registry = _dereq_('../../../registry');
34886
34887var constants = _dereq_('./constants');
34888var i000 = constants.i000;
34889var i090 = constants.i090;
34890var i180 = constants.i180;
34891var i270 = constants.i270;
34892
34893var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
34894var clearOutlineControllers = handleOutline.clearOutlineControllers;
34895
34896var helpers = _dereq_('./helpers');
34897var pointsShapeRectangle = helpers.pointsShapeRectangle;
34898var pointsShapeEllipse = helpers.pointsShapeEllipse;
34899var writePaths = helpers.writePaths;
34900var newShapes = _dereq_('./newshapes');
34901
34902module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) {
34903 if(!nCalls) nCalls = 0;
34904
34905 var gd = dragOptions.gd;
34906
34907 function redraw() {
34908 // recursive call
34909 displayOutlines(polygons, outlines, dragOptions, nCalls++);
34910
34911 if(pointsShapeEllipse(polygons[0])) {
34912 update({redrawing: true});
34913 }
34914 }
34915
34916 function update(opts) {
34917 dragOptions.isActiveShape = false; // i.e. to disable controllers
34918
34919 var updateObject = newShapes(outlines, dragOptions);
34920 if(Object.keys(updateObject).length) {
34921 Registry.call((opts || {}).redrawing ? 'relayout' : '_guiRelayout', gd, updateObject);
34922 }
34923 }
34924
34925
34926 var isActiveShape = dragOptions.isActiveShape;
34927 var fullLayout = gd._fullLayout;
34928 var zoomLayer = fullLayout._zoomlayer;
34929
34930 var dragmode = dragOptions.dragmode;
34931 var isDrawMode = drawMode(dragmode);
34932
34933 if(isDrawMode) gd._fullLayout._drawing = true;
34934 else if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd);
34935
34936 // make outline
34937 outlines.attr('d', writePaths(polygons));
34938
34939 // add controllers
34940 var vertexDragOptions;
34941 var shapeDragOptions;
34942 var indexI; // cell index
34943 var indexJ; // vertex or cell-controller index
34944 var copyPolygons;
34945
34946 if(isActiveShape && !nCalls) {
34947 copyPolygons = recordPositions([], polygons);
34948
34949 var g = zoomLayer.append('g').attr('class', 'outline-controllers');
34950 addVertexControllers(g);
34951 addShapeControllers();
34952 }
34953
34954 function startDragVertex(evt) {
34955 indexI = +evt.srcElement.getAttribute('data-i');
34956 indexJ = +evt.srcElement.getAttribute('data-j');
34957
34958 vertexDragOptions[indexI][indexJ].moveFn = moveVertexController;
34959 }
34960
34961 function moveVertexController(dx, dy) {
34962 if(!polygons.length) return;
34963
34964 var x0 = copyPolygons[indexI][indexJ][1];
34965 var y0 = copyPolygons[indexI][indexJ][2];
34966
34967 var cell = polygons[indexI];
34968 var len = cell.length;
34969 if(pointsShapeRectangle(cell)) {
34970 for(var q = 0; q < len; q++) {
34971 if(q === indexJ) continue;
34972
34973 // move other corners of rectangle
34974 var pos = cell[q];
34975
34976 if(pos[1] === cell[indexJ][1]) {
34977 pos[1] = x0 + dx;
34978 }
34979
34980 if(pos[2] === cell[indexJ][2]) {
34981 pos[2] = y0 + dy;
34982 }
34983 }
34984 // move the corner
34985 cell[indexJ][1] = x0 + dx;
34986 cell[indexJ][2] = y0 + dy;
34987
34988 if(!pointsShapeRectangle(cell)) {
34989 // reject result to rectangles with ensure areas
34990 for(var j = 0; j < len; j++) {
34991 for(var k = 0; k < cell[j].length; k++) {
34992 cell[j][k] = copyPolygons[indexI][j][k];
34993 }
34994 }
34995 }
34996 } else { // other polylines
34997 cell[indexJ][1] = x0 + dx;
34998 cell[indexJ][2] = y0 + dy;
34999 }
35000
35001 redraw();
35002 }
35003
35004 function endDragVertexController() {
35005 update();
35006 }
35007
35008 function removeVertex() {
35009 if(!polygons.length) return;
35010 if(!polygons[indexI]) return;
35011 if(!polygons[indexI].length) return;
35012
35013 var newPolygon = [];
35014 for(var j = 0; j < polygons[indexI].length; j++) {
35015 if(j !== indexJ) {
35016 newPolygon.push(
35017 polygons[indexI][j]
35018 );
35019 }
35020 }
35021
35022 if(newPolygon.length > 1 && !(
35023 newPolygon.length === 2 && newPolygon[1][0] === 'Z')
35024 ) {
35025 if(indexJ === 0) {
35026 newPolygon[0][0] = 'M';
35027 }
35028
35029 polygons[indexI] = newPolygon;
35030
35031 redraw();
35032 update();
35033 }
35034 }
35035
35036 function clickVertexController(numClicks, evt) {
35037 if(numClicks === 2) {
35038 indexI = +evt.srcElement.getAttribute('data-i');
35039 indexJ = +evt.srcElement.getAttribute('data-j');
35040
35041 var cell = polygons[indexI];
35042 if(
35043 !pointsShapeRectangle(cell) &&
35044 !pointsShapeEllipse(cell)
35045 ) {
35046 removeVertex();
35047 }
35048 }
35049 }
35050
35051 function addVertexControllers(g) {
35052 vertexDragOptions = [];
35053
35054 for(var i = 0; i < polygons.length; i++) {
35055 var cell = polygons[i];
35056
35057 var onRect = pointsShapeRectangle(cell);
35058 var onEllipse = !onRect && pointsShapeEllipse(cell);
35059
35060 vertexDragOptions[i] = [];
35061 for(var j = 0; j < cell.length; j++) {
35062 if(cell[j][0] === 'Z') continue;
35063
35064 if(onEllipse &&
35065 j !== i000 &&
35066 j !== i090 &&
35067 j !== i180 &&
35068 j !== i270
35069 ) {
35070 continue;
35071 }
35072
35073 var x = cell[j][1];
35074 var y = cell[j][2];
35075
35076 var vertex = g.append('circle')
35077 .classed('cursor-grab', true)
35078 .attr('data-i', i)
35079 .attr('data-j', j)
35080 .attr('cx', x)
35081 .attr('cy', y)
35082 .attr('r', 4)
35083 .style({
35084 'mix-blend-mode': 'luminosity',
35085 fill: 'black',
35086 stroke: 'white',
35087 'stroke-width': 1
35088 });
35089
35090 vertexDragOptions[i][j] = {
35091 element: vertex.node(),
35092 gd: gd,
35093 prepFn: startDragVertex,
35094 doneFn: endDragVertexController,
35095 clickFn: clickVertexController
35096 };
35097
35098 dragElement.init(vertexDragOptions[i][j]);
35099 }
35100 }
35101 }
35102
35103 function moveShape(dx, dy) {
35104 if(!polygons.length) return;
35105
35106 for(var i = 0; i < polygons.length; i++) {
35107 for(var j = 0; j < polygons[i].length; j++) {
35108 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
35109 polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx;
35110 polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy;
35111 }
35112 }
35113 }
35114 }
35115
35116 function moveShapeController(dx, dy) {
35117 moveShape(dx, dy);
35118
35119 redraw();
35120 }
35121
35122 function startDragShapeController(evt) {
35123 indexI = +evt.srcElement.getAttribute('data-i');
35124 if(!indexI) indexI = 0; // ensure non-existing move button get zero index
35125
35126 shapeDragOptions[indexI].moveFn = moveShapeController;
35127 }
35128
35129 function endDragShapeController() {
35130 update();
35131 }
35132
35133 function addShapeControllers() {
35134 shapeDragOptions = [];
35135
35136 if(!polygons.length) return;
35137
35138 var i = 0;
35139 shapeDragOptions[i] = {
35140 element: outlines[0][0],
35141 gd: gd,
35142 prepFn: startDragShapeController,
35143 doneFn: endDragShapeController
35144 };
35145
35146 dragElement.init(shapeDragOptions[i]);
35147 }
35148};
35149
35150function recordPositions(polygonsOut, polygonsIn) {
35151 for(var i = 0; i < polygonsIn.length; i++) {
35152 var cell = polygonsIn[i];
35153 polygonsOut[i] = [];
35154 for(var j = 0; j < cell.length; j++) {
35155 polygonsOut[i][j] = [];
35156 for(var k = 0; k < cell[j].length; k++) {
35157 polygonsOut[i][j][k] = cell[j][k];
35158 }
35159 }
35160 }
35161 return polygonsOut;
35162}
35163
35164},{"../../../plots/cartesian/handle_outline":232,"../../../registry":272,"../../dragelement":69,"../../dragelement/helpers":68,"./constants":133,"./helpers":136,"./newshapes":137}],136:[function(_dereq_,module,exports){
35165/**
35166* Copyright 2012-2020, Plotly, Inc.
35167* All rights reserved.
35168*
35169* This source code is licensed under the MIT license found in the
35170* LICENSE file in the root directory of this source tree.
35171*/
35172
35173
35174'use strict';
35175
35176var parseSvgPath = _dereq_('parse-svg-path');
35177
35178var constants = _dereq_('./constants');
35179var CIRCLE_SIDES = constants.CIRCLE_SIDES;
35180var SQRT2 = constants.SQRT2;
35181
35182var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
35183var p2r = cartesianHelpers.p2r;
35184var r2p = cartesianHelpers.r2p;
35185
35186var iC = [0, 3, 4, 5, 6, 1, 2];
35187var iQS = [0, 3, 4, 1, 2];
35188
35189exports.writePaths = function(polygons) {
35190 var nI = polygons.length;
35191 if(!nI) return 'M0,0Z';
35192
35193 var str = '';
35194 for(var i = 0; i < nI; i++) {
35195 var nJ = polygons[i].length;
35196 for(var j = 0; j < nJ; j++) {
35197 var w = polygons[i][j][0];
35198 if(w === 'Z') {
35199 str += 'Z';
35200 } else {
35201 var nK = polygons[i][j].length;
35202 for(var k = 0; k < nK; k++) {
35203 var realK = k;
35204 if(w === 'Q' || w === 'S') {
35205 realK = iQS[k];
35206 } else if(w === 'C') {
35207 realK = iC[k];
35208 }
35209
35210 str += polygons[i][j][realK];
35211 if(k > 0 && k < nK - 1) {
35212 str += ',';
35213 }
35214 }
35215 }
35216 }
35217 }
35218
35219 return str;
35220};
35221
35222exports.readPaths = function(str, gd, plotinfo, isActiveShape) {
35223 var cmd = parseSvgPath(str);
35224
35225 var polys = [];
35226 var n = -1;
35227 var newPoly = function() {
35228 n++;
35229 polys[n] = [];
35230 };
35231
35232 var k;
35233 var x = 0;
35234 var y = 0;
35235 var initX;
35236 var initY;
35237 var recStart = function() {
35238 initX = x;
35239 initY = y;
35240 };
35241
35242 recStart();
35243 for(var i = 0; i < cmd.length; i++) {
35244 var newPos = [];
35245
35246 var x1, x2, y1, y2; // i.e. extra params for curves
35247
35248 var c = cmd[i][0];
35249 var w = c;
35250 switch(c) {
35251 case 'M':
35252 newPoly();
35253 x = +cmd[i][1];
35254 y = +cmd[i][2];
35255 newPos.push([w, x, y]);
35256
35257 recStart();
35258 break;
35259
35260 case 'Q':
35261 case 'S':
35262 x1 = +cmd[i][1];
35263 y1 = +cmd[i][2];
35264 x = +cmd[i][3];
35265 y = +cmd[i][4];
35266 newPos.push([w, x, y, x1, y1]); // -> iQS order
35267 break;
35268
35269 case 'C':
35270 x1 = +cmd[i][1];
35271 y1 = +cmd[i][2];
35272 x2 = +cmd[i][3];
35273 y2 = +cmd[i][4];
35274 x = +cmd[i][5];
35275 y = +cmd[i][6];
35276 newPos.push([w, x, y, x1, y1, x2, y2]); // -> iC order
35277 break;
35278
35279 case 'T':
35280 case 'L':
35281 x = +cmd[i][1];
35282 y = +cmd[i][2];
35283 newPos.push([w, x, y]);
35284 break;
35285
35286 case 'H':
35287 w = 'L'; // convert to line (for now)
35288 x = +cmd[i][1];
35289 newPos.push([w, x, y]);
35290 break;
35291
35292 case 'V':
35293 w = 'L'; // convert to line (for now)
35294 y = +cmd[i][1];
35295 newPos.push([w, x, y]);
35296 break;
35297
35298 case 'A':
35299 w = 'L'; // convert to line to handle circle
35300 var rx = +cmd[i][1];
35301 var ry = +cmd[i][2];
35302 if(!+cmd[i][4]) {
35303 rx = -rx;
35304 ry = -ry;
35305 }
35306
35307 var cenX = x - rx;
35308 var cenY = y;
35309 for(k = 1; k <= CIRCLE_SIDES / 2; k++) {
35310 var t = 2 * Math.PI * k / CIRCLE_SIDES;
35311 newPos.push([
35312 w,
35313 cenX + rx * Math.cos(t),
35314 cenY + ry * Math.sin(t)
35315 ]);
35316 }
35317 break;
35318
35319 case 'Z':
35320 if(x !== initX || y !== initY) {
35321 x = initX;
35322 y = initY;
35323 newPos.push([w, x, y]);
35324 }
35325 break;
35326 }
35327
35328 var domain = (plotinfo || {}).domain;
35329 var size = gd._fullLayout._size;
35330 var xPixelSized = plotinfo && plotinfo.xsizemode === 'pixel';
35331 var yPixelSized = plotinfo && plotinfo.ysizemode === 'pixel';
35332 var noOffset = isActiveShape === false;
35333
35334 for(var j = 0; j < newPos.length; j++) {
35335 for(k = 0; k + 2 < 7; k += 2) {
35336 var _x = newPos[j][k + 1];
35337 var _y = newPos[j][k + 2];
35338
35339 if(_x === undefined || _y === undefined) continue;
35340 // keep track of end point for Z
35341 x = _x;
35342 y = _y;
35343
35344 if(plotinfo) {
35345 if(plotinfo.xaxis && plotinfo.xaxis.p2r) {
35346 if(noOffset) _x -= plotinfo.xaxis._offset;
35347 if(xPixelSized) {
35348 _x = r2p(plotinfo.xaxis, plotinfo.xanchor) + _x;
35349 } else {
35350 _x = p2r(plotinfo.xaxis, _x);
35351 }
35352 } else {
35353 if(noOffset) _x -= size.l;
35354 if(domain) _x = domain.x[0] + _x / size.w;
35355 else _x = _x / size.w;
35356 }
35357
35358 if(plotinfo.yaxis && plotinfo.yaxis.p2r) {
35359 if(noOffset) _y -= plotinfo.yaxis._offset;
35360 if(yPixelSized) {
35361 _y = r2p(plotinfo.yaxis, plotinfo.yanchor) - _y;
35362 } else {
35363 _y = p2r(plotinfo.yaxis, _y);
35364 }
35365 } else {
35366 if(noOffset) _y -= size.t;
35367 if(domain) _y = domain.y[1] - _y / size.h;
35368 else _y = 1 - _y / size.h;
35369 }
35370 }
35371
35372 newPos[j][k + 1] = _x;
35373 newPos[j][k + 2] = _y;
35374 }
35375 polys[n].push(
35376 newPos[j].slice()
35377 );
35378 }
35379 }
35380
35381 return polys;
35382};
35383
35384function almostEq(a, b) {
35385 return Math.abs(a - b) <= 1e-6;
35386}
35387
35388function dist(a, b) {
35389 var dx = b[1] - a[1];
35390 var dy = b[2] - a[2];
35391 return Math.sqrt(
35392 dx * dx +
35393 dy * dy
35394 );
35395}
35396
35397exports.pointsShapeRectangle = function(cell) {
35398 var len = cell.length;
35399 if(len !== 5) return false;
35400
35401 for(var j = 1; j < 3; j++) {
35402 var e01 = cell[0][j] - cell[1][j];
35403 var e32 = cell[3][j] - cell[2][j];
35404
35405 if(!almostEq(e01, e32)) return false;
35406
35407 var e03 = cell[0][j] - cell[3][j];
35408 var e12 = cell[1][j] - cell[2][j];
35409 if(!almostEq(e03, e12)) return false;
35410 }
35411
35412 // N.B. rotated rectangles are not valid rects since rotation is not supported in shapes for now.
35413 if(
35414 !almostEq(cell[0][1], cell[1][1]) &&
35415 !almostEq(cell[0][1], cell[3][1])
35416 ) return false;
35417
35418 // reject cases with zero area
35419 return !!(
35420 dist(cell[0], cell[1]) *
35421 dist(cell[0], cell[3])
35422 );
35423};
35424
35425exports.pointsShapeEllipse = function(cell) {
35426 var len = cell.length;
35427 if(len !== CIRCLE_SIDES + 1) return false;
35428
35429 // opposite diagonals should be the same
35430 len = CIRCLE_SIDES;
35431 for(var i = 0; i < len; i++) {
35432 var k = (len * 2 - i) % len;
35433
35434 var k2 = (len / 2 + k) % len;
35435 var i2 = (len / 2 + i) % len;
35436
35437 if(!almostEq(
35438 dist(cell[i], cell[i2]),
35439 dist(cell[k], cell[k2])
35440 )) return false;
35441 }
35442 return true;
35443};
35444
35445exports.handleEllipse = function(isEllipse, start, end) {
35446 if(!isEllipse) return [start, end]; // i.e. case of line
35447
35448 var pos = exports.ellipseOver({
35449 x0: start[0],
35450 y0: start[1],
35451 x1: end[0],
35452 y1: end[1]
35453 });
35454
35455 var cx = (pos.x1 + pos.x0) / 2;
35456 var cy = (pos.y1 + pos.y0) / 2;
35457 var rx = (pos.x1 - pos.x0) / 2;
35458 var ry = (pos.y1 - pos.y0) / 2;
35459
35460 // make a circle when one dimension is zero
35461 if(!rx) rx = ry = ry / SQRT2;
35462 if(!ry) ry = rx = rx / SQRT2;
35463
35464 var cell = [];
35465 for(var i = 0; i < CIRCLE_SIDES; i++) {
35466 var t = i * 2 * Math.PI / CIRCLE_SIDES;
35467 cell.push([
35468 cx + rx * Math.cos(t),
35469 cy + ry * Math.sin(t),
35470 ]);
35471 }
35472 return cell;
35473};
35474
35475exports.ellipseOver = function(pos) {
35476 var x0 = pos.x0;
35477 var y0 = pos.y0;
35478 var x1 = pos.x1;
35479 var y1 = pos.y1;
35480
35481 var dx = x1 - x0;
35482 var dy = y1 - y0;
35483
35484 x0 -= dx;
35485 y0 -= dy;
35486
35487 var cx = (x0 + x1) / 2;
35488 var cy = (y0 + y1) / 2;
35489
35490 var scale = SQRT2;
35491 dx *= scale;
35492 dy *= scale;
35493
35494 return {
35495 x0: cx - dx,
35496 y0: cy - dy,
35497 x1: cx + dx,
35498 y1: cy + dy
35499 };
35500};
35501
35502},{"../../../plots/cartesian/helpers":233,"./constants":133,"parse-svg-path":22}],137:[function(_dereq_,module,exports){
35503/**
35504* Copyright 2012-2020, Plotly, Inc.
35505* All rights reserved.
35506*
35507* This source code is licensed under the MIT license found in the
35508* LICENSE file in the root directory of this source tree.
35509*/
35510
35511
35512'use strict';
35513
35514var dragHelpers = _dereq_('../../dragelement/helpers');
35515var drawMode = dragHelpers.drawMode;
35516var openMode = dragHelpers.openMode;
35517
35518var constants = _dereq_('./constants');
35519var i000 = constants.i000;
35520var i090 = constants.i090;
35521var i180 = constants.i180;
35522var i270 = constants.i270;
35523var cos45 = constants.cos45;
35524var sin45 = constants.sin45;
35525
35526var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
35527var p2r = cartesianHelpers.p2r;
35528var r2p = cartesianHelpers.r2p;
35529
35530var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
35531var clearSelect = handleOutline.clearSelect;
35532
35533var helpers = _dereq_('./helpers');
35534var readPaths = helpers.readPaths;
35535var writePaths = helpers.writePaths;
35536var ellipseOver = helpers.ellipseOver;
35537
35538
35539module.exports = function newShapes(outlines, dragOptions) {
35540 if(!outlines.length) return;
35541 var e = outlines[0][0]; // pick first
35542 if(!e) return;
35543 var d = e.getAttribute('d');
35544
35545 var gd = dragOptions.gd;
35546 var drwStyle = gd._fullLayout.newshape;
35547
35548 var plotinfo = dragOptions.plotinfo;
35549 var xaxis = plotinfo.xaxis;
35550 var yaxis = plotinfo.yaxis;
35551 var xPaper = !!plotinfo.domain || !plotinfo.xaxis;
35552 var yPaper = !!plotinfo.domain || !plotinfo.yaxis;
35553
35554 var isActiveShape = dragOptions.isActiveShape;
35555 var dragmode = dragOptions.dragmode;
35556
35557 var shapes = (gd.layout || {}).shapes || [];
35558
35559 if(!drawMode(dragmode) && isActiveShape !== undefined) {
35560 var id = gd._fullLayout._activeShapeIndex;
35561 if(id < shapes.length) {
35562 switch(gd._fullLayout.shapes[id].type) {
35563 case 'rect':
35564 dragmode = 'drawrect';
35565 break;
35566 case 'circle':
35567 dragmode = 'drawcircle';
35568 break;
35569 case 'line':
35570 dragmode = 'drawline';
35571 break;
35572 case 'path':
35573 var path = shapes[id].path || '';
35574 if(path[path.length - 1] === 'Z') {
35575 dragmode = 'drawclosedpath';
35576 } else {
35577 dragmode = 'drawopenpath';
35578 }
35579 break;
35580 }
35581 }
35582 }
35583
35584 var isOpenMode = openMode(dragmode);
35585
35586 var polygons = readPaths(d, gd, plotinfo, isActiveShape);
35587
35588 var newShape = {
35589 editable: true,
35590
35591 xref: xPaper ? 'paper' : xaxis._id,
35592 yref: yPaper ? 'paper' : yaxis._id,
35593
35594 layer: drwStyle.layer,
35595 opacity: drwStyle.opacity,
35596 line: {
35597 color: drwStyle.line.color,
35598 width: drwStyle.line.width,
35599 dash: drwStyle.line.dash
35600 }
35601 };
35602
35603 if(!isOpenMode) {
35604 newShape.fillcolor = drwStyle.fillcolor;
35605 newShape.fillrule = drwStyle.fillrule;
35606 }
35607
35608 var cell;
35609 // line, rect and circle can be in one cell
35610 // only define cell if there is single cell
35611 if(polygons.length === 1) cell = polygons[0];
35612
35613 if(
35614 cell &&
35615 dragmode === 'drawrect'
35616 ) {
35617 newShape.type = 'rect';
35618 newShape.x0 = cell[0][1];
35619 newShape.y0 = cell[0][2];
35620 newShape.x1 = cell[2][1];
35621 newShape.y1 = cell[2][2];
35622 } else if(
35623 cell &&
35624 dragmode === 'drawline'
35625 ) {
35626 newShape.type = 'line';
35627 newShape.x0 = cell[0][1];
35628 newShape.y0 = cell[0][2];
35629 newShape.x1 = cell[1][1];
35630 newShape.y1 = cell[1][2];
35631 } else if(
35632 cell &&
35633 dragmode === 'drawcircle'
35634 ) {
35635 newShape.type = 'circle'; // an ellipse!
35636
35637 var xA = cell[i000][1];
35638 var xB = cell[i090][1];
35639 var xC = cell[i180][1];
35640 var xD = cell[i270][1];
35641
35642 var yA = cell[i000][2];
35643 var yB = cell[i090][2];
35644 var yC = cell[i180][2];
35645 var yD = cell[i270][2];
35646
35647 var xDateOrLog = plotinfo.xaxis && (
35648 plotinfo.xaxis.type === 'date' ||
35649 plotinfo.xaxis.type === 'log'
35650 );
35651
35652 var yDateOrLog = plotinfo.yaxis && (
35653 plotinfo.yaxis.type === 'date' ||
35654 plotinfo.yaxis.type === 'log'
35655 );
35656
35657 if(xDateOrLog) {
35658 xA = r2p(plotinfo.xaxis, xA);
35659 xB = r2p(plotinfo.xaxis, xB);
35660 xC = r2p(plotinfo.xaxis, xC);
35661 xD = r2p(plotinfo.xaxis, xD);
35662 }
35663
35664 if(yDateOrLog) {
35665 yA = r2p(plotinfo.yaxis, yA);
35666 yB = r2p(plotinfo.yaxis, yB);
35667 yC = r2p(plotinfo.yaxis, yC);
35668 yD = r2p(plotinfo.yaxis, yD);
35669 }
35670
35671 var x0 = (xB + xD) / 2;
35672 var y0 = (yA + yC) / 2;
35673 var rx = (xD - xB + xC - xA) / 2;
35674 var ry = (yD - yB + yC - yA) / 2;
35675 var pos = ellipseOver({
35676 x0: x0,
35677 y0: y0,
35678 x1: x0 + rx * cos45,
35679 y1: y0 + ry * sin45
35680 });
35681
35682 if(xDateOrLog) {
35683 pos.x0 = p2r(plotinfo.xaxis, pos.x0);
35684 pos.x1 = p2r(plotinfo.xaxis, pos.x1);
35685 }
35686
35687 if(yDateOrLog) {
35688 pos.y0 = p2r(plotinfo.yaxis, pos.y0);
35689 pos.y1 = p2r(plotinfo.yaxis, pos.y1);
35690 }
35691
35692 newShape.x0 = pos.x0;
35693 newShape.y0 = pos.y0;
35694 newShape.x1 = pos.x1;
35695 newShape.y1 = pos.y1;
35696 } else {
35697 newShape.type = 'path';
35698 if(xaxis && yaxis) fixDatesForPaths(polygons, xaxis, yaxis);
35699 newShape.path = writePaths(polygons);
35700 cell = null;
35701 }
35702
35703 clearSelect(gd);
35704
35705 var editHelpers = dragOptions.editHelpers;
35706 var modifyItem = (editHelpers || {}).modifyItem;
35707
35708 var allShapes = [];
35709 for(var q = 0; q < shapes.length; q++) {
35710 var beforeEdit = gd._fullLayout.shapes[q];
35711 allShapes[q] = beforeEdit._input;
35712
35713 if(
35714 isActiveShape !== undefined &&
35715 q === gd._fullLayout._activeShapeIndex
35716 ) {
35717 var afterEdit = newShape;
35718
35719 switch(beforeEdit.type) {
35720 case 'line':
35721 case 'rect':
35722 case 'circle':
35723 modifyItem('x0', afterEdit.x0);
35724 modifyItem('x1', afterEdit.x1);
35725 modifyItem('y0', afterEdit.y0);
35726 modifyItem('y1', afterEdit.y1);
35727 break;
35728
35729 case 'path':
35730 modifyItem('path', afterEdit.path);
35731 break;
35732 }
35733 }
35734 }
35735
35736 if(isActiveShape === undefined) {
35737 allShapes.push(newShape); // add new shape
35738 return allShapes;
35739 }
35740
35741 return editHelpers ? editHelpers.getUpdateObj() : {};
35742};
35743
35744function fixDatesForPaths(polygons, xaxis, yaxis) {
35745 var xIsDate = xaxis.type === 'date';
35746 var yIsDate = yaxis.type === 'date';
35747 if(!xIsDate && !yIsDate) return polygons;
35748
35749 for(var i = 0; i < polygons.length; i++) {
35750 for(var j = 0; j < polygons[i].length; j++) {
35751 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
35752 if(xIsDate) polygons[i][j][k + 1] = polygons[i][j][k + 1].replace(' ', '_');
35753 if(yIsDate) polygons[i][j][k + 2] = polygons[i][j][k + 2].replace(' ', '_');
35754 }
35755 }
35756 }
35757
35758 return polygons;
35759}
35760
35761},{"../../../plots/cartesian/handle_outline":232,"../../../plots/cartesian/helpers":233,"../../dragelement/helpers":68,"./constants":133,"./helpers":136}],138:[function(_dereq_,module,exports){
35762/**
35763* Copyright 2012-2020, Plotly, Inc.
35764* All rights reserved.
35765*
35766* This source code is licensed under the MIT license found in the
35767* LICENSE file in the root directory of this source tree.
35768*/
35769
35770
35771'use strict';
35772
35773var constants = _dereq_('./constants');
35774
35775var Lib = _dereq_('../../lib');
35776
35777// special position conversion functions... category axis positions can't be
35778// specified by their data values, because they don't make a continuous mapping.
35779// so these have to be specified in terms of the category serial numbers,
35780// but can take fractional values. Other axis types we specify position based on
35781// the actual data values.
35782// TODO: in V2.0 (when log axis ranges are in data units) range and shape position
35783// will be identical, so rangeToShapePosition and shapePositionToRange can be
35784// removed entirely.
35785
35786exports.rangeToShapePosition = function(ax) {
35787 return (ax.type === 'log') ? ax.r2d : function(v) { return v; };
35788};
35789
35790exports.shapePositionToRange = function(ax) {
35791 return (ax.type === 'log') ? ax.d2r : function(v) { return v; };
35792};
35793
35794exports.decodeDate = function(convertToPx) {
35795 return function(v) {
35796 if(v.replace) v = v.replace('_', ' ');
35797 return convertToPx(v);
35798 };
35799};
35800
35801exports.encodeDate = function(convertToDate) {
35802 return function(v) { return convertToDate(v).replace(' ', '_'); };
35803};
35804
35805exports.extractPathCoords = function(path, paramsToUse) {
35806 var extractedCoordinates = [];
35807
35808 var segments = path.match(constants.segmentRE);
35809 segments.forEach(function(segment) {
35810 var relevantParamIdx = paramsToUse[segment.charAt(0)].drawn;
35811 if(relevantParamIdx === undefined) return;
35812
35813 var params = segment.substr(1).match(constants.paramRE);
35814 if(!params || params.length < relevantParamIdx) return;
35815
35816 extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx]));
35817 });
35818
35819 return extractedCoordinates;
35820};
35821
35822exports.getDataToPixel = function(gd, axis, isVertical) {
35823 var gs = gd._fullLayout._size;
35824 var dataToPixel;
35825
35826 if(axis) {
35827 var d2r = exports.shapePositionToRange(axis);
35828
35829 dataToPixel = function(v) {
35830 return axis._offset + axis.r2p(d2r(v, true));
35831 };
35832
35833 if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel);
35834 } else if(isVertical) {
35835 dataToPixel = function(v) { return gs.t + gs.h * (1 - v); };
35836 } else {
35837 dataToPixel = function(v) { return gs.l + gs.w * v; };
35838 }
35839
35840 return dataToPixel;
35841};
35842
35843exports.getPixelToData = function(gd, axis, isVertical) {
35844 var gs = gd._fullLayout._size;
35845 var pixelToData;
35846
35847 if(axis) {
35848 var r2d = exports.rangeToShapePosition(axis);
35849 pixelToData = function(p) { return r2d(axis.p2r(p - axis._offset)); };
35850 } else if(isVertical) {
35851 pixelToData = function(p) { return 1 - (p - gs.t) / gs.h; };
35852 } else {
35853 pixelToData = function(p) { return (p - gs.l) / gs.w; };
35854 }
35855
35856 return pixelToData;
35857};
35858
35859/**
35860 * Based on the given stroke width, rounds the passed
35861 * position value to represent either a full or half pixel.
35862 *
35863 * In case of an odd stroke width (e.g. 1), this measure ensures
35864 * that a stroke positioned at the returned position isn't rendered
35865 * blurry due to anti-aliasing.
35866 *
35867 * In case of an even stroke width (e.g. 2), this measure ensures
35868 * that the position value is transformed to a full pixel value
35869 * so that anti-aliasing doesn't take effect either.
35870 *
35871 * @param {number} pos The raw position value to be transformed
35872 * @param {number} strokeWidth The stroke width
35873 * @returns {number} either an integer or a .5 decimal number
35874 */
35875exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) {
35876 var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1;
35877 var posValAsInt = Math.round(pos);
35878
35879 return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt;
35880};
35881
35882exports.makeOptionsAndPlotinfo = function(gd, index) {
35883 var options = gd._fullLayout.shapes[index] || {};
35884
35885 var plotinfo = gd._fullLayout._plots[options.xref + options.yref];
35886 var hasPlotinfo = !!plotinfo;
35887 if(hasPlotinfo) {
35888 plotinfo._hadPlotinfo = true;
35889 } else {
35890 plotinfo = {};
35891 if(options.xref && options.xref !== 'paper') plotinfo.xaxis = gd._fullLayout[options.xref + 'axis'];
35892 if(options.yref && options.yref !== 'paper') plotinfo.yaxis = gd._fullLayout[options.yref + 'axis'];
35893 }
35894
35895 plotinfo.xsizemode = options.xsizemode;
35896 plotinfo.ysizemode = options.ysizemode;
35897 plotinfo.xanchor = options.xanchor;
35898 plotinfo.yanchor = options.yanchor;
35899
35900 return {
35901 options: options,
35902 plotinfo: plotinfo
35903 };
35904};
35905
35906},{"../../lib":177,"./constants":129}],139:[function(_dereq_,module,exports){
35907/**
35908* Copyright 2012-2020, Plotly, Inc.
35909* All rights reserved.
35910*
35911* This source code is licensed under the MIT license found in the
35912* LICENSE file in the root directory of this source tree.
35913*/
35914
35915
35916'use strict';
35917
35918var drawModule = _dereq_('./draw');
35919
35920module.exports = {
35921 moduleType: 'component',
35922 name: 'shapes',
35923
35924 layoutAttributes: _dereq_('./attributes'),
35925 supplyLayoutDefaults: _dereq_('./defaults'),
35926 supplyDrawNewShapeDefaults: _dereq_('./draw_newshape/defaults'),
35927 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'),
35928
35929 calcAutorange: _dereq_('./calc_autorange'),
35930 draw: drawModule.draw,
35931 drawOne: drawModule.drawOne
35932};
35933
35934},{"../../plots/cartesian/include_components":234,"./attributes":127,"./calc_autorange":128,"./defaults":130,"./draw":131,"./draw_newshape/defaults":134}],140:[function(_dereq_,module,exports){
35935/**
35936* Copyright 2012-2020, Plotly, Inc.
35937* All rights reserved.
35938*
35939* This source code is licensed under the MIT license found in the
35940* LICENSE file in the root directory of this source tree.
35941*/
35942
35943'use strict';
35944
35945var fontAttrs = _dereq_('../../plots/font_attributes');
35946var padAttrs = _dereq_('../../plots/pad_attributes');
35947var extendDeepAll = _dereq_('../../lib/extend').extendDeepAll;
35948var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
35949var animationAttrs = _dereq_('../../plots/animation_attributes');
35950var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
35951var constants = _dereq_('./constants');
35952
35953var stepsAttrs = templatedArray('step', {
35954 visible: {
35955 valType: 'boolean',
35956
35957 dflt: true,
35958
35959 },
35960 method: {
35961 valType: 'enumerated',
35962 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
35963 dflt: 'restyle',
35964
35965
35966 },
35967 args: {
35968 valType: 'info_array',
35969
35970 freeLength: true,
35971 items: [
35972 { valType: 'any' },
35973 { valType: 'any' },
35974 { valType: 'any' }
35975 ],
35976
35977 },
35978 label: {
35979 valType: 'string',
35980
35981
35982 },
35983 value: {
35984 valType: 'string',
35985
35986
35987 },
35988 execute: {
35989 valType: 'boolean',
35990
35991 dflt: true,
35992
35993 }
35994});
35995
35996module.exports = overrideAll(templatedArray('slider', {
35997 visible: {
35998 valType: 'boolean',
35999
36000 dflt: true,
36001
36002 },
36003
36004 active: {
36005 valType: 'number',
36006
36007 min: 0,
36008 dflt: 0,
36009
36010 },
36011
36012 steps: stepsAttrs,
36013
36014 lenmode: {
36015 valType: 'enumerated',
36016 values: ['fraction', 'pixels'],
36017
36018 dflt: 'fraction',
36019
36020 },
36021 len: {
36022 valType: 'number',
36023 min: 0,
36024 dflt: 1,
36025
36026
36027 },
36028 x: {
36029 valType: 'number',
36030 min: -2,
36031 max: 3,
36032 dflt: 0,
36033
36034
36035 },
36036 pad: extendDeepAll(padAttrs({editType: 'arraydraw'}), {
36037
36038 }, {t: {dflt: 20}}),
36039 xanchor: {
36040 valType: 'enumerated',
36041 values: ['auto', 'left', 'center', 'right'],
36042 dflt: 'left',
36043
36044
36045 },
36046 y: {
36047 valType: 'number',
36048 min: -2,
36049 max: 3,
36050 dflt: 0,
36051
36052
36053 },
36054 yanchor: {
36055 valType: 'enumerated',
36056 values: ['auto', 'top', 'middle', 'bottom'],
36057 dflt: 'top',
36058
36059
36060 },
36061
36062 transition: {
36063 duration: {
36064 valType: 'number',
36065
36066 min: 0,
36067 dflt: 150,
36068
36069 },
36070 easing: {
36071 valType: 'enumerated',
36072 values: animationAttrs.transition.easing.values,
36073
36074 dflt: 'cubic-in-out',
36075
36076 }
36077 },
36078
36079 currentvalue: {
36080 visible: {
36081 valType: 'boolean',
36082
36083 dflt: true,
36084
36085 },
36086
36087 xanchor: {
36088 valType: 'enumerated',
36089 values: ['left', 'center', 'right'],
36090 dflt: 'left',
36091
36092
36093 },
36094
36095 offset: {
36096 valType: 'number',
36097 dflt: 10,
36098
36099
36100 },
36101
36102 prefix: {
36103 valType: 'string',
36104
36105
36106 },
36107
36108 suffix: {
36109 valType: 'string',
36110
36111
36112 },
36113
36114 font: fontAttrs({
36115
36116 })
36117 },
36118
36119 font: fontAttrs({
36120
36121 }),
36122
36123 activebgcolor: {
36124 valType: 'color',
36125
36126 dflt: constants.gripBgActiveColor,
36127
36128 },
36129 bgcolor: {
36130 valType: 'color',
36131
36132 dflt: constants.railBgColor,
36133
36134 },
36135 bordercolor: {
36136 valType: 'color',
36137 dflt: constants.railBorderColor,
36138
36139
36140 },
36141 borderwidth: {
36142 valType: 'number',
36143 min: 0,
36144 dflt: constants.railBorderWidth,
36145
36146
36147 },
36148 ticklen: {
36149 valType: 'number',
36150 min: 0,
36151 dflt: constants.tickLength,
36152
36153
36154 },
36155 tickcolor: {
36156 valType: 'color',
36157 dflt: constants.tickColor,
36158
36159
36160 },
36161 tickwidth: {
36162 valType: 'number',
36163 min: 0,
36164 dflt: 1,
36165
36166
36167 },
36168 minorticklen: {
36169 valType: 'number',
36170 min: 0,
36171 dflt: constants.minorTickLength,
36172
36173
36174 }
36175}), 'arraydraw', 'from-root');
36176
36177},{"../../lib/extend":170,"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../../plots/animation_attributes":217,"../../plots/font_attributes":250,"../../plots/pad_attributes":262,"./constants":141}],141:[function(_dereq_,module,exports){
36178/**
36179* Copyright 2012-2020, Plotly, Inc.
36180* All rights reserved.
36181*
36182* This source code is licensed under the MIT license found in the
36183* LICENSE file in the root directory of this source tree.
36184*/
36185
36186
36187'use strict';
36188
36189
36190module.exports = {
36191
36192 // layout attribute name
36193 name: 'sliders',
36194
36195 // class names
36196 containerClassName: 'slider-container',
36197 groupClassName: 'slider-group',
36198 inputAreaClass: 'slider-input-area',
36199 railRectClass: 'slider-rail-rect',
36200 railTouchRectClass: 'slider-rail-touch-rect',
36201 gripRectClass: 'slider-grip-rect',
36202 tickRectClass: 'slider-tick-rect',
36203 inputProxyClass: 'slider-input-proxy',
36204 labelsClass: 'slider-labels',
36205 labelGroupClass: 'slider-label-group',
36206 labelClass: 'slider-label',
36207 currentValueClass: 'slider-current-value',
36208
36209 railHeight: 5,
36210
36211 // DOM attribute name in button group keeping track
36212 // of active update menu
36213 menuIndexAttrName: 'slider-active-index',
36214
36215 // id root pass to Plots.autoMargin
36216 autoMarginIdRoot: 'slider-',
36217
36218 // min item width / height
36219 minWidth: 30,
36220 minHeight: 30,
36221
36222 // padding around item text
36223 textPadX: 40,
36224
36225 // arrow offset off right edge
36226 arrowOffsetX: 4,
36227
36228 railRadius: 2,
36229 railWidth: 5,
36230 railBorder: 4,
36231 railBorderWidth: 1,
36232 railBorderColor: '#bec8d9',
36233 railBgColor: '#f8fafc',
36234
36235 // The distance of the rail from the edge of the touchable area
36236 // Slightly less than the step inset because of the curved edges
36237 // of the rail
36238 railInset: 8,
36239
36240 // The distance from the extremal tick marks to the edge of the
36241 // touchable area. This is basically the same as the grip radius,
36242 // but for other styles it wouldn't really need to be.
36243 stepInset: 10,
36244
36245 gripRadius: 10,
36246 gripWidth: 20,
36247 gripHeight: 20,
36248 gripBorder: 20,
36249 gripBorderWidth: 1,
36250 gripBorderColor: '#bec8d9',
36251 gripBgColor: '#f6f8fa',
36252 gripBgActiveColor: '#dbdde0',
36253
36254 labelPadding: 8,
36255 labelOffset: 0,
36256
36257 tickWidth: 1,
36258 tickColor: '#333',
36259 tickOffset: 25,
36260 tickLength: 7,
36261
36262 minorTickOffset: 25,
36263 minorTickColor: '#333',
36264 minorTickLength: 4,
36265
36266 // Extra space below the current value label:
36267 currentValuePadding: 8,
36268 currentValueInset: 0,
36269};
36270
36271},{}],142:[function(_dereq_,module,exports){
36272/**
36273* Copyright 2012-2020, Plotly, Inc.
36274* All rights reserved.
36275*
36276* This source code is licensed under the MIT license found in the
36277* LICENSE file in the root directory of this source tree.
36278*/
36279
36280'use strict';
36281
36282var Lib = _dereq_('../../lib');
36283var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
36284
36285var attributes = _dereq_('./attributes');
36286var constants = _dereq_('./constants');
36287
36288var name = constants.name;
36289var stepAttrs = attributes.steps;
36290
36291
36292module.exports = function slidersDefaults(layoutIn, layoutOut) {
36293 handleArrayContainerDefaults(layoutIn, layoutOut, {
36294 name: name,
36295 handleItemDefaults: sliderDefaults
36296 });
36297};
36298
36299function sliderDefaults(sliderIn, sliderOut, layoutOut) {
36300 function coerce(attr, dflt) {
36301 return Lib.coerce(sliderIn, sliderOut, attributes, attr, dflt);
36302 }
36303
36304 var steps = handleArrayContainerDefaults(sliderIn, sliderOut, {
36305 name: 'steps',
36306 handleItemDefaults: stepDefaults
36307 });
36308
36309 var stepCount = 0;
36310 for(var i = 0; i < steps.length; i++) {
36311 if(steps[i].visible) stepCount++;
36312 }
36313
36314 var visible;
36315 // If it has fewer than two options, it's not really a slider
36316 if(stepCount < 2) visible = sliderOut.visible = false;
36317 else visible = coerce('visible');
36318 if(!visible) return;
36319
36320 sliderOut._stepCount = stepCount;
36321 var visSteps = sliderOut._visibleSteps = Lib.filterVisible(steps);
36322
36323 var active = coerce('active');
36324 if(!(steps[active] || {}).visible) sliderOut.active = visSteps[0]._index;
36325
36326 coerce('x');
36327 coerce('y');
36328 Lib.noneOrAll(sliderIn, sliderOut, ['x', 'y']);
36329
36330 coerce('xanchor');
36331 coerce('yanchor');
36332
36333 coerce('len');
36334 coerce('lenmode');
36335
36336 coerce('pad.t');
36337 coerce('pad.r');
36338 coerce('pad.b');
36339 coerce('pad.l');
36340
36341 Lib.coerceFont(coerce, 'font', layoutOut.font);
36342
36343 var currentValueIsVisible = coerce('currentvalue.visible');
36344
36345 if(currentValueIsVisible) {
36346 coerce('currentvalue.xanchor');
36347 coerce('currentvalue.prefix');
36348 coerce('currentvalue.suffix');
36349 coerce('currentvalue.offset');
36350
36351 Lib.coerceFont(coerce, 'currentvalue.font', sliderOut.font);
36352 }
36353
36354 coerce('transition.duration');
36355 coerce('transition.easing');
36356
36357 coerce('bgcolor');
36358 coerce('activebgcolor');
36359 coerce('bordercolor');
36360 coerce('borderwidth');
36361 coerce('ticklen');
36362 coerce('tickwidth');
36363 coerce('tickcolor');
36364 coerce('minorticklen');
36365}
36366
36367function stepDefaults(valueIn, valueOut) {
36368 function coerce(attr, dflt) {
36369 return Lib.coerce(valueIn, valueOut, stepAttrs, attr, dflt);
36370 }
36371
36372 var visible;
36373 if(valueIn.method !== 'skip' && !Array.isArray(valueIn.args)) {
36374 visible = valueOut.visible = false;
36375 } else visible = coerce('visible');
36376
36377 if(visible) {
36378 coerce('method');
36379 coerce('args');
36380 var label = coerce('label', 'step-' + valueOut._index);
36381 coerce('value', label);
36382 coerce('execute');
36383 }
36384}
36385
36386},{"../../lib":177,"../../plots/array_container_defaults":218,"./attributes":140,"./constants":141}],143:[function(_dereq_,module,exports){
36387/**
36388* Copyright 2012-2020, Plotly, Inc.
36389* All rights reserved.
36390*
36391* This source code is licensed under the MIT license found in the
36392* LICENSE file in the root directory of this source tree.
36393*/
36394
36395'use strict';
36396
36397var d3 = _dereq_('d3');
36398
36399var Plots = _dereq_('../../plots/plots');
36400var Color = _dereq_('../color');
36401var Drawing = _dereq_('../drawing');
36402var Lib = _dereq_('../../lib');
36403var svgTextUtils = _dereq_('../../lib/svg_text_utils');
36404var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
36405
36406var constants = _dereq_('./constants');
36407var alignmentConstants = _dereq_('../../constants/alignment');
36408var LINE_SPACING = alignmentConstants.LINE_SPACING;
36409var FROM_TL = alignmentConstants.FROM_TL;
36410var FROM_BR = alignmentConstants.FROM_BR;
36411
36412module.exports = function draw(gd) {
36413 var fullLayout = gd._fullLayout;
36414 var sliderData = makeSliderData(fullLayout, gd);
36415
36416 // draw a container for *all* sliders:
36417 var sliders = fullLayout._infolayer
36418 .selectAll('g.' + constants.containerClassName)
36419 .data(sliderData.length > 0 ? [0] : []);
36420
36421 sliders.enter().append('g')
36422 .classed(constants.containerClassName, true)
36423 .style('cursor', 'ew-resize');
36424
36425 function clearSlider(sliderOpts) {
36426 if(sliderOpts._commandObserver) {
36427 sliderOpts._commandObserver.remove();
36428 delete sliderOpts._commandObserver;
36429 }
36430
36431 // Most components don't need to explicitly remove autoMargin, because
36432 // marginPushers does this - but slider updates don't go through
36433 // a full replot so we need to explicitly remove it.
36434 Plots.autoMargin(gd, autoMarginId(sliderOpts));
36435 }
36436
36437 sliders.exit().each(function() {
36438 d3.select(this).selectAll('g.' + constants.groupClassName)
36439 .each(clearSlider);
36440 })
36441 .remove();
36442
36443 // Return early if no menus visible:
36444 if(sliderData.length === 0) return;
36445
36446 var sliderGroups = sliders.selectAll('g.' + constants.groupClassName)
36447 .data(sliderData, keyFunction);
36448
36449 sliderGroups.enter().append('g')
36450 .classed(constants.groupClassName, true);
36451
36452 sliderGroups.exit()
36453 .each(clearSlider)
36454 .remove();
36455
36456 // Find the dimensions of the sliders:
36457 for(var i = 0; i < sliderData.length; i++) {
36458 var sliderOpts = sliderData[i];
36459 findDimensions(gd, sliderOpts);
36460 }
36461
36462 sliderGroups.each(function(sliderOpts) {
36463 var gSlider = d3.select(this);
36464
36465 computeLabelSteps(sliderOpts);
36466
36467 Plots.manageCommandObserver(gd, sliderOpts, sliderOpts._visibleSteps, function(data) {
36468 // NB: Same as below. This is *not* always the same as sliderOpts since
36469 // if a new set of steps comes in, the reference in this callback would
36470 // be invalid. We need to refetch it from the slider group, which is
36471 // the join data that creates this slider. So if this slider still exists,
36472 // the group should be valid, *to the best of my knowledge.* If not,
36473 // we'd have to look it up by d3 data join index/key.
36474 var opts = gSlider.data()[0];
36475
36476 if(opts.active === data.index) return;
36477 if(opts._dragging) return;
36478
36479 setActive(gd, gSlider, opts, data.index, false, true);
36480 });
36481
36482 drawSlider(gd, d3.select(this), sliderOpts);
36483 });
36484};
36485
36486function autoMarginId(sliderOpts) {
36487 return constants.autoMarginIdRoot + sliderOpts._index;
36488}
36489
36490// This really only just filters by visibility:
36491function makeSliderData(fullLayout, gd) {
36492 var contOpts = fullLayout[constants.name];
36493 var sliderData = [];
36494
36495 for(var i = 0; i < contOpts.length; i++) {
36496 var item = contOpts[i];
36497 if(!item.visible) continue;
36498 item._gd = gd;
36499 sliderData.push(item);
36500 }
36501
36502 return sliderData;
36503}
36504
36505// This is set in the defaults step:
36506function keyFunction(opts) {
36507 return opts._index;
36508}
36509
36510// Compute the dimensions (mutates sliderOpts):
36511function findDimensions(gd, sliderOpts) {
36512 var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass)
36513 .data(sliderOpts._visibleSteps);
36514
36515 sliderLabels.enter().append('g')
36516 .classed(constants.labelGroupClass, true);
36517
36518 // loop over fake buttons to find width / height
36519 var maxLabelWidth = 0;
36520 var labelHeight = 0;
36521 sliderLabels.each(function(stepOpts) {
36522 var labelGroup = d3.select(this);
36523
36524 var text = drawLabel(labelGroup, {step: stepOpts}, sliderOpts);
36525
36526 var textNode = text.node();
36527 if(textNode) {
36528 var bBox = Drawing.bBox(textNode);
36529 labelHeight = Math.max(labelHeight, bBox.height);
36530 maxLabelWidth = Math.max(maxLabelWidth, bBox.width);
36531 }
36532 });
36533
36534 sliderLabels.remove();
36535
36536 var dims = sliderOpts._dims = {};
36537
36538 dims.inputAreaWidth = Math.max(
36539 constants.railWidth,
36540 constants.gripHeight
36541 );
36542
36543 // calculate some overall dimensions - some of these are needed for
36544 // calculating the currentValue dimensions
36545 var graphSize = gd._fullLayout._size;
36546 dims.lx = graphSize.l + graphSize.w * sliderOpts.x;
36547 dims.ly = graphSize.t + graphSize.h * (1 - sliderOpts.y);
36548
36549 if(sliderOpts.lenmode === 'fraction') {
36550 // fraction:
36551 dims.outerLength = Math.round(graphSize.w * sliderOpts.len);
36552 } else {
36553 // pixels:
36554 dims.outerLength = sliderOpts.len;
36555 }
36556
36557 // The length of the rail, *excluding* padding on either end:
36558 dims.inputAreaStart = 0;
36559 dims.inputAreaLength = Math.round(dims.outerLength - sliderOpts.pad.l - sliderOpts.pad.r);
36560
36561 var textableInputLength = dims.inputAreaLength - 2 * constants.stepInset;
36562 var availableSpacePerLabel = textableInputLength / (sliderOpts._stepCount - 1);
36563 var computedSpacePerLabel = maxLabelWidth + constants.labelPadding;
36564 dims.labelStride = Math.max(1, Math.ceil(computedSpacePerLabel / availableSpacePerLabel));
36565 dims.labelHeight = labelHeight;
36566
36567 // loop over all possible values for currentValue to find the
36568 // area we need for it
36569 dims.currentValueMaxWidth = 0;
36570 dims.currentValueHeight = 0;
36571 dims.currentValueTotalHeight = 0;
36572 dims.currentValueMaxLines = 1;
36573
36574 if(sliderOpts.currentvalue.visible) {
36575 // Get the dimensions of the current value label:
36576 var dummyGroup = Drawing.tester.append('g');
36577
36578 sliderLabels.each(function(stepOpts) {
36579 var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label);
36580 var curValSize = (curValPrefix.node() && Drawing.bBox(curValPrefix.node())) || {width: 0, height: 0};
36581 var lines = svgTextUtils.lineCount(curValPrefix);
36582 dims.currentValueMaxWidth = Math.max(dims.currentValueMaxWidth, Math.ceil(curValSize.width));
36583 dims.currentValueHeight = Math.max(dims.currentValueHeight, Math.ceil(curValSize.height));
36584 dims.currentValueMaxLines = Math.max(dims.currentValueMaxLines, lines);
36585 });
36586
36587 dims.currentValueTotalHeight = dims.currentValueHeight + sliderOpts.currentvalue.offset;
36588
36589 dummyGroup.remove();
36590 }
36591
36592 dims.height = dims.currentValueTotalHeight + constants.tickOffset + sliderOpts.ticklen + constants.labelOffset + dims.labelHeight + sliderOpts.pad.t + sliderOpts.pad.b;
36593
36594 var xanchor = 'left';
36595 if(Lib.isRightAnchor(sliderOpts)) {
36596 dims.lx -= dims.outerLength;
36597 xanchor = 'right';
36598 }
36599 if(Lib.isCenterAnchor(sliderOpts)) {
36600 dims.lx -= dims.outerLength / 2;
36601 xanchor = 'center';
36602 }
36603
36604 var yanchor = 'top';
36605 if(Lib.isBottomAnchor(sliderOpts)) {
36606 dims.ly -= dims.height;
36607 yanchor = 'bottom';
36608 }
36609 if(Lib.isMiddleAnchor(sliderOpts)) {
36610 dims.ly -= dims.height / 2;
36611 yanchor = 'middle';
36612 }
36613
36614 dims.outerLength = Math.ceil(dims.outerLength);
36615 dims.height = Math.ceil(dims.height);
36616 dims.lx = Math.round(dims.lx);
36617 dims.ly = Math.round(dims.ly);
36618
36619 var marginOpts = {
36620 y: sliderOpts.y,
36621 b: dims.height * FROM_BR[yanchor],
36622 t: dims.height * FROM_TL[yanchor]
36623 };
36624
36625 if(sliderOpts.lenmode === 'fraction') {
36626 marginOpts.l = 0;
36627 marginOpts.xl = sliderOpts.x - sliderOpts.len * FROM_TL[xanchor];
36628 marginOpts.r = 0;
36629 marginOpts.xr = sliderOpts.x + sliderOpts.len * FROM_BR[xanchor];
36630 } else {
36631 marginOpts.x = sliderOpts.x;
36632 marginOpts.l = dims.outerLength * FROM_TL[xanchor];
36633 marginOpts.r = dims.outerLength * FROM_BR[xanchor];
36634 }
36635
36636 Plots.autoMargin(gd, autoMarginId(sliderOpts), marginOpts);
36637}
36638
36639function drawSlider(gd, sliderGroup, sliderOpts) {
36640 // This is related to the other long notes in this file regarding what happens
36641 // when slider steps disappear. This particular fix handles what happens when
36642 // the *current* slider step is removed. The drawing functions will error out
36643 // when they fail to find it, so the fix for now is that it will just draw the
36644 // slider in the first position but will not execute the command.
36645 if(!((sliderOpts.steps[sliderOpts.active] || {}).visible)) {
36646 sliderOpts.active = sliderOpts._visibleSteps[0]._index;
36647 }
36648
36649 // These are carefully ordered for proper z-ordering:
36650 sliderGroup
36651 .call(drawCurrentValue, sliderOpts)
36652 .call(drawRail, sliderOpts)
36653 .call(drawLabelGroup, sliderOpts)
36654 .call(drawTicks, sliderOpts)
36655 .call(drawTouchRect, gd, sliderOpts)
36656 .call(drawGrip, gd, sliderOpts);
36657
36658 var dims = sliderOpts._dims;
36659
36660 // Position the rectangle:
36661 Drawing.setTranslate(sliderGroup, dims.lx + sliderOpts.pad.l, dims.ly + sliderOpts.pad.t);
36662
36663 sliderGroup.call(setGripPosition, sliderOpts, false);
36664 sliderGroup.call(drawCurrentValue, sliderOpts);
36665}
36666
36667function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
36668 if(!sliderOpts.currentvalue.visible) return;
36669
36670 var dims = sliderOpts._dims;
36671 var x0, textAnchor;
36672
36673 switch(sliderOpts.currentvalue.xanchor) {
36674 case 'right':
36675 // This is anchored left and adjusted by the width of the longest label
36676 // so that the prefix doesn't move. The goal of this is to emphasize
36677 // what's actually changing and make the update less distracting.
36678 x0 = dims.inputAreaLength - constants.currentValueInset - dims.currentValueMaxWidth;
36679 textAnchor = 'left';
36680 break;
36681 case 'center':
36682 x0 = dims.inputAreaLength * 0.5;
36683 textAnchor = 'middle';
36684 break;
36685 default:
36686 x0 = constants.currentValueInset;
36687 textAnchor = 'left';
36688 }
36689
36690 var text = Lib.ensureSingle(sliderGroup, 'text', constants.labelClass, function(s) {
36691 s.classed('user-select-none', true)
36692 .attr({
36693 'text-anchor': textAnchor,
36694 'data-notex': 1
36695 });
36696 });
36697
36698 var str = sliderOpts.currentvalue.prefix ? sliderOpts.currentvalue.prefix : '';
36699
36700 if(typeof valueOverride === 'string') {
36701 str += valueOverride;
36702 } else {
36703 var curVal = sliderOpts.steps[sliderOpts.active].label;
36704 var _meta = sliderOpts._gd._fullLayout._meta;
36705 if(_meta) curVal = Lib.templateString(curVal, _meta);
36706 str += curVal;
36707 }
36708
36709 if(sliderOpts.currentvalue.suffix) {
36710 str += sliderOpts.currentvalue.suffix;
36711 }
36712
36713 text.call(Drawing.font, sliderOpts.currentvalue.font)
36714 .text(str)
36715 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
36716
36717 var lines = svgTextUtils.lineCount(text);
36718
36719 var y0 = (dims.currentValueMaxLines + 1 - lines) *
36720 sliderOpts.currentvalue.font.size * LINE_SPACING;
36721
36722 svgTextUtils.positionText(text, x0, y0);
36723
36724 return text;
36725}
36726
36727function drawGrip(sliderGroup, gd, sliderOpts) {
36728 var grip = Lib.ensureSingle(sliderGroup, 'rect', constants.gripRectClass, function(s) {
36729 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
36730 .style('pointer-events', 'all');
36731 });
36732
36733 grip.attr({
36734 width: constants.gripWidth,
36735 height: constants.gripHeight,
36736 rx: constants.gripRadius,
36737 ry: constants.gripRadius,
36738 })
36739 .call(Color.stroke, sliderOpts.bordercolor)
36740 .call(Color.fill, sliderOpts.bgcolor)
36741 .style('stroke-width', sliderOpts.borderwidth + 'px');
36742}
36743
36744function drawLabel(item, data, sliderOpts) {
36745 var text = Lib.ensureSingle(item, 'text', constants.labelClass, function(s) {
36746 s.classed('user-select-none', true)
36747 .attr({
36748 'text-anchor': 'middle',
36749 'data-notex': 1
36750 });
36751 });
36752
36753 var tx = data.step.label;
36754 var _meta = sliderOpts._gd._fullLayout._meta;
36755 if(_meta) tx = Lib.templateString(tx, _meta);
36756
36757 text.call(Drawing.font, sliderOpts.font)
36758 .text(tx)
36759 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
36760
36761 return text;
36762}
36763
36764function drawLabelGroup(sliderGroup, sliderOpts) {
36765 var labels = Lib.ensureSingle(sliderGroup, 'g', constants.labelsClass);
36766 var dims = sliderOpts._dims;
36767
36768 var labelItems = labels.selectAll('g.' + constants.labelGroupClass)
36769 .data(dims.labelSteps);
36770
36771 labelItems.enter().append('g')
36772 .classed(constants.labelGroupClass, true);
36773
36774 labelItems.exit().remove();
36775
36776 labelItems.each(function(d) {
36777 var item = d3.select(this);
36778
36779 item.call(drawLabel, d, sliderOpts);
36780
36781 Drawing.setTranslate(item,
36782 normalizedValueToPosition(sliderOpts, d.fraction),
36783 constants.tickOffset +
36784 sliderOpts.ticklen +
36785 // position is the baseline of the top line of text only, even
36786 // if the label spans multiple lines
36787 sliderOpts.font.size * LINE_SPACING +
36788 constants.labelOffset +
36789 dims.currentValueTotalHeight
36790 );
36791 });
36792}
36793
36794function handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, doTransition) {
36795 var quantizedPosition = Math.round(normalizedPosition * (sliderOpts._stepCount - 1));
36796 var quantizedIndex = sliderOpts._visibleSteps[quantizedPosition]._index;
36797
36798 if(quantizedIndex !== sliderOpts.active) {
36799 setActive(gd, sliderGroup, sliderOpts, quantizedIndex, true, doTransition);
36800 }
36801}
36802
36803function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition) {
36804 var previousActive = sliderOpts.active;
36805 sliderOpts.active = index;
36806
36807 // due to templating, it's possible this slider doesn't even exist yet
36808 arrayEditor(gd.layout, constants.name, sliderOpts)
36809 .applyUpdate('active', index);
36810
36811 var step = sliderOpts.steps[sliderOpts.active];
36812
36813 sliderGroup.call(setGripPosition, sliderOpts, doTransition);
36814 sliderGroup.call(drawCurrentValue, sliderOpts);
36815
36816 gd.emit('plotly_sliderchange', {
36817 slider: sliderOpts,
36818 step: sliderOpts.steps[sliderOpts.active],
36819 interaction: doCallback,
36820 previousActive: previousActive
36821 });
36822
36823 if(step && step.method && doCallback) {
36824 if(sliderGroup._nextMethod) {
36825 // If we've already queued up an update, just overwrite it with the most recent:
36826 sliderGroup._nextMethod.step = step;
36827 sliderGroup._nextMethod.doCallback = doCallback;
36828 sliderGroup._nextMethod.doTransition = doTransition;
36829 } else {
36830 sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
36831 sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
36832 var _step = sliderGroup._nextMethod.step;
36833 if(!_step.method) return;
36834
36835 if(_step.execute) {
36836 Plots.executeAPICommand(gd, _step.method, _step.args);
36837 }
36838
36839 sliderGroup._nextMethod = null;
36840 sliderGroup._nextMethodRaf = null;
36841 });
36842 }
36843 }
36844}
36845
36846function attachGripEvents(item, gd, sliderGroup) {
36847 var node = sliderGroup.node();
36848 var $gd = d3.select(gd);
36849
36850 // NB: This is *not* the same as sliderOpts itself! These callbacks
36851 // are in a closure so this array won't actually be correct if the
36852 // steps have changed since this was initialized. The sliderGroup,
36853 // however, has not changed since that *is* the slider, so it must
36854 // be present to receive mouse events.
36855 function getSliderOpts() {
36856 return sliderGroup.data()[0];
36857 }
36858
36859 item.on('mousedown', function() {
36860 var sliderOpts = getSliderOpts();
36861 gd.emit('plotly_sliderstart', {slider: sliderOpts});
36862
36863 var grip = sliderGroup.select('.' + constants.gripRectClass);
36864
36865 d3.event.stopPropagation();
36866 d3.event.preventDefault();
36867 grip.call(Color.fill, sliderOpts.activebgcolor);
36868
36869 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
36870 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
36871 sliderOpts._dragging = true;
36872
36873 $gd.on('mousemove', function() {
36874 var sliderOpts = getSliderOpts();
36875 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
36876 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
36877 });
36878
36879 $gd.on('mouseup', function() {
36880 var sliderOpts = getSliderOpts();
36881 sliderOpts._dragging = false;
36882 grip.call(Color.fill, sliderOpts.bgcolor);
36883 $gd.on('mouseup', null);
36884 $gd.on('mousemove', null);
36885
36886 gd.emit('plotly_sliderend', {
36887 slider: sliderOpts,
36888 step: sliderOpts.steps[sliderOpts.active]
36889 });
36890 });
36891 });
36892}
36893
36894function drawTicks(sliderGroup, sliderOpts) {
36895 var tick = sliderGroup.selectAll('rect.' + constants.tickRectClass)
36896 .data(sliderOpts._visibleSteps);
36897 var dims = sliderOpts._dims;
36898
36899 tick.enter().append('rect')
36900 .classed(constants.tickRectClass, true);
36901
36902 tick.exit().remove();
36903
36904 tick.attr({
36905 width: sliderOpts.tickwidth + 'px',
36906 'shape-rendering': 'crispEdges'
36907 });
36908
36909 tick.each(function(d, i) {
36910 var isMajor = i % dims.labelStride === 0;
36911 var item = d3.select(this);
36912
36913 item
36914 .attr({height: isMajor ? sliderOpts.ticklen : sliderOpts.minorticklen})
36915 .call(Color.fill, isMajor ? sliderOpts.tickcolor : sliderOpts.tickcolor);
36916
36917 Drawing.setTranslate(item,
36918 normalizedValueToPosition(sliderOpts, i / (sliderOpts._stepCount - 1)) - 0.5 * sliderOpts.tickwidth,
36919 (isMajor ? constants.tickOffset : constants.minorTickOffset) + dims.currentValueTotalHeight
36920 );
36921 });
36922}
36923
36924function computeLabelSteps(sliderOpts) {
36925 var dims = sliderOpts._dims;
36926 dims.labelSteps = [];
36927 var nsteps = sliderOpts._stepCount;
36928
36929 for(var i = 0; i < nsteps; i += dims.labelStride) {
36930 dims.labelSteps.push({
36931 fraction: i / (nsteps - 1),
36932 step: sliderOpts._visibleSteps[i]
36933 });
36934 }
36935}
36936
36937function setGripPosition(sliderGroup, sliderOpts, doTransition) {
36938 var grip = sliderGroup.select('rect.' + constants.gripRectClass);
36939
36940 var quantizedIndex = 0;
36941 for(var i = 0; i < sliderOpts._stepCount; i++) {
36942 if(sliderOpts._visibleSteps[i]._index === sliderOpts.active) {
36943 quantizedIndex = i;
36944 break;
36945 }
36946 }
36947
36948 var x = normalizedValueToPosition(sliderOpts, quantizedIndex / (sliderOpts._stepCount - 1));
36949
36950 // If this is true, then *this component* is already invoking its own command
36951 // and has triggered its own animation.
36952 if(sliderOpts._invokingCommand) return;
36953
36954 var el = grip;
36955 if(doTransition && sliderOpts.transition.duration > 0) {
36956 el = el.transition()
36957 .duration(sliderOpts.transition.duration)
36958 .ease(sliderOpts.transition.easing);
36959 }
36960
36961 // Drawing.setTranslate doesn't work here becasue of the transition duck-typing.
36962 // It's also not necessary because there are no other transitions to preserve.
36963 el.attr('transform', 'translate(' + (x - constants.gripWidth * 0.5) + ',' + (sliderOpts._dims.currentValueTotalHeight) + ')');
36964}
36965
36966// Convert a number from [0-1] to a pixel position relative to the slider group container:
36967function normalizedValueToPosition(sliderOpts, normalizedPosition) {
36968 var dims = sliderOpts._dims;
36969 return dims.inputAreaStart + constants.stepInset +
36970 (dims.inputAreaLength - 2 * constants.stepInset) * Math.min(1, Math.max(0, normalizedPosition));
36971}
36972
36973// Convert a position relative to the slider group to a nubmer in [0, 1]
36974function positionToNormalizedValue(sliderOpts, position) {
36975 var dims = sliderOpts._dims;
36976 return Math.min(1, Math.max(0, (position - constants.stepInset - dims.inputAreaStart) / (dims.inputAreaLength - 2 * constants.stepInset - 2 * dims.inputAreaStart)));
36977}
36978
36979function drawTouchRect(sliderGroup, gd, sliderOpts) {
36980 var dims = sliderOpts._dims;
36981 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railTouchRectClass, function(s) {
36982 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
36983 .style('pointer-events', 'all');
36984 });
36985
36986 rect.attr({
36987 width: dims.inputAreaLength,
36988 height: Math.max(dims.inputAreaWidth, constants.tickOffset + sliderOpts.ticklen + dims.labelHeight)
36989 })
36990 .call(Color.fill, sliderOpts.bgcolor)
36991 .attr('opacity', 0);
36992
36993 Drawing.setTranslate(rect, 0, dims.currentValueTotalHeight);
36994}
36995
36996function drawRail(sliderGroup, sliderOpts) {
36997 var dims = sliderOpts._dims;
36998 var computedLength = dims.inputAreaLength - constants.railInset * 2;
36999 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railRectClass);
37000
37001 rect.attr({
37002 width: computedLength,
37003 height: constants.railWidth,
37004 rx: constants.railRadius,
37005 ry: constants.railRadius,
37006 'shape-rendering': 'crispEdges'
37007 })
37008 .call(Color.stroke, sliderOpts.bordercolor)
37009 .call(Color.fill, sliderOpts.bgcolor)
37010 .style('stroke-width', sliderOpts.borderwidth + 'px');
37011
37012 Drawing.setTranslate(rect,
37013 constants.railInset,
37014 (dims.inputAreaWidth - constants.railWidth) * 0.5 + dims.currentValueTotalHeight
37015 );
37016}
37017
37018},{"../../constants/alignment":152,"../../lib":177,"../../lib/svg_text_utils":198,"../../plot_api/plot_template":212,"../../plots/plots":263,"../color":50,"../drawing":72,"./constants":141,"d3":13}],144:[function(_dereq_,module,exports){
37019/**
37020* Copyright 2012-2020, Plotly, Inc.
37021* All rights reserved.
37022*
37023* This source code is licensed under the MIT license found in the
37024* LICENSE file in the root directory of this source tree.
37025*/
37026
37027'use strict';
37028
37029var constants = _dereq_('./constants');
37030
37031module.exports = {
37032 moduleType: 'component',
37033 name: constants.name,
37034
37035 layoutAttributes: _dereq_('./attributes'),
37036 supplyLayoutDefaults: _dereq_('./defaults'),
37037
37038 draw: _dereq_('./draw')
37039};
37040
37041},{"./attributes":140,"./constants":141,"./defaults":142,"./draw":143}],145:[function(_dereq_,module,exports){
37042/**
37043* Copyright 2012-2020, Plotly, Inc.
37044* All rights reserved.
37045*
37046* This source code is licensed under the MIT license found in the
37047* LICENSE file in the root directory of this source tree.
37048*/
37049
37050
37051'use strict';
37052
37053var d3 = _dereq_('d3');
37054var isNumeric = _dereq_('fast-isnumeric');
37055
37056var Plots = _dereq_('../../plots/plots');
37057var Registry = _dereq_('../../registry');
37058var Lib = _dereq_('../../lib');
37059var Drawing = _dereq_('../drawing');
37060var Color = _dereq_('../color');
37061var svgTextUtils = _dereq_('../../lib/svg_text_utils');
37062var interactConstants = _dereq_('../../constants/interactions');
37063
37064var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE;
37065var numStripRE = / [XY][0-9]* /;
37066
37067/**
37068 * Titles - (re)draw titles on the axes and plot:
37069 * @param {DOM element} gd - the graphDiv
37070 * @param {string} titleClass - the css class of this title
37071 * @param {object} options - how and what to draw
37072 * propContainer - the layout object containing `title` and `titlefont`
37073 * attributes that apply to this title
37074 * propName - the full name of the title property (for Plotly.relayout)
37075 * [traceIndex] - include only if this property applies to one trace
37076 * (such as a colorbar title) - then editing pipes to Plotly.restyle
37077 * instead of Plotly.relayout
37078 * placeholder - placeholder text for an empty editable title
37079 * [avoid] {object} - include if this title should move to avoid other elements
37080 * selection - d3 selection of elements to avoid
37081 * side - which direction to move if there is a conflict
37082 * [offsetLeft] - if these elements are subject to a translation
37083 * wrt the title element
37084 * [offsetTop]
37085 * attributes {object} - position and alignment attributes
37086 * x - pixels
37087 * y - pixels
37088 * text-anchor - start|middle|end
37089 * transform {object} - how to transform the title after positioning
37090 * rotate - degrees
37091 * offset - shift up/down in the rotated frame (unused?)
37092 * containerGroup - if an svg <g> element already exists to hold this
37093 * title, include here. Otherwise it will go in fullLayout._infolayer
37094 * _meta {object (optional} - meta key-value to for title with
37095 * Lib.templateString, default to fullLayout._meta, if not provided
37096 *
37097 * @return {selection} d3 selection of title container group
37098 */
37099function draw(gd, titleClass, options) {
37100 var cont = options.propContainer;
37101 var prop = options.propName;
37102 var placeholder = options.placeholder;
37103 var traceIndex = options.traceIndex;
37104 var avoid = options.avoid || {};
37105 var attributes = options.attributes;
37106 var transform = options.transform;
37107 var group = options.containerGroup;
37108
37109 var fullLayout = gd._fullLayout;
37110
37111 var opacity = 1;
37112 var isplaceholder = false;
37113 var title = cont.title;
37114 var txt = (title && title.text ? title.text : '').trim();
37115
37116 var font = title && title.font ? title.font : {};
37117 var fontFamily = font.family;
37118 var fontSize = font.size;
37119 var fontColor = font.color;
37120
37121 // only make this title editable if we positively identify its property
37122 // as one that has editing enabled.
37123 var editAttr;
37124 if(prop === 'title.text') editAttr = 'titleText';
37125 else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText';
37126 else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText';
37127 var editable = gd._context.edits[editAttr];
37128
37129 if(txt === '') opacity = 0;
37130 // look for placeholder text while stripping out numbers from eg X2, Y3
37131 // this is just for backward compatibility with the old version that had
37132 // "Click to enter X2 title" and may have gotten saved in some old plots,
37133 // we don't want this to show up when these are displayed.
37134 else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) {
37135 opacity = 0.2;
37136 isplaceholder = true;
37137 if(!editable) txt = '';
37138 }
37139
37140 if(options._meta) {
37141 txt = Lib.templateString(txt, options._meta);
37142 } else if(fullLayout._meta) {
37143 txt = Lib.templateString(txt, fullLayout._meta);
37144 }
37145
37146 var elShouldExist = txt || editable;
37147
37148 if(!group) {
37149 group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass);
37150 }
37151
37152 var el = group.selectAll('text')
37153 .data(elShouldExist ? [0] : []);
37154 el.enter().append('text');
37155 el.text(txt)
37156 // this is hacky, but convertToTspans uses the class
37157 // to determine whether to rotate mathJax...
37158 // so we need to clear out any old class and put the
37159 // correct one (only relevant for colorbars, at least
37160 // for now) - ie don't use .classed
37161 .attr('class', titleClass);
37162 el.exit().remove();
37163
37164 if(!elShouldExist) return group;
37165
37166 function titleLayout(titleEl) {
37167 Lib.syncOrAsync([drawTitle, scootTitle], titleEl);
37168 }
37169
37170 function drawTitle(titleEl) {
37171 var transformVal;
37172
37173 if(transform) {
37174 transformVal = '';
37175 if(transform.rotate) {
37176 transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')';
37177 }
37178 if(transform.offset) {
37179 transformVal += 'translate(0, ' + transform.offset + ')';
37180 }
37181 } else {
37182 transformVal = null;
37183 }
37184
37185 titleEl.attr('transform', transformVal);
37186
37187 titleEl.style({
37188 'font-family': fontFamily,
37189 'font-size': d3.round(fontSize, 2) + 'px',
37190 fill: Color.rgb(fontColor),
37191 opacity: opacity * Color.opacity(fontColor),
37192 'font-weight': Plots.fontWeight
37193 })
37194 .attr(attributes)
37195 .call(svgTextUtils.convertToTspans, gd);
37196
37197 return Plots.previousPromises(gd);
37198 }
37199
37200 function scootTitle(titleElIn) {
37201 var titleGroup = d3.select(titleElIn.node().parentNode);
37202
37203 if(avoid && avoid.selection && avoid.side && txt) {
37204 titleGroup.attr('transform', null);
37205
37206 // move toward avoid.side (= left, right, top, bottom) if needed
37207 // can include pad (pixels, default 2)
37208 var backside = OPPOSITE_SIDE[avoid.side];
37209 var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1;
37210 var pad = isNumeric(avoid.pad) ? avoid.pad : 2;
37211
37212 var titlebb = Drawing.bBox(titleGroup.node());
37213 var paperbb = {
37214 left: 0,
37215 top: 0,
37216 right: fullLayout.width,
37217 bottom: fullLayout.height
37218 };
37219
37220 var maxshift = avoid.maxShift ||
37221 shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]);
37222 var shift = 0;
37223
37224 // Prevent the title going off the paper
37225 if(maxshift < 0) {
37226 shift = maxshift;
37227 } else {
37228 // so we don't have to offset each avoided element,
37229 // give the title the opposite offset
37230 var offsetLeft = avoid.offsetLeft || 0;
37231 var offsetTop = avoid.offsetTop || 0;
37232 titlebb.left -= offsetLeft;
37233 titlebb.right -= offsetLeft;
37234 titlebb.top -= offsetTop;
37235 titlebb.bottom -= offsetTop;
37236
37237 // iterate over a set of elements (avoid.selection)
37238 // to avoid collisions with
37239 avoid.selection.each(function() {
37240 var avoidbb = Drawing.bBox(this);
37241
37242 if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) {
37243 shift = Math.max(shift, shiftSign * (
37244 avoidbb[avoid.side] - titlebb[backside]) + pad);
37245 }
37246 });
37247 shift = Math.min(maxshift, shift);
37248 }
37249
37250 if(shift > 0 || maxshift < 0) {
37251 var shiftTemplate = {
37252 left: [-shift, 0],
37253 right: [shift, 0],
37254 top: [0, -shift],
37255 bottom: [0, shift]
37256 }[avoid.side];
37257 titleGroup.attr('transform', 'translate(' + shiftTemplate + ')');
37258 }
37259 }
37260 }
37261
37262 el.call(titleLayout);
37263
37264 function setPlaceholder() {
37265 opacity = 0;
37266 isplaceholder = true;
37267 el.text(placeholder)
37268 .on('mouseover.opacity', function() {
37269 d3.select(this).transition()
37270 .duration(interactConstants.SHOW_PLACEHOLDER).style('opacity', 1);
37271 })
37272 .on('mouseout.opacity', function() {
37273 d3.select(this).transition()
37274 .duration(interactConstants.HIDE_PLACEHOLDER).style('opacity', 0);
37275 });
37276 }
37277
37278 if(editable) {
37279 if(!txt) setPlaceholder();
37280 else el.on('.opacity', null);
37281
37282 el.call(svgTextUtils.makeEditable, {gd: gd})
37283 .on('edit', function(text) {
37284 if(traceIndex !== undefined) {
37285 Registry.call('_guiRestyle', gd, prop, text, traceIndex);
37286 } else {
37287 Registry.call('_guiRelayout', gd, prop, text);
37288 }
37289 })
37290 .on('cancel', function() {
37291 this.text(this.attr('data-unformatted'))
37292 .call(titleLayout);
37293 })
37294 .on('input', function(d) {
37295 this.text(d || ' ')
37296 .call(svgTextUtils.positionText, attributes.x, attributes.y);
37297 });
37298 }
37299 el.classed('js-placeholder', isplaceholder);
37300
37301 return group;
37302}
37303
37304module.exports = {
37305 draw: draw
37306};
37307
37308},{"../../constants/alignment":152,"../../constants/interactions":154,"../../lib":177,"../../lib/svg_text_utils":198,"../../plots/plots":263,"../../registry":272,"../color":50,"../drawing":72,"d3":13,"fast-isnumeric":15}],146:[function(_dereq_,module,exports){
37309/**
37310* Copyright 2012-2020, Plotly, Inc.
37311* All rights reserved.
37312*
37313* This source code is licensed under the MIT license found in the
37314* LICENSE file in the root directory of this source tree.
37315*/
37316
37317'use strict';
37318
37319var fontAttrs = _dereq_('../../plots/font_attributes');
37320var colorAttrs = _dereq_('../color/attributes');
37321var extendFlat = _dereq_('../../lib/extend').extendFlat;
37322var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
37323var padAttrs = _dereq_('../../plots/pad_attributes');
37324var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
37325
37326var buttonsAttrs = templatedArray('button', {
37327 visible: {
37328 valType: 'boolean',
37329
37330
37331 },
37332 method: {
37333 valType: 'enumerated',
37334 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
37335 dflt: 'restyle',
37336
37337
37338 },
37339 args: {
37340 valType: 'info_array',
37341
37342 freeLength: true,
37343 items: [
37344 {valType: 'any'},
37345 {valType: 'any'},
37346 {valType: 'any'}
37347 ],
37348
37349 },
37350 args2: {
37351 valType: 'info_array',
37352
37353 freeLength: true,
37354 items: [
37355 {valType: 'any'},
37356 {valType: 'any'},
37357 {valType: 'any'}
37358 ],
37359
37360 },
37361 label: {
37362 valType: 'string',
37363
37364 dflt: '',
37365
37366 },
37367 execute: {
37368 valType: 'boolean',
37369
37370 dflt: true,
37371
37372 }
37373});
37374
37375module.exports = overrideAll(templatedArray('updatemenu', {
37376 _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],
37377
37378 visible: {
37379 valType: 'boolean',
37380
37381
37382 },
37383
37384 type: {
37385 valType: 'enumerated',
37386 values: ['dropdown', 'buttons'],
37387 dflt: 'dropdown',
37388
37389
37390 },
37391
37392 direction: {
37393 valType: 'enumerated',
37394 values: ['left', 'right', 'up', 'down'],
37395 dflt: 'down',
37396
37397
37398 },
37399
37400 active: {
37401 valType: 'integer',
37402
37403 min: -1,
37404 dflt: 0,
37405
37406 },
37407
37408 showactive: {
37409 valType: 'boolean',
37410
37411 dflt: true,
37412
37413 },
37414
37415 buttons: buttonsAttrs,
37416
37417 x: {
37418 valType: 'number',
37419 min: -2,
37420 max: 3,
37421 dflt: -0.05,
37422
37423
37424 },
37425 xanchor: {
37426 valType: 'enumerated',
37427 values: ['auto', 'left', 'center', 'right'],
37428 dflt: 'right',
37429
37430
37431 },
37432 y: {
37433 valType: 'number',
37434 min: -2,
37435 max: 3,
37436 dflt: 1,
37437
37438
37439 },
37440 yanchor: {
37441 valType: 'enumerated',
37442 values: ['auto', 'top', 'middle', 'bottom'],
37443 dflt: 'top',
37444
37445
37446 },
37447
37448 pad: extendFlat(padAttrs({editType: 'arraydraw'}), {
37449
37450 }),
37451
37452 font: fontAttrs({
37453
37454 }),
37455
37456 bgcolor: {
37457 valType: 'color',
37458
37459
37460 },
37461 bordercolor: {
37462 valType: 'color',
37463 dflt: colorAttrs.borderLine,
37464
37465
37466 },
37467 borderwidth: {
37468 valType: 'number',
37469 min: 0,
37470 dflt: 1,
37471
37472 editType: 'arraydraw',
37473
37474 }
37475}), 'arraydraw', 'from-root');
37476
37477},{"../../lib/extend":170,"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../../plots/font_attributes":250,"../../plots/pad_attributes":262,"../color/attributes":49}],147:[function(_dereq_,module,exports){
37478/**
37479* Copyright 2012-2020, Plotly, Inc.
37480* All rights reserved.
37481*
37482* This source code is licensed under the MIT license found in the
37483* LICENSE file in the root directory of this source tree.
37484*/
37485
37486
37487'use strict';
37488
37489
37490module.exports = {
37491
37492 // layout attribute name
37493 name: 'updatemenus',
37494
37495 // class names
37496 containerClassName: 'updatemenu-container',
37497 headerGroupClassName: 'updatemenu-header-group',
37498 headerClassName: 'updatemenu-header',
37499 headerArrowClassName: 'updatemenu-header-arrow',
37500 dropdownButtonGroupClassName: 'updatemenu-dropdown-button-group',
37501 dropdownButtonClassName: 'updatemenu-dropdown-button',
37502 buttonClassName: 'updatemenu-button',
37503 itemRectClassName: 'updatemenu-item-rect',
37504 itemTextClassName: 'updatemenu-item-text',
37505
37506 // DOM attribute name in button group keeping track
37507 // of active update menu
37508 menuIndexAttrName: 'updatemenu-active-index',
37509
37510 // id root pass to Plots.autoMargin
37511 autoMarginIdRoot: 'updatemenu-',
37512
37513 // options when 'active: -1'
37514 blankHeaderOpts: { label: ' ' },
37515
37516 // min item width / height
37517 minWidth: 30,
37518 minHeight: 30,
37519
37520 // padding around item text
37521 textPadX: 24,
37522 arrowPadX: 16,
37523
37524 // item rect radii
37525 rx: 2,
37526 ry: 2,
37527
37528 // item text x offset off left edge
37529 textOffsetX: 12,
37530
37531 // item text y offset (w.r.t. middle)
37532 textOffsetY: 3,
37533
37534 // arrow offset off right edge
37535 arrowOffsetX: 4,
37536
37537 // gap between header and buttons
37538 gapButtonHeader: 5,
37539
37540 // gap between between buttons
37541 gapButton: 2,
37542
37543 // color given to active buttons
37544 activeColor: '#F4FAFF',
37545
37546 // color given to hovered buttons
37547 hoverColor: '#F4FAFF',
37548
37549 // symbol for menu open arrow
37550 arrowSymbol: {
37551 left: '◄',
37552 right: '►',
37553 up: '▲',
37554 down: '▼'
37555 }
37556};
37557
37558},{}],148:[function(_dereq_,module,exports){
37559/**
37560* Copyright 2012-2020, Plotly, Inc.
37561* All rights reserved.
37562*
37563* This source code is licensed under the MIT license found in the
37564* LICENSE file in the root directory of this source tree.
37565*/
37566
37567'use strict';
37568
37569var Lib = _dereq_('../../lib');
37570var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
37571
37572var attributes = _dereq_('./attributes');
37573var constants = _dereq_('./constants');
37574
37575var name = constants.name;
37576var buttonAttrs = attributes.buttons;
37577
37578
37579module.exports = function updateMenusDefaults(layoutIn, layoutOut) {
37580 var opts = {
37581 name: name,
37582 handleItemDefaults: menuDefaults
37583 };
37584
37585 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
37586};
37587
37588function menuDefaults(menuIn, menuOut, layoutOut) {
37589 function coerce(attr, dflt) {
37590 return Lib.coerce(menuIn, menuOut, attributes, attr, dflt);
37591 }
37592
37593 var buttons = handleArrayContainerDefaults(menuIn, menuOut, {
37594 name: 'buttons',
37595 handleItemDefaults: buttonDefaults
37596 });
37597
37598 var visible = coerce('visible', buttons.length > 0);
37599 if(!visible) return;
37600
37601 coerce('active');
37602 coerce('direction');
37603 coerce('type');
37604 coerce('showactive');
37605
37606 coerce('x');
37607 coerce('y');
37608 Lib.noneOrAll(menuIn, menuOut, ['x', 'y']);
37609
37610 coerce('xanchor');
37611 coerce('yanchor');
37612
37613 coerce('pad.t');
37614 coerce('pad.r');
37615 coerce('pad.b');
37616 coerce('pad.l');
37617
37618 Lib.coerceFont(coerce, 'font', layoutOut.font);
37619
37620 coerce('bgcolor', layoutOut.paper_bgcolor);
37621 coerce('bordercolor');
37622 coerce('borderwidth');
37623}
37624
37625function buttonDefaults(buttonIn, buttonOut) {
37626 function coerce(attr, dflt) {
37627 return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt);
37628 }
37629
37630 var visible = coerce('visible',
37631 (buttonIn.method === 'skip' || Array.isArray(buttonIn.args)));
37632 if(visible) {
37633 coerce('method');
37634 coerce('args');
37635 coerce('args2');
37636 coerce('label');
37637 coerce('execute');
37638 }
37639}
37640
37641},{"../../lib":177,"../../plots/array_container_defaults":218,"./attributes":146,"./constants":147}],149:[function(_dereq_,module,exports){
37642/**
37643* Copyright 2012-2020, Plotly, Inc.
37644* All rights reserved.
37645*
37646* This source code is licensed under the MIT license found in the
37647* LICENSE file in the root directory of this source tree.
37648*/
37649
37650
37651'use strict';
37652
37653var d3 = _dereq_('d3');
37654
37655var Plots = _dereq_('../../plots/plots');
37656var Color = _dereq_('../color');
37657var Drawing = _dereq_('../drawing');
37658var Lib = _dereq_('../../lib');
37659var svgTextUtils = _dereq_('../../lib/svg_text_utils');
37660var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
37661
37662var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
37663
37664var constants = _dereq_('./constants');
37665var ScrollBox = _dereq_('./scrollbox');
37666
37667module.exports = function draw(gd) {
37668 var fullLayout = gd._fullLayout;
37669 var menuData = Lib.filterVisible(fullLayout[constants.name]);
37670
37671 /* Update menu data is bound to the header-group.
37672 * The items in the header group are always present.
37673 *
37674 * Upon clicking on a header its corresponding button
37675 * data is bound to the button-group.
37676 *
37677 * We draw all headers in one group before all buttons
37678 * so that the buttons *always* appear above the headers.
37679 *
37680 * Note that only one set of buttons are visible at once.
37681 *
37682 * <g container />
37683 *
37684 * <g header-group />
37685 * <g item header />
37686 * <text item header-arrow />
37687 * <g header-group />
37688 * <g item header />
37689 * <text item header-arrow />
37690 * ...
37691 *
37692 * <g button-group />
37693 * <g item button />
37694 * <g item button />
37695 * ...
37696 */
37697
37698 function clearAutoMargin(menuOpts) {
37699 Plots.autoMargin(gd, autoMarginId(menuOpts));
37700 }
37701
37702 // draw update menu container
37703 var menus = fullLayout._menulayer
37704 .selectAll('g.' + constants.containerClassName)
37705 .data(menuData.length > 0 ? [0] : []);
37706
37707 menus.enter().append('g')
37708 .classed(constants.containerClassName, true)
37709 .style('cursor', 'pointer');
37710
37711 menus.exit().each(function() {
37712 // Most components don't need to explicitly remove autoMargin, because
37713 // marginPushers does this - but updatemenu updates don't go through
37714 // a full replot so we need to explicitly remove it.
37715 // This is for removing *all* updatemenus, removing individuals is
37716 // handled below, in headerGroups.exit
37717 d3.select(this).selectAll('g.' + constants.headerGroupClassName)
37718 .each(clearAutoMargin);
37719 }).remove();
37720
37721 // return early if no update menus are visible
37722 if(menuData.length === 0) return;
37723
37724 // join header group
37725 var headerGroups = menus.selectAll('g.' + constants.headerGroupClassName)
37726 .data(menuData, keyFunction);
37727
37728 headerGroups.enter().append('g')
37729 .classed(constants.headerGroupClassName, true);
37730
37731 // draw dropdown button container
37732 var gButton = Lib.ensureSingle(menus, 'g', constants.dropdownButtonGroupClassName, function(s) {
37733 s.style('pointer-events', 'all');
37734 });
37735
37736 // find dimensions before plotting anything (this mutates menuOpts)
37737 for(var i = 0; i < menuData.length; i++) {
37738 var menuOpts = menuData[i];
37739 findDimensions(gd, menuOpts);
37740 }
37741
37742 // setup scrollbox
37743 var scrollBoxId = 'updatemenus' + fullLayout._uid;
37744 var scrollBox = new ScrollBox(gd, gButton, scrollBoxId);
37745
37746 // remove exiting header, remove dropped buttons and reset margins
37747 if(headerGroups.enter().size()) {
37748 // make sure gButton is on top of all headers
37749 gButton.node().parentNode.appendChild(gButton.node());
37750 gButton.call(removeAllButtons);
37751 }
37752
37753 headerGroups.exit().each(function(menuOpts) {
37754 gButton.call(removeAllButtons);
37755 clearAutoMargin(menuOpts);
37756 }).remove();
37757
37758 // draw headers!
37759 headerGroups.each(function(menuOpts) {
37760 var gHeader = d3.select(this);
37761
37762 var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
37763
37764 Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
37765 setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true);
37766 });
37767
37768 if(menuOpts.type === 'dropdown') {
37769 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
37770
37771 // if this menu is active, update the dropdown container
37772 if(isActive(gButton, menuOpts)) {
37773 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
37774 }
37775 } else {
37776 drawButtons(gd, gHeader, null, null, menuOpts);
37777 }
37778 });
37779};
37780
37781// Note that '_index' is set at the default step,
37782// it corresponds to the menu index in the user layout update menu container.
37783// Because a menu can be set invisible,
37784// this is a more 'consistent' field than the index in the menuData.
37785function keyFunction(menuOpts) {
37786 return menuOpts._index;
37787}
37788
37789function isFolded(gButton) {
37790 return +gButton.attr(constants.menuIndexAttrName) === -1;
37791}
37792
37793function isActive(gButton, menuOpts) {
37794 return +gButton.attr(constants.menuIndexAttrName) === menuOpts._index;
37795}
37796
37797function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex, isSilentUpdate) {
37798 // update 'active' attribute in menuOpts
37799 menuOpts.active = buttonIndex;
37800
37801 // due to templating, it's possible this slider doesn't even exist yet
37802 arrayEditor(gd.layout, constants.name, menuOpts)
37803 .applyUpdate('active', buttonIndex);
37804
37805 if(menuOpts.type === 'buttons') {
37806 drawButtons(gd, gHeader, null, null, menuOpts);
37807 } else if(menuOpts.type === 'dropdown') {
37808 // fold up buttons and redraw header
37809 gButton.attr(constants.menuIndexAttrName, '-1');
37810
37811 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
37812
37813 if(!isSilentUpdate) {
37814 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
37815 }
37816 }
37817}
37818
37819function drawHeader(gd, gHeader, gButton, scrollBox, menuOpts) {
37820 var header = Lib.ensureSingle(gHeader, 'g', constants.headerClassName, function(s) {
37821 s.style('pointer-events', 'all');
37822 });
37823
37824 var dims = menuOpts._dims;
37825 var active = menuOpts.active;
37826 var headerOpts = menuOpts.buttons[active] || constants.blankHeaderOpts;
37827 var posOpts = { y: menuOpts.pad.t, yPad: 0, x: menuOpts.pad.l, xPad: 0, index: 0 };
37828 var positionOverrides = {
37829 width: dims.headerWidth,
37830 height: dims.headerHeight
37831 };
37832
37833 header
37834 .call(drawItem, menuOpts, headerOpts, gd)
37835 .call(setItemPosition, menuOpts, posOpts, positionOverrides);
37836
37837 // draw drop arrow at the right edge
37838 var arrow = Lib.ensureSingle(gHeader, 'text', constants.headerArrowClassName, function(s) {
37839 s.classed('user-select-none', true)
37840 .attr('text-anchor', 'end')
37841 .call(Drawing.font, menuOpts.font)
37842 .text(constants.arrowSymbol[menuOpts.direction]);
37843 });
37844
37845 arrow.attr({
37846 x: dims.headerWidth - constants.arrowOffsetX + menuOpts.pad.l,
37847 y: dims.headerHeight / 2 + constants.textOffsetY + menuOpts.pad.t
37848 });
37849
37850 header.on('click', function() {
37851 gButton.call(removeAllButtons,
37852 String(isActive(gButton, menuOpts) ? -1 : menuOpts._index)
37853 );
37854
37855 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
37856 });
37857
37858 header.on('mouseover', function() {
37859 header.call(styleOnMouseOver);
37860 });
37861
37862 header.on('mouseout', function() {
37863 header.call(styleOnMouseOut, menuOpts);
37864 });
37865
37866 // translate header group
37867 Drawing.setTranslate(gHeader, dims.lx, dims.ly);
37868}
37869
37870function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) {
37871 // If this is a set of buttons, set pointer events = all since we play
37872 // some minor games with which container is which in order to simplify
37873 // the drawing of *either* buttons or menus
37874 if(!gButton) {
37875 gButton = gHeader;
37876 gButton.attr('pointer-events', 'all');
37877 }
37878
37879 var buttonData = (!isFolded(gButton) || menuOpts.type === 'buttons') ?
37880 menuOpts.buttons :
37881 [];
37882
37883 var klass = menuOpts.type === 'dropdown' ? constants.dropdownButtonClassName : constants.buttonClassName;
37884
37885 var buttons = gButton.selectAll('g.' + klass)
37886 .data(Lib.filterVisible(buttonData));
37887
37888 var enter = buttons.enter().append('g')
37889 .classed(klass, true);
37890
37891 var exit = buttons.exit();
37892
37893 if(menuOpts.type === 'dropdown') {
37894 enter.attr('opacity', '0')
37895 .transition()
37896 .attr('opacity', '1');
37897
37898 exit.transition()
37899 .attr('opacity', '0')
37900 .remove();
37901 } else {
37902 exit.remove();
37903 }
37904
37905 var x0 = 0;
37906 var y0 = 0;
37907 var dims = menuOpts._dims;
37908
37909 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
37910
37911 if(menuOpts.type === 'dropdown') {
37912 if(isVertical) {
37913 y0 = dims.headerHeight + constants.gapButtonHeader;
37914 } else {
37915 x0 = dims.headerWidth + constants.gapButtonHeader;
37916 }
37917 }
37918
37919 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'up') {
37920 y0 = -constants.gapButtonHeader + constants.gapButton - dims.openHeight;
37921 }
37922
37923 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'left') {
37924 x0 = -constants.gapButtonHeader + constants.gapButton - dims.openWidth;
37925 }
37926
37927 var posOpts = {
37928 x: dims.lx + x0 + menuOpts.pad.l,
37929 y: dims.ly + y0 + menuOpts.pad.t,
37930 yPad: constants.gapButton,
37931 xPad: constants.gapButton,
37932 index: 0,
37933 };
37934
37935 var scrollBoxPosition = {
37936 l: posOpts.x + menuOpts.borderwidth,
37937 t: posOpts.y + menuOpts.borderwidth
37938 };
37939
37940 buttons.each(function(buttonOpts, buttonIndex) {
37941 var button = d3.select(this);
37942
37943 button
37944 .call(drawItem, menuOpts, buttonOpts, gd)
37945 .call(setItemPosition, menuOpts, posOpts);
37946
37947 button.on('click', function() {
37948 // skip `dragend` events
37949 if(d3.event.defaultPrevented) return;
37950
37951 if(buttonOpts.execute) {
37952 if(buttonOpts.args2 && menuOpts.active === buttonIndex) {
37953 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1);
37954 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2);
37955 } else {
37956 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex);
37957 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);
37958 }
37959 }
37960
37961 gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active});
37962 });
37963
37964 button.on('mouseover', function() {
37965 button.call(styleOnMouseOver);
37966 });
37967
37968 button.on('mouseout', function() {
37969 button.call(styleOnMouseOut, menuOpts);
37970 buttons.call(styleButtons, menuOpts);
37971 });
37972 });
37973
37974 buttons.call(styleButtons, menuOpts);
37975
37976 if(isVertical) {
37977 scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth);
37978 scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t;
37979 } else {
37980 scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l;
37981 scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight);
37982 }
37983
37984 scrollBoxPosition.direction = menuOpts.direction;
37985
37986 if(scrollBox) {
37987 if(buttons.size()) {
37988 drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition);
37989 } else {
37990 hideScrollBox(scrollBox);
37991 }
37992 }
37993}
37994
37995function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) {
37996 // enable the scrollbox
37997 var direction = menuOpts.direction;
37998 var isVertical = (direction === 'up' || direction === 'down');
37999 var dims = menuOpts._dims;
38000
38001 var active = menuOpts.active;
38002 var translateX, translateY;
38003 var i;
38004 if(isVertical) {
38005 translateY = 0;
38006 for(i = 0; i < active; i++) {
38007 translateY += dims.heights[i] + constants.gapButton;
38008 }
38009 } else {
38010 translateX = 0;
38011 for(i = 0; i < active; i++) {
38012 translateX += dims.widths[i] + constants.gapButton;
38013 }
38014 }
38015
38016 scrollBox.enable(position, translateX, translateY);
38017
38018 if(scrollBox.hbar) {
38019 scrollBox.hbar
38020 .attr('opacity', '0')
38021 .transition()
38022 .attr('opacity', '1');
38023 }
38024
38025 if(scrollBox.vbar) {
38026 scrollBox.vbar
38027 .attr('opacity', '0')
38028 .transition()
38029 .attr('opacity', '1');
38030 }
38031}
38032
38033function hideScrollBox(scrollBox) {
38034 var hasHBar = !!scrollBox.hbar;
38035 var hasVBar = !!scrollBox.vbar;
38036
38037 if(hasHBar) {
38038 scrollBox.hbar
38039 .transition()
38040 .attr('opacity', '0')
38041 .each('end', function() {
38042 hasHBar = false;
38043 if(!hasVBar) scrollBox.disable();
38044 });
38045 }
38046
38047 if(hasVBar) {
38048 scrollBox.vbar
38049 .transition()
38050 .attr('opacity', '0')
38051 .each('end', function() {
38052 hasVBar = false;
38053 if(!hasHBar) scrollBox.disable();
38054 });
38055 }
38056}
38057
38058function drawItem(item, menuOpts, itemOpts, gd) {
38059 item.call(drawItemRect, menuOpts)
38060 .call(drawItemText, menuOpts, itemOpts, gd);
38061}
38062
38063function drawItemRect(item, menuOpts) {
38064 var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) {
38065 s.attr({
38066 rx: constants.rx,
38067 ry: constants.ry,
38068 'shape-rendering': 'crispEdges'
38069 });
38070 });
38071
38072 rect.call(Color.stroke, menuOpts.bordercolor)
38073 .call(Color.fill, menuOpts.bgcolor)
38074 .style('stroke-width', menuOpts.borderwidth + 'px');
38075}
38076
38077function drawItemText(item, menuOpts, itemOpts, gd) {
38078 var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) {
38079 s.classed('user-select-none', true)
38080 .attr({
38081 'text-anchor': 'start',
38082 'data-notex': 1
38083 });
38084 });
38085
38086 var tx = itemOpts.label;
38087 var _meta = gd._fullLayout._meta;
38088 if(_meta) tx = Lib.templateString(tx, _meta);
38089
38090 text.call(Drawing.font, menuOpts.font)
38091 .text(tx)
38092 .call(svgTextUtils.convertToTspans, gd);
38093}
38094
38095function styleButtons(buttons, menuOpts) {
38096 var active = menuOpts.active;
38097
38098 buttons.each(function(buttonOpts, i) {
38099 var button = d3.select(this);
38100
38101 if(i === active && menuOpts.showactive) {
38102 button.select('rect.' + constants.itemRectClassName)
38103 .call(Color.fill, constants.activeColor);
38104 }
38105 });
38106}
38107
38108function styleOnMouseOver(item) {
38109 item.select('rect.' + constants.itemRectClassName)
38110 .call(Color.fill, constants.hoverColor);
38111}
38112
38113function styleOnMouseOut(item, menuOpts) {
38114 item.select('rect.' + constants.itemRectClassName)
38115 .call(Color.fill, menuOpts.bgcolor);
38116}
38117
38118// find item dimensions (this mutates menuOpts)
38119function findDimensions(gd, menuOpts) {
38120 var dims = menuOpts._dims = {
38121 width1: 0,
38122 height1: 0,
38123 heights: [],
38124 widths: [],
38125 totalWidth: 0,
38126 totalHeight: 0,
38127 openWidth: 0,
38128 openHeight: 0,
38129 lx: 0,
38130 ly: 0
38131 };
38132
38133 var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName)
38134 .data(Lib.filterVisible(menuOpts.buttons));
38135
38136 fakeButtons.enter().append('g')
38137 .classed(constants.dropdownButtonClassName, true);
38138
38139 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
38140
38141 // loop over fake buttons to find width / height
38142 fakeButtons.each(function(buttonOpts, i) {
38143 var button = d3.select(this);
38144
38145 button.call(drawItem, menuOpts, buttonOpts, gd);
38146
38147 var text = button.select('.' + constants.itemTextClassName);
38148
38149 // width is given by max width of all buttons
38150 var tWidth = text.node() && Drawing.bBox(text.node()).width;
38151 var wEff = Math.max(tWidth + constants.textPadX, constants.minWidth);
38152
38153 // height is determined by item text
38154 var tHeight = menuOpts.font.size * LINE_SPACING;
38155 var tLines = svgTextUtils.lineCount(text);
38156 var hEff = Math.max(tHeight * tLines, constants.minHeight) + constants.textOffsetY;
38157
38158 hEff = Math.ceil(hEff);
38159 wEff = Math.ceil(wEff);
38160
38161 // Store per-item sizes since a row of horizontal buttons, for example,
38162 // don't all need to be the same width:
38163 dims.widths[i] = wEff;
38164 dims.heights[i] = hEff;
38165
38166 // Height and width of individual element:
38167 dims.height1 = Math.max(dims.height1, hEff);
38168 dims.width1 = Math.max(dims.width1, wEff);
38169
38170 if(isVertical) {
38171 dims.totalWidth = Math.max(dims.totalWidth, wEff);
38172 dims.openWidth = dims.totalWidth;
38173 dims.totalHeight += hEff + constants.gapButton;
38174 dims.openHeight += hEff + constants.gapButton;
38175 } else {
38176 dims.totalWidth += wEff + constants.gapButton;
38177 dims.openWidth += wEff + constants.gapButton;
38178 dims.totalHeight = Math.max(dims.totalHeight, hEff);
38179 dims.openHeight = dims.totalHeight;
38180 }
38181 });
38182
38183 if(isVertical) {
38184 dims.totalHeight -= constants.gapButton;
38185 } else {
38186 dims.totalWidth -= constants.gapButton;
38187 }
38188
38189
38190 dims.headerWidth = dims.width1 + constants.arrowPadX;
38191 dims.headerHeight = dims.height1;
38192
38193 if(menuOpts.type === 'dropdown') {
38194 if(isVertical) {
38195 dims.width1 += constants.arrowPadX;
38196 dims.totalHeight = dims.height1;
38197 } else {
38198 dims.totalWidth = dims.width1;
38199 }
38200 dims.totalWidth += constants.arrowPadX;
38201 }
38202
38203 fakeButtons.remove();
38204
38205 var paddedWidth = dims.totalWidth + menuOpts.pad.l + menuOpts.pad.r;
38206 var paddedHeight = dims.totalHeight + menuOpts.pad.t + menuOpts.pad.b;
38207
38208 var graphSize = gd._fullLayout._size;
38209 dims.lx = graphSize.l + graphSize.w * menuOpts.x;
38210 dims.ly = graphSize.t + graphSize.h * (1 - menuOpts.y);
38211
38212 var xanchor = 'left';
38213 if(Lib.isRightAnchor(menuOpts)) {
38214 dims.lx -= paddedWidth;
38215 xanchor = 'right';
38216 }
38217 if(Lib.isCenterAnchor(menuOpts)) {
38218 dims.lx -= paddedWidth / 2;
38219 xanchor = 'center';
38220 }
38221
38222 var yanchor = 'top';
38223 if(Lib.isBottomAnchor(menuOpts)) {
38224 dims.ly -= paddedHeight;
38225 yanchor = 'bottom';
38226 }
38227 if(Lib.isMiddleAnchor(menuOpts)) {
38228 dims.ly -= paddedHeight / 2;
38229 yanchor = 'middle';
38230 }
38231
38232 dims.totalWidth = Math.ceil(dims.totalWidth);
38233 dims.totalHeight = Math.ceil(dims.totalHeight);
38234 dims.lx = Math.round(dims.lx);
38235 dims.ly = Math.round(dims.ly);
38236
38237 Plots.autoMargin(gd, autoMarginId(menuOpts), {
38238 x: menuOpts.x,
38239 y: menuOpts.y,
38240 l: paddedWidth * ({right: 1, center: 0.5}[xanchor] || 0),
38241 r: paddedWidth * ({left: 1, center: 0.5}[xanchor] || 0),
38242 b: paddedHeight * ({top: 1, middle: 0.5}[yanchor] || 0),
38243 t: paddedHeight * ({bottom: 1, middle: 0.5}[yanchor] || 0)
38244 });
38245}
38246
38247function autoMarginId(menuOpts) {
38248 return constants.autoMarginIdRoot + menuOpts._index;
38249}
38250
38251// set item positions (mutates posOpts)
38252function setItemPosition(item, menuOpts, posOpts, overrideOpts) {
38253 overrideOpts = overrideOpts || {};
38254 var rect = item.select('.' + constants.itemRectClassName);
38255 var text = item.select('.' + constants.itemTextClassName);
38256 var borderWidth = menuOpts.borderwidth;
38257 var index = posOpts.index;
38258 var dims = menuOpts._dims;
38259
38260 Drawing.setTranslate(item, borderWidth + posOpts.x, borderWidth + posOpts.y);
38261
38262 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
38263 var finalHeight = overrideOpts.height || (isVertical ? dims.heights[index] : dims.height1);
38264
38265 rect.attr({
38266 x: 0,
38267 y: 0,
38268 width: overrideOpts.width || (isVertical ? dims.width1 : dims.widths[index]),
38269 height: finalHeight
38270 });
38271
38272 var tHeight = menuOpts.font.size * LINE_SPACING;
38273 var tLines = svgTextUtils.lineCount(text);
38274 var spanOffset = ((tLines - 1) * tHeight / 2);
38275
38276 svgTextUtils.positionText(text, constants.textOffsetX,
38277 finalHeight / 2 - spanOffset + constants.textOffsetY);
38278
38279 if(isVertical) {
38280 posOpts.y += dims.heights[index] + posOpts.yPad;
38281 } else {
38282 posOpts.x += dims.widths[index] + posOpts.xPad;
38283 }
38284
38285 posOpts.index++;
38286}
38287
38288function removeAllButtons(gButton, newMenuIndexAttr) {
38289 gButton
38290 .attr(constants.menuIndexAttrName, newMenuIndexAttr || '-1')
38291 .selectAll('g.' + constants.dropdownButtonClassName).remove();
38292}
38293
38294},{"../../constants/alignment":152,"../../lib":177,"../../lib/svg_text_utils":198,"../../plot_api/plot_template":212,"../../plots/plots":263,"../color":50,"../drawing":72,"./constants":147,"./scrollbox":151,"d3":13}],150:[function(_dereq_,module,exports){
38295arguments[4][144][0].apply(exports,arguments)
38296},{"./attributes":146,"./constants":147,"./defaults":148,"./draw":149,"dup":144}],151:[function(_dereq_,module,exports){
38297/**
38298* Copyright 2012-2020, Plotly, Inc.
38299* All rights reserved.
38300*
38301* This source code is licensed under the MIT license found in the
38302* LICENSE file in the root directory of this source tree.
38303*/
38304
38305'use strict';
38306
38307module.exports = ScrollBox;
38308
38309var d3 = _dereq_('d3');
38310
38311var Color = _dereq_('../color');
38312var Drawing = _dereq_('../drawing');
38313
38314var Lib = _dereq_('../../lib');
38315
38316/**
38317 * Helper class to setup a scroll box
38318 *
38319 * @class
38320 * @param gd Plotly's graph div
38321 * @param container Container to be scroll-boxed (as a D3 selection)
38322 * @param {string} id Id for the clip path to implement the scroll box
38323 */
38324function ScrollBox(gd, container, id) {
38325 this.gd = gd;
38326 this.container = container;
38327 this.id = id;
38328
38329 // See ScrollBox.prototype.enable for further definition
38330 this.position = null; // scrollbox position
38331 this.translateX = null; // scrollbox horizontal translation
38332 this.translateY = null; // scrollbox vertical translation
38333 this.hbar = null; // horizontal scrollbar D3 selection
38334 this.vbar = null; // vertical scrollbar D3 selection
38335
38336 // <rect> element to capture pointer events
38337 this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
38338
38339 this.bg.exit()
38340 .on('.drag', null)
38341 .on('wheel', null)
38342 .remove();
38343
38344 this.bg.enter().append('rect')
38345 .classed('scrollbox-bg', true)
38346 .style('pointer-events', 'all')
38347 .attr({
38348 opacity: 0,
38349 x: 0,
38350 y: 0,
38351 width: 0,
38352 height: 0
38353 });
38354}
38355
38356// scroll bar dimensions
38357ScrollBox.barWidth = 2;
38358ScrollBox.barLength = 20;
38359ScrollBox.barRadius = 2;
38360ScrollBox.barPad = 1;
38361ScrollBox.barColor = '#808BA4';
38362
38363/**
38364 * If needed, setup a clip path and scrollbars
38365 *
38366 * @method
38367 * @param {Object} position
38368 * @param {number} position.l Left side position (in pixels)
38369 * @param {number} position.t Top side (in pixels)
38370 * @param {number} position.w Width (in pixels)
38371 * @param {number} position.h Height (in pixels)
38372 * @param {string} [position.direction='down']
38373 * Either 'down', 'left', 'right' or 'up'
38374 * @param {number} [translateX=0] Horizontal offset (in pixels)
38375 * @param {number} [translateY=0] Vertical offset (in pixels)
38376 */
38377ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
38378 var fullLayout = this.gd._fullLayout;
38379 var fullWidth = fullLayout.width;
38380 var fullHeight = fullLayout.height;
38381
38382 // compute position of scrollbox
38383 this.position = position;
38384
38385 var l = this.position.l;
38386 var w = this.position.w;
38387 var t = this.position.t;
38388 var h = this.position.h;
38389 var direction = this.position.direction;
38390 var isDown = (direction === 'down');
38391 var isLeft = (direction === 'left');
38392 var isRight = (direction === 'right');
38393 var isUp = (direction === 'up');
38394 var boxW = w;
38395 var boxH = h;
38396 var boxL, boxR;
38397 var boxT, boxB;
38398
38399 if(!isDown && !isLeft && !isRight && !isUp) {
38400 this.position.direction = 'down';
38401 isDown = true;
38402 }
38403
38404 var isVertical = isDown || isUp;
38405 if(isVertical) {
38406 boxL = l;
38407 boxR = boxL + boxW;
38408
38409 if(isDown) {
38410 // anchor to top side
38411 boxT = t;
38412 boxB = Math.min(boxT + boxH, fullHeight);
38413 boxH = boxB - boxT;
38414 } else {
38415 // anchor to bottom side
38416 boxB = t + boxH;
38417 boxT = Math.max(boxB - boxH, 0);
38418 boxH = boxB - boxT;
38419 }
38420 } else {
38421 boxT = t;
38422 boxB = boxT + boxH;
38423
38424 if(isLeft) {
38425 // anchor to right side
38426 boxR = l + boxW;
38427 boxL = Math.max(boxR - boxW, 0);
38428 boxW = boxR - boxL;
38429 } else {
38430 // anchor to left side
38431 boxL = l;
38432 boxR = Math.min(boxL + boxW, fullWidth);
38433 boxW = boxR - boxL;
38434 }
38435 }
38436
38437 this._box = {
38438 l: boxL,
38439 t: boxT,
38440 w: boxW,
38441 h: boxH
38442 };
38443
38444 // compute position of horizontal scroll bar
38445 var needsHorizontalScrollBar = (w > boxW);
38446 var hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad;
38447 var hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad;
38448 // draw horizontal scrollbar on the bottom side
38449 var hbarL = l;
38450 var hbarT = t + h;
38451
38452 if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH;
38453
38454 var hbar = this.container.selectAll('rect.scrollbar-horizontal').data(
38455 (needsHorizontalScrollBar) ? [0] : []);
38456
38457 hbar.exit()
38458 .on('.drag', null)
38459 .remove();
38460
38461 hbar.enter().append('rect')
38462 .classed('scrollbar-horizontal', true)
38463 .call(Color.fill, ScrollBox.barColor);
38464
38465 if(needsHorizontalScrollBar) {
38466 this.hbar = hbar.attr({
38467 'rx': ScrollBox.barRadius,
38468 'ry': ScrollBox.barRadius,
38469 'x': hbarL,
38470 'y': hbarT,
38471 'width': hbarW,
38472 'height': hbarH
38473 });
38474
38475 // hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax
38476 this._hbarXMin = hbarL + hbarW / 2;
38477 this._hbarTranslateMax = boxW - hbarW;
38478 } else {
38479 delete this.hbar;
38480 delete this._hbarXMin;
38481 delete this._hbarTranslateMax;
38482 }
38483
38484 // compute position of vertical scroll bar
38485 var needsVerticalScrollBar = (h > boxH);
38486 var vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad;
38487 var vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad;
38488 // draw vertical scrollbar on the right side
38489 var vbarL = l + w;
38490 var vbarT = t;
38491
38492 if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW;
38493
38494 var vbar = this.container.selectAll('rect.scrollbar-vertical').data(
38495 (needsVerticalScrollBar) ? [0] : []);
38496
38497 vbar.exit()
38498 .on('.drag', null)
38499 .remove();
38500
38501 vbar.enter().append('rect')
38502 .classed('scrollbar-vertical', true)
38503 .call(Color.fill, ScrollBox.barColor);
38504
38505 if(needsVerticalScrollBar) {
38506 this.vbar = vbar.attr({
38507 'rx': ScrollBox.barRadius,
38508 'ry': ScrollBox.barRadius,
38509 'x': vbarL,
38510 'y': vbarT,
38511 'width': vbarW,
38512 'height': vbarH
38513 });
38514
38515 // vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax
38516 this._vbarYMin = vbarT + vbarH / 2;
38517 this._vbarTranslateMax = boxH - vbarH;
38518 } else {
38519 delete this.vbar;
38520 delete this._vbarYMin;
38521 delete this._vbarTranslateMax;
38522 }
38523
38524 // setup a clip path (if scroll bars are needed)
38525 var clipId = this.id;
38526 var clipL = boxL - 0.5;
38527 var clipR = (needsVerticalScrollBar) ? boxR + vbarW + 0.5 : boxR + 0.5;
38528 var clipT = boxT - 0.5;
38529 var clipB = (needsHorizontalScrollBar) ? boxB + hbarH + 0.5 : boxB + 0.5;
38530
38531 var clipPath = fullLayout._topdefs.selectAll('#' + clipId)
38532 .data((needsHorizontalScrollBar || needsVerticalScrollBar) ? [0] : []);
38533
38534 clipPath.exit().remove();
38535
38536 clipPath.enter()
38537 .append('clipPath').attr('id', clipId)
38538 .append('rect');
38539
38540 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
38541 this._clipRect = clipPath.select('rect').attr({
38542 x: Math.floor(clipL),
38543 y: Math.floor(clipT),
38544 width: Math.ceil(clipR) - Math.floor(clipL),
38545 height: Math.ceil(clipB) - Math.floor(clipT)
38546 });
38547
38548 this.container.call(Drawing.setClipUrl, clipId, this.gd);
38549
38550 this.bg.attr({
38551 x: l,
38552 y: t,
38553 width: w,
38554 height: h
38555 });
38556 } else {
38557 this.bg.attr({
38558 width: 0,
38559 height: 0
38560 });
38561 this.container
38562 .on('wheel', null)
38563 .on('.drag', null)
38564 .call(Drawing.setClipUrl, null);
38565 delete this._clipRect;
38566 }
38567
38568 // set up drag listeners (if scroll bars are needed)
38569 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
38570 var onBoxDrag = d3.behavior.drag()
38571 .on('dragstart', function() {
38572 d3.event.sourceEvent.preventDefault();
38573 })
38574 .on('drag', this._onBoxDrag.bind(this));
38575
38576 this.container
38577 .on('wheel', null)
38578 .on('wheel', this._onBoxWheel.bind(this))
38579 .on('.drag', null)
38580 .call(onBoxDrag);
38581
38582 var onBarDrag = d3.behavior.drag()
38583 .on('dragstart', function() {
38584 d3.event.sourceEvent.preventDefault();
38585 d3.event.sourceEvent.stopPropagation();
38586 })
38587 .on('drag', this._onBarDrag.bind(this));
38588
38589 if(needsHorizontalScrollBar) {
38590 this.hbar
38591 .on('.drag', null)
38592 .call(onBarDrag);
38593 }
38594
38595 if(needsVerticalScrollBar) {
38596 this.vbar
38597 .on('.drag', null)
38598 .call(onBarDrag);
38599 }
38600 }
38601
38602 // set scrollbox translation
38603 this.setTranslate(translateX, translateY);
38604};
38605
38606/**
38607 * If present, remove clip-path and scrollbars
38608 *
38609 * @method
38610 */
38611ScrollBox.prototype.disable = function disable() {
38612 if(this.hbar || this.vbar) {
38613 this.bg.attr({
38614 width: 0,
38615 height: 0
38616 });
38617 this.container
38618 .on('wheel', null)
38619 .on('.drag', null)
38620 .call(Drawing.setClipUrl, null);
38621 delete this._clipRect;
38622 }
38623
38624 if(this.hbar) {
38625 this.hbar.on('.drag', null);
38626 this.hbar.remove();
38627 delete this.hbar;
38628 delete this._hbarXMin;
38629 delete this._hbarTranslateMax;
38630 }
38631
38632 if(this.vbar) {
38633 this.vbar.on('.drag', null);
38634 this.vbar.remove();
38635 delete this.vbar;
38636 delete this._vbarYMin;
38637 delete this._vbarTranslateMax;
38638 }
38639};
38640
38641/**
38642 * Handles scroll box drag events
38643 *
38644 * @method
38645 */
38646ScrollBox.prototype._onBoxDrag = function _onBoxDrag() {
38647 var translateX = this.translateX;
38648 var translateY = this.translateY;
38649
38650 if(this.hbar) {
38651 translateX -= d3.event.dx;
38652 }
38653
38654 if(this.vbar) {
38655 translateY -= d3.event.dy;
38656 }
38657
38658 this.setTranslate(translateX, translateY);
38659};
38660
38661/**
38662 * Handles scroll box wheel events
38663 *
38664 * @method
38665 */
38666ScrollBox.prototype._onBoxWheel = function _onBoxWheel() {
38667 var translateX = this.translateX;
38668 var translateY = this.translateY;
38669
38670 if(this.hbar) {
38671 translateX += d3.event.deltaY;
38672 }
38673
38674 if(this.vbar) {
38675 translateY += d3.event.deltaY;
38676 }
38677
38678 this.setTranslate(translateX, translateY);
38679};
38680
38681/**
38682 * Handles scroll bar drag events
38683 *
38684 * @method
38685 */
38686ScrollBox.prototype._onBarDrag = function _onBarDrag() {
38687 var translateX = this.translateX;
38688 var translateY = this.translateY;
38689
38690 if(this.hbar) {
38691 var xMin = translateX + this._hbarXMin;
38692 var xMax = xMin + this._hbarTranslateMax;
38693 var x = Lib.constrain(d3.event.x, xMin, xMax);
38694 var xf = (x - xMin) / (xMax - xMin);
38695
38696 var translateXMax = this.position.w - this._box.w;
38697
38698 translateX = xf * translateXMax;
38699 }
38700
38701 if(this.vbar) {
38702 var yMin = translateY + this._vbarYMin;
38703 var yMax = yMin + this._vbarTranslateMax;
38704 var y = Lib.constrain(d3.event.y, yMin, yMax);
38705 var yf = (y - yMin) / (yMax - yMin);
38706
38707 var translateYMax = this.position.h - this._box.h;
38708
38709 translateY = yf * translateYMax;
38710 }
38711
38712 this.setTranslate(translateX, translateY);
38713};
38714
38715/**
38716 * Set clip path and scroll bar translate transform
38717 *
38718 * @method
38719 * @param {number} [translateX=0] Horizontal offset (in pixels)
38720 * @param {number} [translateY=0] Vertical offset (in pixels)
38721 */
38722ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) {
38723 // store translateX and translateY (needed by mouse event handlers)
38724 var translateXMax = this.position.w - this._box.w;
38725 var translateYMax = this.position.h - this._box.h;
38726
38727 translateX = Lib.constrain(translateX || 0, 0, translateXMax);
38728 translateY = Lib.constrain(translateY || 0, 0, translateYMax);
38729
38730 this.translateX = translateX;
38731 this.translateY = translateY;
38732
38733 this.container.call(Drawing.setTranslate,
38734 this._box.l - this.position.l - translateX,
38735 this._box.t - this.position.t - translateY);
38736
38737 if(this._clipRect) {
38738 this._clipRect.attr({
38739 x: Math.floor(this.position.l + translateX - 0.5),
38740 y: Math.floor(this.position.t + translateY - 0.5)
38741 });
38742 }
38743
38744 if(this.hbar) {
38745 var xf = translateX / translateXMax;
38746
38747 this.hbar.call(Drawing.setTranslate,
38748 translateX + xf * this._hbarTranslateMax,
38749 translateY);
38750 }
38751
38752 if(this.vbar) {
38753 var yf = translateY / translateYMax;
38754
38755 this.vbar.call(Drawing.setTranslate,
38756 translateX,
38757 translateY + yf * this._vbarTranslateMax);
38758 }
38759};
38760
38761},{"../../lib":177,"../color":50,"../drawing":72,"d3":13}],152:[function(_dereq_,module,exports){
38762/**
38763* Copyright 2012-2020, Plotly, Inc.
38764* All rights reserved.
38765*
38766* This source code is licensed under the MIT license found in the
38767* LICENSE file in the root directory of this source tree.
38768*/
38769
38770'use strict';
38771
38772// fraction of some size to get to a named position
38773module.exports = {
38774 // from bottom left: this is the origin of our paper-reference
38775 // positioning system
38776 FROM_BL: {
38777 left: 0,
38778 center: 0.5,
38779 right: 1,
38780 bottom: 0,
38781 middle: 0.5,
38782 top: 1
38783 },
38784 // from top left: this is the screen pixel positioning origin
38785 FROM_TL: {
38786 left: 0,
38787 center: 0.5,
38788 right: 1,
38789 bottom: 1,
38790 middle: 0.5,
38791 top: 0
38792 },
38793 // from bottom right: sometimes you just need the opposite of ^^
38794 FROM_BR: {
38795 left: 1,
38796 center: 0.5,
38797 right: 0,
38798 bottom: 0,
38799 middle: 0.5,
38800 top: 1
38801 },
38802 // multiple of fontSize to get the vertical offset between lines
38803 LINE_SPACING: 1.3,
38804
38805 // multiple of fontSize to shift from the baseline
38806 // to the cap (captical letter) line
38807 // (to use when we don't calculate this shift from Drawing.bBox)
38808 // This is an approximation since in reality cap height can differ
38809 // from font to font. However, according to Wikipedia
38810 // an "average" font might have a cap height of 70% of the em
38811 // https://en.wikipedia.org/wiki/Em_(typography)#History
38812 CAP_SHIFT: 0.70,
38813
38814 // half the cap height (distance between baseline and cap line)
38815 // of an "average" font (for more info see above).
38816 MID_SHIFT: 0.35,
38817
38818 OPPOSITE_SIDE: {
38819 left: 'right',
38820 right: 'left',
38821 top: 'bottom',
38822 bottom: 'top'
38823 }
38824};
38825
38826},{}],153:[function(_dereq_,module,exports){
38827/**
38828* Copyright 2012-2020, Plotly, Inc.
38829* All rights reserved.
38830*
38831* This source code is licensed under the MIT license found in the
38832* LICENSE file in the root directory of this source tree.
38833*/
38834
38835'use strict';
38836
38837module.exports = {
38838 FORMAT_LINK: 'https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format',
38839 DATE_FORMAT_LINK: 'https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format'
38840};
38841
38842},{}],154:[function(_dereq_,module,exports){
38843/**
38844* Copyright 2012-2020, Plotly, Inc.
38845* All rights reserved.
38846*
38847* This source code is licensed under the MIT license found in the
38848* LICENSE file in the root directory of this source tree.
38849*/
38850
38851'use strict';
38852
38853
38854module.exports = {
38855 /**
38856 * Timing information for interactive elements
38857 */
38858 SHOW_PLACEHOLDER: 100,
38859 HIDE_PLACEHOLDER: 1000,
38860
38861 // opacity dimming fraction for points that are not in selection
38862 DESELECTDIM: 0.2
38863};
38864
38865},{}],155:[function(_dereq_,module,exports){
38866/**
38867* Copyright 2012-2020, Plotly, Inc.
38868* All rights reserved.
38869*
38870* This source code is licensed under the MIT license found in the
38871* LICENSE file in the root directory of this source tree.
38872*/
38873
38874'use strict';
38875
38876
38877module.exports = {
38878 /**
38879 * Standardize all missing data in calcdata to use undefined
38880 * never null or NaN.
38881 * That way we can use !==undefined, or !== BADNUM,
38882 * to test for real data
38883 */
38884 BADNUM: undefined,
38885
38886 /*
38887 * Limit certain operations to well below floating point max value
38888 * to avoid glitches: Make sure that even when you multiply it by the
38889 * number of pixels on a giant screen it still works
38890 */
38891 FP_SAFE: Number.MAX_VALUE / 10000,
38892
38893 /*
38894 * conversion of date units to milliseconds
38895 * year and month constants are marked "AVG"
38896 * to remind us that not all years and months
38897 * have the same length
38898 */
38899 ONEAVGYEAR: 31557600000, // 365.25 days
38900 ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR
38901 ONEDAY: 86400000,
38902 ONEHOUR: 3600000,
38903 ONEMIN: 60000,
38904 ONESEC: 1000,
38905
38906 /*
38907 * For fast conversion btwn world calendars and epoch ms, the Julian Day Number
38908 * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD()
38909 */
38910 EPOCHJD: 2440587.5,
38911
38912 /*
38913 * Are two values nearly equal? Compare to 1PPM
38914 */
38915 ALMOST_EQUAL: 1 - 1e-6,
38916
38917 /*
38918 * If we're asked to clip a non-positive log value, how far off-screen
38919 * do we put it?
38920 */
38921 LOG_CLIP: 10,
38922
38923 /*
38924 * not a number, but for displaying numbers: the "minus sign" symbol is
38925 * wider than the regular ascii dash "-"
38926 */
38927 MINUS_SIGN: '\u2212'
38928};
38929
38930},{}],156:[function(_dereq_,module,exports){
38931/**
38932* Copyright 2012-2020, Plotly, Inc.
38933* All rights reserved.
38934*
38935* This source code is licensed under the MIT license found in the
38936* LICENSE file in the root directory of this source tree.
38937*/
38938
38939
38940'use strict';
38941
38942
38943exports.xmlns = 'http://www.w3.org/2000/xmlns/';
38944exports.svg = 'http://www.w3.org/2000/svg';
38945exports.xlink = 'http://www.w3.org/1999/xlink';
38946
38947// the 'old' d3 quirk got fix in v3.5.7
38948// https://github.com/mbostock/d3/commit/a6f66e9dd37f764403fc7c1f26be09ab4af24fed
38949exports.svgAttrs = {
38950 xmlns: exports.svg,
38951 'xmlns:xlink': exports.xlink
38952};
38953
38954},{}],157:[function(_dereq_,module,exports){
38955/**
38956* Copyright 2012-2020, Plotly, Inc.
38957* All rights reserved.
38958*
38959* This source code is licensed under the MIT license found in the
38960* LICENSE file in the root directory of this source tree.
38961*/
38962
38963'use strict';
38964
38965exports.version = _dereq_('./version').version;
38966
38967// inject promise polyfill
38968_dereq_('es6-promise').polyfill();
38969
38970// inject plot css
38971_dereq_('../build/plotcss');
38972
38973// inject default MathJax config
38974_dereq_('./fonts/mathjax_config')();
38975
38976// include registry module and expose register method
38977var Registry = _dereq_('./registry');
38978var register = exports.register = Registry.register;
38979
38980// expose plot api methods
38981var plotApi = _dereq_('./plot_api');
38982var methodNames = Object.keys(plotApi);
38983for(var i = 0; i < methodNames.length; i++) {
38984 var name = methodNames[i];
38985 // _ -> private API methods, but still registered for internal use
38986 if(name.charAt(0) !== '_') exports[name] = plotApi[name];
38987 register({
38988 moduleType: 'apiMethod',
38989 name: name,
38990 fn: plotApi[name]
38991 });
38992}
38993
38994// scatter is the only trace included by default
38995register(_dereq_('./traces/scatter'));
38996
38997// register all registrable components modules
38998register([
38999 _dereq_('./components/legend'),
39000 _dereq_('./components/fx'), // fx needs to come after legend
39001 _dereq_('./components/annotations'),
39002 _dereq_('./components/annotations3d'),
39003 _dereq_('./components/shapes'),
39004 _dereq_('./components/images'),
39005 _dereq_('./components/updatemenus'),
39006 _dereq_('./components/sliders'),
39007 _dereq_('./components/rangeslider'),
39008 _dereq_('./components/rangeselector'),
39009 _dereq_('./components/grid'),
39010 _dereq_('./components/errorbars'),
39011 _dereq_('./components/colorscale'),
39012 _dereq_('./components/colorbar')
39013]);
39014
39015// locales en and en-US are required for default behavior
39016register([
39017 _dereq_('./locale-en'),
39018 _dereq_('./locale-en-us')
39019]);
39020
39021// locales that are present in the window should be loaded
39022if(window.PlotlyLocales && Array.isArray(window.PlotlyLocales)) {
39023 register(window.PlotlyLocales);
39024 delete window.PlotlyLocales;
39025}
39026
39027// plot icons
39028exports.Icons = _dereq_('./fonts/ploticon');
39029
39030// unofficial 'beta' plot methods, use at your own risk
39031exports.Plots = _dereq_('./plots/plots');
39032exports.Fx = _dereq_('./components/fx');
39033exports.Snapshot = _dereq_('./snapshot');
39034exports.PlotSchema = _dereq_('./plot_api/plot_schema');
39035exports.Queue = _dereq_('./lib/queue');
39036
39037// export d3 used in the bundle
39038exports.d3 = _dereq_('d3');
39039
39040},{"../build/plotcss":1,"./components/annotations":43,"./components/annotations3d":48,"./components/colorbar":56,"./components/colorscale":62,"./components/errorbars":78,"./components/fx":90,"./components/grid":94,"./components/images":99,"./components/legend":107,"./components/rangeselector":118,"./components/rangeslider":125,"./components/shapes":139,"./components/sliders":144,"./components/updatemenus":150,"./fonts/mathjax_config":158,"./fonts/ploticon":159,"./lib/queue":191,"./locale-en":203,"./locale-en-us":202,"./plot_api":207,"./plot_api/plot_schema":211,"./plots/plots":263,"./registry":272,"./snapshot":277,"./traces/scatter":306,"./version":331,"d3":13,"es6-promise":14}],158:[function(_dereq_,module,exports){
39041/**
39042* Copyright 2012-2020, Plotly, Inc.
39043* All rights reserved.
39044*
39045* This source code is licensed under the MIT license found in the
39046* LICENSE file in the root directory of this source tree.
39047*/
39048
39049'use strict';
39050
39051/* global MathJax:false */
39052
39053module.exports = function() {
39054 if(typeof MathJax !== 'undefined') {
39055 var globalConfig = (window.PlotlyConfig || {}).MathJaxConfig !== 'local';
39056
39057 if(globalConfig) {
39058 MathJax.Hub.Config({
39059 messageStyle: 'none',
39060 skipStartupTypeset: true,
39061 displayAlign: 'left',
39062 tex2jax: {
39063 inlineMath: [['$', '$'], ['\\(', '\\)']]
39064 }
39065 });
39066 MathJax.Hub.Configured();
39067 }
39068 }
39069};
39070
39071},{}],159:[function(_dereq_,module,exports){
39072/**
39073* Copyright 2012-2020, Plotly, Inc.
39074* All rights reserved.
39075*
39076* This source code is licensed under the MIT license found in the
39077* LICENSE file in the root directory of this source tree.
39078*/
39079
39080'use strict';
39081
39082module.exports = {
39083 'undo': {
39084 'width': 857.1,
39085 'height': 1000,
39086 'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z',
39087 'transform': 'matrix(1 0 0 -1 0 850)'
39088 },
39089 'home': {
39090 'width': 928.6,
39091 'height': 1000,
39092 'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z',
39093 'transform': 'matrix(1 0 0 -1 0 850)'
39094 },
39095 'camera-retro': {
39096 'width': 1000,
39097 'height': 1000,
39098 'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z',
39099 'transform': 'matrix(1 0 0 -1 0 850)'
39100 },
39101 'zoombox': {
39102 'width': 1000,
39103 'height': 1000,
39104 'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z',
39105 'transform': 'matrix(1 0 0 -1 0 850)'
39106 },
39107 'pan': {
39108 'width': 1000,
39109 'height': 1000,
39110 'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z',
39111 'transform': 'matrix(1 0 0 -1 0 850)'
39112 },
39113 'zoom_plus': {
39114 'width': 875,
39115 'height': 1000,
39116 'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z',
39117 'transform': 'matrix(1 0 0 -1 0 850)'
39118 },
39119 'zoom_minus': {
39120 'width': 875,
39121 'height': 1000,
39122 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
39123 'transform': 'matrix(1 0 0 -1 0 850)'
39124 },
39125 'autoscale': {
39126 'width': 1000,
39127 'height': 1000,
39128 'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z',
39129 'transform': 'matrix(1 0 0 -1 0 850)'
39130 },
39131 'tooltip_basic': {
39132 'width': 1500,
39133 'height': 1000,
39134 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z',
39135 'transform': 'matrix(1 0 0 -1 0 850)'
39136 },
39137 'tooltip_compare': {
39138 'width': 1125,
39139 'height': 1000,
39140 'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z',
39141 'transform': 'matrix(1 0 0 -1 0 850)'
39142 },
39143 'plotlylogo': {
39144 'width': 1542,
39145 'height': 1000,
39146 'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z',
39147 'transform': 'matrix(1 0 0 -1 0 850)'
39148 },
39149 'z-axis': {
39150 'width': 1000,
39151 'height': 1000,
39152 'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z',
39153 'transform': 'matrix(1 0 0 -1 0 850)'
39154 },
39155 '3d_rotate': {
39156 'width': 1000,
39157 'height': 1000,
39158 'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z',
39159 'transform': 'matrix(1 0 0 -1 0 850)'
39160 },
39161 'camera': {
39162 'width': 1000,
39163 'height': 1000,
39164 'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z',
39165 'transform': 'matrix(1 0 0 -1 0 850)'
39166 },
39167 'movie': {
39168 'width': 1000,
39169 'height': 1000,
39170 'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z',
39171 'transform': 'matrix(1 0 0 -1 0 850)'
39172 },
39173 'question': {
39174 'width': 857.1,
39175 'height': 1000,
39176 'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z',
39177 'transform': 'matrix(1 0 0 -1 0 850)'
39178 },
39179 'disk': {
39180 'width': 857.1,
39181 'height': 1000,
39182 'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z',
39183 'transform': 'matrix(1 0 0 -1 0 850)'
39184 },
39185 'drawopenpath': {
39186 'width': 70,
39187 'height': 70,
39188 'path': 'M33.21,85.65a7.31,7.31,0,0,1-2.59-.48c-8.16-3.11-9.27-19.8-9.88-41.3-.1-3.58-.19-6.68-.35-9-.15-2.1-.67-3.48-1.43-3.79-2.13-.88-7.91,2.32-12,5.86L3,32.38c1.87-1.64,11.55-9.66,18.27-6.9,2.13.87,4.75,3.14,5.17,9,.17,2.43.26,5.59.36,9.25a224.17,224.17,0,0,0,1.5,23.4c1.54,10.76,4,12.22,4.48,12.4.84.32,2.79-.46,5.76-3.59L43,80.07C41.53,81.57,37.68,85.64,33.21,85.65ZM74.81,69a11.34,11.34,0,0,0,6.09-6.72L87.26,44.5,74.72,32,56.9,38.35c-2.37.86-5.57,3.42-6.61,6L38.65,72.14l8.42,8.43ZM55,46.27a7.91,7.91,0,0,1,3.64-3.17l14.8-5.3,8,8L76.11,60.6l-.06.19a6.37,6.37,0,0,1-3,3.43L48.25,74.59,44.62,71Zm16.57,7.82A6.9,6.9,0,1,0,64.64,61,6.91,6.91,0,0,0,71.54,54.09Zm-4.05,0a2.85,2.85,0,1,1-2.85-2.85A2.86,2.86,0,0,1,67.49,54.09Zm-4.13,5.22L60.5,56.45,44.26,72.7l2.86,2.86ZM97.83,35.67,84.14,22l-8.57,8.57L89.26,44.24Zm-13.69-8,8,8-2.85,2.85-8-8Z',
39189 'transform': 'matrix(1 0 0 1 -15 -15)'
39190 },
39191 'drawclosedpath': {
39192 'width': 90,
39193 'height': 90,
39194 'path': 'M88.41,21.12a26.56,26.56,0,0,0-36.18,0l-2.07,2-2.07-2a26.57,26.57,0,0,0-36.18,0,23.74,23.74,0,0,0,0,34.8L48,90.12a3.22,3.22,0,0,0,4.42,0l36-34.21a23.73,23.73,0,0,0,0-34.79ZM84,51.24,50.16,83.35,16.35,51.25a17.28,17.28,0,0,1,0-25.47,20,20,0,0,1,27.3,0l4.29,4.07a3.23,3.23,0,0,0,4.44,0l4.29-4.07a20,20,0,0,1,27.3,0,17.27,17.27,0,0,1,0,25.46ZM66.76,47.68h-33v6.91h33ZM53.35,35H46.44V68h6.91Z',
39195 'transform': 'matrix(1 0 0 1 -5 -5)'
39196 },
39197 'lasso': {
39198 'width': 1031,
39199 'height': 1000,
39200 'path': 'm1018 538c-36 207-290 336-568 286-277-48-473-256-436-463 10-57 36-108 76-151-13-66 11-137 68-183 34-28 75-41 114-42l-55-70 0 0c-2-1-3-2-4-3-10-14-8-34 5-45 14-11 34-8 45 4 1 1 2 3 2 5l0 0 113 140c16 11 31 24 45 40 4 3 6 7 8 11 48-3 100 0 151 9 278 48 473 255 436 462z m-624-379c-80 14-149 48-197 96 42 42 109 47 156 9 33-26 47-66 41-105z m-187-74c-19 16-33 37-39 60 50-32 109-55 174-68-42-25-95-24-135 8z m360 75c-34-7-69-9-102-8 8 62-16 128-68 170-73 59-175 54-244-5-9 20-16 40-20 61-28 159 121 317 333 354s407-60 434-217c28-159-121-318-333-355z',
39201 'transform': 'matrix(1 0 0 -1 0 850)'
39202 },
39203 'selectbox': {
39204 'width': 1000,
39205 'height': 1000,
39206 'path': 'm0 850l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-285l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z',
39207 'transform': 'matrix(1 0 0 -1 0 850)'
39208 },
39209 'drawline': {
39210 'width': 70,
39211 'height': 70,
39212 'path': 'M60.64,62.3a11.29,11.29,0,0,0,6.09-6.72l6.35-17.72L60.54,25.31l-17.82,6.4c-2.36.86-5.57,3.41-6.6,6L24.48,65.5l8.42,8.42ZM40.79,39.63a7.89,7.89,0,0,1,3.65-3.17l14.79-5.31,8,8L61.94,54l-.06.19a6.44,6.44,0,0,1-3,3.43L34.07,68l-3.62-3.63Zm16.57,7.81a6.9,6.9,0,1,0-6.89,6.9A6.9,6.9,0,0,0,57.36,47.44Zm-4,0a2.86,2.86,0,1,1-2.85-2.85A2.86,2.86,0,0,1,53.32,47.44Zm-4.13,5.22L46.33,49.8,30.08,66.05l2.86,2.86ZM83.65,29,70,15.34,61.4,23.9,75.09,37.59ZM70,21.06l8,8-2.84,2.85-8-8ZM87,80.49H10.67V87H87Z',
39213 'transform': 'matrix(1 0 0 1 -15 -15)'
39214 },
39215 'drawrect': {
39216 'width': 80,
39217 'height': 80,
39218 'path': 'M78,22V79H21V22H78m9-9H12V88H87V13ZM68,46.22H31V54H68ZM53,32H45.22V69H53Z',
39219 'transform': 'matrix(1 0 0 1 -10 -10)'
39220 },
39221 'drawcircle': {
39222 'width': 80,
39223 'height': 80,
39224 'path': 'M50,84.72C26.84,84.72,8,69.28,8,50.3S26.84,15.87,50,15.87,92,31.31,92,50.3,73.16,84.72,50,84.72Zm0-60.59c-18.6,0-33.74,11.74-33.74,26.17S31.4,76.46,50,76.46,83.74,64.72,83.74,50.3,68.6,24.13,50,24.13Zm17.15,22h-34v7.11h34Zm-13.8-13H46.24v34h7.11Z',
39225 'transform': 'matrix(1 0 0 1 -10 -10)'
39226 },
39227 'eraseshape': {
39228 'width': 80,
39229 'height': 80,
39230 'path': 'M82.77,78H31.85L6,49.57,31.85,21.14H82.77a8.72,8.72,0,0,1,8.65,8.77V69.24A8.72,8.72,0,0,1,82.77,78ZM35.46,69.84H82.77a.57.57,0,0,0,.49-.6V29.91a.57.57,0,0,0-.49-.61H35.46L17,49.57Zm32.68-34.7-24,24,5,5,24-24Zm-19,.53-5,5,24,24,5-5Z',
39231 'transform': 'matrix(1 0 0 1 -10 -10)'
39232 },
39233 'spikeline': {
39234 'width': 1000,
39235 'height': 1000,
39236 'path': 'M512 409c0-57-46-104-103-104-57 0-104 47-104 104 0 57 47 103 104 103 57 0 103-46 103-103z m-327-39l92 0 0 92-92 0z m-185 0l92 0 0 92-92 0z m370-186l92 0 0 93-92 0z m0-184l92 0 0 92-92 0z',
39237 'transform': 'matrix(1.5 0 0 -1.5 0 850)'
39238 },
39239 'pencil': {
39240 'width': 1792,
39241 'height': 1792,
39242 'path': 'M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z',
39243 'transform': 'matrix(1 0 0 1 0 1)'
39244 },
39245 'newplotlylogo': {
39246 'name': 'newplotlylogo',
39247 'svg': '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 132 132\'><defs><style>.cls-1 {fill: #3f4f75;} .cls-2 {fill: #80cfbe;} .cls-3 {fill: #fff;}</style></defs><title>plotly-logomark</title><g id=\'symbol\'><rect class=\'cls-1\' width=\'132\' height=\'132\' rx=\'6\' ry=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'54\' r=\'6\'/><circle class=\'cls-2\' cx=\'102\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'54\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'54\' r=\'6\'/><path class=\'cls-3\' d=\'M30,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,30,72Z\'/><path class=\'cls-3\' d=\'M78,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,78,72Z\'/><path class=\'cls-3\' d=\'M54,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,54,48Z\'/><path class=\'cls-3\' d=\'M102,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,102,48Z\'/></g></svg>'
39248 }
39249};
39250
39251},{}],160:[function(_dereq_,module,exports){
39252/**
39253* Copyright 2012-2020, Plotly, Inc.
39254* All rights reserved.
39255*
39256* This source code is licensed under the MIT license found in the
39257* LICENSE file in the root directory of this source tree.
39258*/
39259
39260
39261'use strict';
39262
39263
39264/**
39265 * Determine the position anchor property of x/y xanchor/yanchor components.
39266 *
39267 * - values < 1/3 align the low side at that fraction,
39268 * - values [1/3, 2/3] align the center at that fraction,
39269 * - values > 2/3 align the right at that fraction.
39270 */
39271
39272
39273exports.isLeftAnchor = function isLeftAnchor(opts) {
39274 return (
39275 opts.xanchor === 'left' ||
39276 (opts.xanchor === 'auto' && opts.x <= 1 / 3)
39277 );
39278};
39279
39280exports.isCenterAnchor = function isCenterAnchor(opts) {
39281 return (
39282 opts.xanchor === 'center' ||
39283 (opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3)
39284 );
39285};
39286
39287exports.isRightAnchor = function isRightAnchor(opts) {
39288 return (
39289 opts.xanchor === 'right' ||
39290 (opts.xanchor === 'auto' && opts.x >= 2 / 3)
39291 );
39292};
39293
39294exports.isTopAnchor = function isTopAnchor(opts) {
39295 return (
39296 opts.yanchor === 'top' ||
39297 (opts.yanchor === 'auto' && opts.y >= 2 / 3)
39298 );
39299};
39300
39301exports.isMiddleAnchor = function isMiddleAnchor(opts) {
39302 return (
39303 opts.yanchor === 'middle' ||
39304 (opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3)
39305 );
39306};
39307
39308exports.isBottomAnchor = function isBottomAnchor(opts) {
39309 return (
39310 opts.yanchor === 'bottom' ||
39311 (opts.yanchor === 'auto' && opts.y <= 1 / 3)
39312 );
39313};
39314
39315},{}],161:[function(_dereq_,module,exports){
39316/**
39317* Copyright 2012-2020, Plotly, Inc.
39318* All rights reserved.
39319*
39320* This source code is licensed under the MIT license found in the
39321* LICENSE file in the root directory of this source tree.
39322*/
39323
39324'use strict';
39325
39326var modModule = _dereq_('./mod');
39327var mod = modModule.mod;
39328var modHalf = modModule.modHalf;
39329
39330var PI = Math.PI;
39331var twoPI = 2 * PI;
39332
39333function deg2rad(deg) { return deg / 180 * PI; }
39334
39335function rad2deg(rad) { return rad / PI * 180; }
39336
39337/**
39338 * is sector a full circle?
39339 * ... this comes up a lot in SVG path-drawing routines
39340 *
39341 * N.B. we consider all sectors that span more that 2pi 'full' circles
39342 *
39343 * @param {2-item array} aBnds : angular bounds in *radians*
39344 * @return {boolean}
39345 */
39346function isFullCircle(aBnds) {
39347 return Math.abs(aBnds[1] - aBnds[0]) > twoPI - 1e-14;
39348}
39349
39350/**
39351 * angular delta between angle 'a' and 'b'
39352 * solution taken from: https://stackoverflow.com/a/2007279
39353 *
39354 * @param {number} a : first angle in *radians*
39355 * @param {number} b : second angle in *radians*
39356 * @return {number} angular delta in *radians*
39357 */
39358function angleDelta(a, b) {
39359 return modHalf(b - a, twoPI);
39360}
39361
39362/**
39363 * angular distance between angle 'a' and 'b'
39364 *
39365 * @param {number} a : first angle in *radians*
39366 * @param {number} b : second angle in *radians*
39367 * @return {number} angular distance in *radians*
39368 */
39369function angleDist(a, b) {
39370 return Math.abs(angleDelta(a, b));
39371}
39372
39373/**
39374 * is angle inside sector?
39375 *
39376 * @param {number} a : angle to test in *radians*
39377 * @param {2-item array} aBnds : sector's angular bounds in *radians*
39378 * @param {boolean}
39379 */
39380function isAngleInsideSector(a, aBnds) {
39381 if(isFullCircle(aBnds)) return true;
39382
39383 var s0, s1;
39384
39385 if(aBnds[0] < aBnds[1]) {
39386 s0 = aBnds[0];
39387 s1 = aBnds[1];
39388 } else {
39389 s0 = aBnds[1];
39390 s1 = aBnds[0];
39391 }
39392
39393 s0 = mod(s0, twoPI);
39394 s1 = mod(s1, twoPI);
39395 if(s0 > s1) s1 += twoPI;
39396
39397 var a0 = mod(a, twoPI);
39398 var a1 = a0 + twoPI;
39399
39400 return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1);
39401}
39402
39403/**
39404 * is pt (r,a) inside sector?
39405 *
39406 * @param {number} r : pt's radial coordinate
39407 * @param {number} a : pt's angular coordinate in *radians*
39408 * @param {2-item array} rBnds : sector's radial bounds
39409 * @param {2-item array} aBnds : sector's angular bounds in *radians*
39410 * @return {boolean}
39411 */
39412function isPtInsideSector(r, a, rBnds, aBnds) {
39413 if(!isAngleInsideSector(a, aBnds)) return false;
39414
39415 var r0, r1;
39416
39417 if(rBnds[0] < rBnds[1]) {
39418 r0 = rBnds[0];
39419 r1 = rBnds[1];
39420 } else {
39421 r0 = rBnds[1];
39422 r1 = rBnds[0];
39423 }
39424
39425 return r >= r0 && r <= r1;
39426}
39427
39428// common to pathArc, pathSector and pathAnnulus
39429function _path(r0, r1, a0, a1, cx, cy, isClosed) {
39430 cx = cx || 0;
39431 cy = cy || 0;
39432
39433 var isCircle = isFullCircle([a0, a1]);
39434 var aStart, aMid, aEnd;
39435 var rStart, rEnd;
39436
39437 if(isCircle) {
39438 aStart = 0;
39439 aMid = PI;
39440 aEnd = twoPI;
39441 } else {
39442 if(a0 < a1) {
39443 aStart = a0;
39444 aEnd = a1;
39445 } else {
39446 aStart = a1;
39447 aEnd = a0;
39448 }
39449 }
39450
39451 if(r0 < r1) {
39452 rStart = r0;
39453 rEnd = r1;
39454 } else {
39455 rStart = r1;
39456 rEnd = r0;
39457 }
39458
39459 // N.B. svg coordinates here, where y increases downward
39460 function pt(r, a) {
39461 return [r * Math.cos(a) + cx, cy - r * Math.sin(a)];
39462 }
39463
39464 var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1;
39465 function arc(r, a, cw) {
39466 return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a);
39467 }
39468
39469 var p;
39470
39471 if(isCircle) {
39472 if(rStart === null) {
39473 p = 'M' + pt(rEnd, aStart) +
39474 arc(rEnd, aMid, 0) +
39475 arc(rEnd, aEnd, 0) + 'Z';
39476 } else {
39477 p = 'M' + pt(rStart, aStart) +
39478 arc(rStart, aMid, 0) +
39479 arc(rStart, aEnd, 0) + 'Z' +
39480 'M' + pt(rEnd, aStart) +
39481 arc(rEnd, aMid, 1) +
39482 arc(rEnd, aEnd, 1) + 'Z';
39483 }
39484 } else {
39485 if(rStart === null) {
39486 p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0);
39487 if(isClosed) p += 'L0,0Z';
39488 } else {
39489 p = 'M' + pt(rStart, aStart) +
39490 'L' + pt(rEnd, aStart) +
39491 arc(rEnd, aEnd, 0) +
39492 'L' + pt(rStart, aEnd) +
39493 arc(rStart, aStart, 1) + 'Z';
39494 }
39495 }
39496
39497 return p;
39498}
39499
39500/**
39501 * path an arc
39502 *
39503 * @param {number} r : radius
39504 * @param {number} a0 : first angular coordinate in *radians*
39505 * @param {number} a1 : second angular coordinate in *radians*
39506 * @param {number (optional)} cx : x coordinate of center
39507 * @param {number (optional)} cy : y coordinate of center
39508 * @return {string} svg path
39509 */
39510function pathArc(r, a0, a1, cx, cy) {
39511 return _path(null, r, a0, a1, cx, cy, 0);
39512}
39513
39514/**
39515 * path a sector
39516 *
39517 * @param {number} r : radius
39518 * @param {number} a0 : first angular coordinate in *radians*
39519 * @param {number} a1 : second angular coordinate in *radians*
39520 * @param {number (optional)} cx : x coordinate of center
39521 * @param {number (optional)} cy : y coordinate of center
39522 * @return {string} svg path
39523 */
39524function pathSector(r, a0, a1, cx, cy) {
39525 return _path(null, r, a0, a1, cx, cy, 1);
39526}
39527
39528/**
39529 * path an annulus
39530 *
39531 * @param {number} r0 : first radial coordinate
39532 * @param {number} r1 : second radial coordinate
39533 * @param {number} a0 : first angular coordinate in *radians*
39534 * @param {number} a1 : second angular coordinate in *radians*
39535 * @param {number (optional)} cx : x coordinate of center
39536 * @param {number (optional)} cy : y coordinate of center
39537 * @return {string} svg path
39538 */
39539function pathAnnulus(r0, r1, a0, a1, cx, cy) {
39540 return _path(r0, r1, a0, a1, cx, cy, 1);
39541}
39542
39543module.exports = {
39544 deg2rad: deg2rad,
39545 rad2deg: rad2deg,
39546 angleDelta: angleDelta,
39547 angleDist: angleDist,
39548 isFullCircle: isFullCircle,
39549 isAngleInsideSector: isAngleInsideSector,
39550 isPtInsideSector: isPtInsideSector,
39551 pathArc: pathArc,
39552 pathSector: pathSector,
39553 pathAnnulus: pathAnnulus
39554};
39555
39556},{"./mod":184}],162:[function(_dereq_,module,exports){
39557/**
39558* Copyright 2012-2020, Plotly, Inc.
39559* All rights reserved.
39560*
39561* This source code is licensed under the MIT license found in the
39562* LICENSE file in the root directory of this source tree.
39563*/
39564
39565'use strict';
39566
39567var isArray = Array.isArray;
39568
39569// IE9 fallbacks
39570
39571var ab = (typeof ArrayBuffer === 'undefined' || !ArrayBuffer.isView) ?
39572 {isView: function() { return false; }} :
39573 ArrayBuffer;
39574
39575var dv = (typeof DataView === 'undefined') ?
39576 function() {} :
39577 DataView;
39578
39579function isTypedArray(a) {
39580 return ab.isView(a) && !(a instanceof dv);
39581}
39582exports.isTypedArray = isTypedArray;
39583
39584function isArrayOrTypedArray(a) {
39585 return isArray(a) || isTypedArray(a);
39586}
39587exports.isArrayOrTypedArray = isArrayOrTypedArray;
39588
39589/*
39590 * Test whether an input object is 1D.
39591 *
39592 * Assumes we already know the object is an array.
39593 *
39594 * Looks only at the first element, if the dimensionality is
39595 * not consistent we won't figure that out here.
39596 */
39597function isArray1D(a) {
39598 return !isArrayOrTypedArray(a[0]);
39599}
39600exports.isArray1D = isArray1D;
39601
39602/*
39603 * Ensures an array has the right amount of storage space. If it doesn't
39604 * exist, it creates an array. If it does exist, it returns it if too
39605 * short or truncates it in-place.
39606 *
39607 * The goal is to just reuse memory to avoid a bit of excessive garbage
39608 * collection.
39609 */
39610exports.ensureArray = function(out, n) {
39611 // TODO: typed array support here? This is only used in
39612 // traces/carpet/compute_control_points
39613 if(!isArray(out)) out = [];
39614
39615 // If too long, truncate. (If too short, it will grow
39616 // automatically so we don't care about that case)
39617 out.length = n;
39618
39619 return out;
39620};
39621
39622/*
39623 * TypedArray-compatible concatenation of n arrays
39624 * if all arrays are the same type it will preserve that type,
39625 * otherwise it falls back on Array.
39626 * Also tries to avoid copying, in case one array has zero length
39627 * But never mutates an existing array
39628 */
39629exports.concat = function() {
39630 var args = [];
39631 var allArray = true;
39632 var totalLen = 0;
39633
39634 var _constructor, arg0, i, argi, posi, leni, out, j;
39635
39636 for(i = 0; i < arguments.length; i++) {
39637 argi = arguments[i];
39638 leni = argi.length;
39639 if(leni) {
39640 if(arg0) args.push(argi);
39641 else {
39642 arg0 = argi;
39643 posi = leni;
39644 }
39645
39646 if(isArray(argi)) {
39647 _constructor = false;
39648 } else {
39649 allArray = false;
39650 if(!totalLen) {
39651 _constructor = argi.constructor;
39652 } else if(_constructor !== argi.constructor) {
39653 // TODO: in principle we could upgrade here,
39654 // ie keep typed array but convert all to Float64Array?
39655 _constructor = false;
39656 }
39657 }
39658
39659 totalLen += leni;
39660 }
39661 }
39662
39663 if(!totalLen) return [];
39664 if(!args.length) return arg0;
39665
39666 if(allArray) return arg0.concat.apply(arg0, args);
39667 if(_constructor) {
39668 // matching typed arrays
39669 out = new _constructor(totalLen);
39670 out.set(arg0);
39671 for(i = 0; i < args.length; i++) {
39672 argi = args[i];
39673 out.set(argi, posi);
39674 posi += argi.length;
39675 }
39676 return out;
39677 }
39678
39679 // mismatched types or Array + typed
39680 out = new Array(totalLen);
39681 for(j = 0; j < arg0.length; j++) out[j] = arg0[j];
39682 for(i = 0; i < args.length; i++) {
39683 argi = args[i];
39684 for(j = 0; j < argi.length; j++) out[posi + j] = argi[j];
39685 posi += j;
39686 }
39687 return out;
39688};
39689
39690exports.maxRowLength = function(z) {
39691 return _rowLength(z, Math.max, 0);
39692};
39693
39694exports.minRowLength = function(z) {
39695 return _rowLength(z, Math.min, Infinity);
39696};
39697
39698function _rowLength(z, fn, len0) {
39699 if(isArrayOrTypedArray(z)) {
39700 if(isArrayOrTypedArray(z[0])) {
39701 var len = len0;
39702 for(var i = 0; i < z.length; i++) {
39703 len = fn(len, z[i].length);
39704 }
39705 return len;
39706 } else {
39707 return z.length;
39708 }
39709 }
39710 return 0;
39711}
39712
39713},{}],163:[function(_dereq_,module,exports){
39714/**
39715* Copyright 2012-2020, Plotly, Inc.
39716* All rights reserved.
39717*
39718* This source code is licensed under the MIT license found in the
39719* LICENSE file in the root directory of this source tree.
39720*/
39721
39722
39723'use strict';
39724
39725var isNumeric = _dereq_('fast-isnumeric');
39726
39727var BADNUM = _dereq_('../constants/numerical').BADNUM;
39728
39729// precompile for speed
39730var JUNK = /^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g;
39731
39732/**
39733 * cleanNumber: remove common leading and trailing cruft
39734 * Always returns either a number or BADNUM.
39735 */
39736module.exports = function cleanNumber(v) {
39737 if(typeof v === 'string') {
39738 v = v.replace(JUNK, '');
39739 }
39740
39741 if(isNumeric(v)) return Number(v);
39742
39743 return BADNUM;
39744};
39745
39746},{"../constants/numerical":155,"fast-isnumeric":15}],164:[function(_dereq_,module,exports){
39747/**
39748* Copyright 2012-2020, Plotly, Inc.
39749* All rights reserved.
39750*
39751* This source code is licensed under the MIT license found in the
39752* LICENSE file in the root directory of this source tree.
39753*/
39754
39755'use strict';
39756
39757/**
39758 * Clear gl frame (if any). This is a common pattern as
39759 * we usually set `preserveDrawingBuffer: true` during
39760 * gl context creation (e.g. via `reglUtils.prepare`).
39761 *
39762 * @param {DOM node or object} gd : graph div object
39763 */
39764module.exports = function clearGlCanvases(gd) {
39765 var fullLayout = gd._fullLayout;
39766
39767 if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
39768 fullLayout._glcanvas.each(function(d) {
39769 if(d.regl) d.regl.clear({color: true, depth: true});
39770 });
39771 }
39772};
39773
39774},{}],165:[function(_dereq_,module,exports){
39775/**
39776* Copyright 2012-2020, Plotly, Inc.
39777* All rights reserved.
39778*
39779* This source code is licensed under the MIT license found in the
39780* LICENSE file in the root directory of this source tree.
39781*/
39782
39783'use strict';
39784
39785/**
39786 * Clear responsive handlers (if any).
39787 *
39788 * @param {DOM node or object} gd : graph div object
39789 */
39790module.exports = function clearResponsive(gd) {
39791 if(gd._responsiveChartHandler) {
39792 window.removeEventListener('resize', gd._responsiveChartHandler);
39793 delete gd._responsiveChartHandler;
39794 }
39795};
39796
39797},{}],166:[function(_dereq_,module,exports){
39798/**
39799* Copyright 2012-2020, Plotly, Inc.
39800* All rights reserved.
39801*
39802* This source code is licensed under the MIT license found in the
39803* LICENSE file in the root directory of this source tree.
39804*/
39805
39806'use strict';
39807
39808var isNumeric = _dereq_('fast-isnumeric');
39809var tinycolor = _dereq_('tinycolor2');
39810
39811var baseTraceAttrs = _dereq_('../plots/attributes');
39812var colorscales = _dereq_('../components/colorscale/scales');
39813var DESELECTDIM = _dereq_('../constants/interactions').DESELECTDIM;
39814
39815var nestedProperty = _dereq_('./nested_property');
39816var counterRegex = _dereq_('./regex').counter;
39817var modHalf = _dereq_('./mod').modHalf;
39818var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
39819
39820exports.valObjectMeta = {
39821 data_array: {
39822 // You can use *dflt=[] to force said array to exist though.
39823
39824
39825
39826 coerceFunction: function(v, propOut, dflt) {
39827 // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also
39828 if(isArrayOrTypedArray(v)) propOut.set(v);
39829 else if(dflt !== undefined) propOut.set(dflt);
39830 }
39831 },
39832 enumerated: {
39833
39834
39835
39836 coerceFunction: function(v, propOut, dflt, opts) {
39837 if(opts.coerceNumber) v = +v;
39838 if(opts.values.indexOf(v) === -1) propOut.set(dflt);
39839 else propOut.set(v);
39840 },
39841 validateFunction: function(v, opts) {
39842 if(opts.coerceNumber) v = +v;
39843
39844 var values = opts.values;
39845 for(var i = 0; i < values.length; i++) {
39846 var k = String(values[i]);
39847
39848 if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) {
39849 var regex = new RegExp(k.substr(1, k.length - 2));
39850 if(regex.test(v)) return true;
39851 } else if(v === values[i]) return true;
39852 }
39853 return false;
39854 }
39855 },
39856 'boolean': {
39857
39858
39859
39860 coerceFunction: function(v, propOut, dflt) {
39861 if(v === true || v === false) propOut.set(v);
39862 else propOut.set(dflt);
39863 }
39864 },
39865 number: {
39866
39867
39868
39869 coerceFunction: function(v, propOut, dflt, opts) {
39870 if(!isNumeric(v) ||
39871 (opts.min !== undefined && v < opts.min) ||
39872 (opts.max !== undefined && v > opts.max)) {
39873 propOut.set(dflt);
39874 } else propOut.set(+v);
39875 }
39876 },
39877 integer: {
39878
39879
39880
39881 coerceFunction: function(v, propOut, dflt, opts) {
39882 if(v % 1 || !isNumeric(v) ||
39883 (opts.min !== undefined && v < opts.min) ||
39884 (opts.max !== undefined && v > opts.max)) {
39885 propOut.set(dflt);
39886 } else propOut.set(+v);
39887 }
39888 },
39889 string: {
39890
39891
39892 // TODO 'values shouldn't be in there (edge case: 'dash' in Scatter)
39893
39894 coerceFunction: function(v, propOut, dflt, opts) {
39895 if(typeof v !== 'string') {
39896 var okToCoerce = (typeof v === 'number');
39897
39898 if(opts.strict === true || !okToCoerce) propOut.set(dflt);
39899 else propOut.set(String(v));
39900 } else if(opts.noBlank && !v) propOut.set(dflt);
39901 else propOut.set(v);
39902 }
39903 },
39904 color: {
39905
39906
39907
39908 coerceFunction: function(v, propOut, dflt) {
39909 if(tinycolor(v).isValid()) propOut.set(v);
39910 else propOut.set(dflt);
39911 }
39912 },
39913 colorlist: {
39914
39915
39916
39917 coerceFunction: function(v, propOut, dflt) {
39918 function isColor(color) {
39919 return tinycolor(color).isValid();
39920 }
39921 if(!Array.isArray(v) || !v.length) propOut.set(dflt);
39922 else if(v.every(isColor)) propOut.set(v);
39923 else propOut.set(dflt);
39924 }
39925 },
39926 colorscale: {
39927
39928
39929
39930 coerceFunction: function(v, propOut, dflt) {
39931 propOut.set(colorscales.get(v, dflt));
39932 }
39933 },
39934 angle: {
39935
39936
39937
39938 coerceFunction: function(v, propOut, dflt) {
39939 if(v === 'auto') propOut.set('auto');
39940 else if(!isNumeric(v)) propOut.set(dflt);
39941 else propOut.set(modHalf(+v, 360));
39942 }
39943 },
39944 subplotid: {
39945
39946
39947
39948 coerceFunction: function(v, propOut, dflt, opts) {
39949 var regex = opts.regex || counterRegex(dflt);
39950 if(typeof v === 'string' && regex.test(v)) {
39951 propOut.set(v);
39952 return;
39953 }
39954 propOut.set(dflt);
39955 },
39956 validateFunction: function(v, opts) {
39957 var dflt = opts.dflt;
39958
39959 if(v === dflt) return true;
39960 if(typeof v !== 'string') return false;
39961 if(counterRegex(dflt).test(v)) return true;
39962
39963 return false;
39964 }
39965 },
39966 flaglist: {
39967
39968
39969
39970 coerceFunction: function(v, propOut, dflt, opts) {
39971 if(typeof v !== 'string') {
39972 propOut.set(dflt);
39973 return;
39974 }
39975 if((opts.extras || []).indexOf(v) !== -1) {
39976 propOut.set(v);
39977 return;
39978 }
39979 var vParts = v.split('+');
39980 var i = 0;
39981 while(i < vParts.length) {
39982 var vi = vParts[i];
39983 if(opts.flags.indexOf(vi) === -1 || vParts.indexOf(vi) < i) {
39984 vParts.splice(i, 1);
39985 } else i++;
39986 }
39987 if(!vParts.length) propOut.set(dflt);
39988 else propOut.set(vParts.join('+'));
39989 }
39990 },
39991 any: {
39992
39993
39994
39995 coerceFunction: function(v, propOut, dflt) {
39996 if(v === undefined) propOut.set(dflt);
39997 else propOut.set(v);
39998 }
39999 },
40000 info_array: {
40001
40002
40003 // set `dimensions=2` for a 2D array or '1-2' for either
40004 // `items` may be a single object instead of an array, in which case
40005 // `freeLength` must be true.
40006 // if `dimensions='1-2'` and items is a 1D array, then the value can
40007 // either be a matching 1D array or an array of such matching 1D arrays
40008
40009 coerceFunction: function(v, propOut, dflt, opts) {
40010 // simplified coerce function just for array items
40011 function coercePart(v, opts, dflt) {
40012 var out;
40013 var propPart = {set: function(v) { out = v; }};
40014
40015 if(dflt === undefined) dflt = opts.dflt;
40016
40017 exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
40018
40019 return out;
40020 }
40021
40022 var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0]));
40023
40024 if(!Array.isArray(v)) {
40025 propOut.set(dflt);
40026 return;
40027 }
40028
40029 var items = opts.items;
40030 var vOut = [];
40031 var arrayItems = Array.isArray(items);
40032 var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]);
40033 var innerItemsOnly = twoD && arrayItems && !arrayItems2D;
40034 var len = (arrayItems && !innerItemsOnly) ? items.length : v.length;
40035
40036 var i, j, row, item, len2, vNew;
40037
40038 dflt = Array.isArray(dflt) ? dflt : [];
40039
40040 if(twoD) {
40041 for(i = 0; i < len; i++) {
40042 vOut[i] = [];
40043 row = Array.isArray(v[i]) ? v[i] : [];
40044 if(innerItemsOnly) len2 = items.length;
40045 else if(arrayItems) len2 = items[i].length;
40046 else len2 = row.length;
40047
40048 for(j = 0; j < len2; j++) {
40049 if(innerItemsOnly) item = items[j];
40050 else if(arrayItems) item = items[i][j];
40051 else item = items;
40052
40053 vNew = coercePart(row[j], item, (dflt[i] || [])[j]);
40054 if(vNew !== undefined) vOut[i][j] = vNew;
40055 }
40056 }
40057 } else {
40058 for(i = 0; i < len; i++) {
40059 vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
40060 if(vNew !== undefined) vOut[i] = vNew;
40061 }
40062 }
40063
40064 propOut.set(vOut);
40065 },
40066 validateFunction: function(v, opts) {
40067 if(!Array.isArray(v)) return false;
40068
40069 var items = opts.items;
40070 var arrayItems = Array.isArray(items);
40071 var twoD = opts.dimensions === 2;
40072
40073 // when free length is off, input and declared lengths must match
40074 if(!opts.freeLength && v.length !== items.length) return false;
40075
40076 // valid when all input items are valid
40077 for(var i = 0; i < v.length; i++) {
40078 if(twoD) {
40079 if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
40080 return false;
40081 }
40082 for(var j = 0; j < v[i].length; j++) {
40083 if(!validate(v[i][j], arrayItems ? items[i][j] : items)) {
40084 return false;
40085 }
40086 }
40087 } else if(!validate(v[i], arrayItems ? items[i] : items)) return false;
40088 }
40089
40090 return true;
40091 }
40092 }
40093};
40094
40095/**
40096 * Ensures that container[attribute] has a valid value.
40097 *
40098 * attributes[attribute] is an object with possible keys:
40099 * - valType: data_array, enumerated, boolean, ... as in valObjectMeta
40100 * - values: (enumerated only) array of allowed vals
40101 * - min, max: (number, integer only) inclusive bounds on allowed vals
40102 * either or both may be omitted
40103 * - dflt: if attribute is invalid or missing, use this default
40104 * if dflt is provided as an argument to lib.coerce it takes precedence
40105 * as a convenience, returns the value it finally set
40106 */
40107exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
40108 var opts = nestedProperty(attributes, attribute).get();
40109 var propIn = nestedProperty(containerIn, attribute);
40110 var propOut = nestedProperty(containerOut, attribute);
40111 var v = propIn.get();
40112
40113 var template = containerOut._template;
40114 if(v === undefined && template) {
40115 v = nestedProperty(template, attribute).get();
40116 // already used the template value, so short-circuit the second check
40117 template = 0;
40118 }
40119
40120 if(dflt === undefined) dflt = opts.dflt;
40121
40122 /**
40123 * arrayOk: value MAY be an array, then we do no value checking
40124 * at this point, because it can be more complicated than the
40125 * individual form (eg. some array vals can be numbers, even if the
40126 * single values must be color strings)
40127 */
40128 if(opts.arrayOk && isArrayOrTypedArray(v)) {
40129 propOut.set(v);
40130 return v;
40131 }
40132
40133 var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction;
40134 coerceFunction(v, propOut, dflt, opts);
40135
40136 var out = propOut.get();
40137 // in case v was provided but invalid, try the template again so it still
40138 // overrides the regular default
40139 if(template && out === dflt && !validate(v, opts)) {
40140 v = nestedProperty(template, attribute).get();
40141 coerceFunction(v, propOut, dflt, opts);
40142 out = propOut.get();
40143 }
40144 return out;
40145};
40146
40147/**
40148 * Variation on coerce
40149 *
40150 * Uses coerce to get attribute value if user input is valid,
40151 * returns attribute default if user input it not valid or
40152 * returns false if there is no user input.
40153 */
40154exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) {
40155 var propIn = nestedProperty(containerIn, attribute);
40156 var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt);
40157 var valIn = propIn.get();
40158
40159 return (valIn !== undefined && valIn !== null) ? propOut : false;
40160};
40161
40162/*
40163 * Shortcut to coerce the three font attributes
40164 *
40165 * 'coerce' is a lib.coerce wrapper with implied first three arguments
40166 */
40167exports.coerceFont = function(coerce, attr, dfltObj) {
40168 var out = {};
40169
40170 dfltObj = dfltObj || {};
40171
40172 out.family = coerce(attr + '.family', dfltObj.family);
40173 out.size = coerce(attr + '.size', dfltObj.size);
40174 out.color = coerce(attr + '.color', dfltObj.color);
40175
40176 return out;
40177};
40178
40179/** Coerce shortcut for 'hoverinfo'
40180 * handling 1-vs-multi-trace dflt logic
40181 *
40182 * @param {object} traceIn : user trace object
40183 * @param {object} traceOut : full trace object (requires _module ref)
40184 * @param {object} layoutOut : full layout object (require _dataLength ref)
40185 * @return {any} : the coerced value
40186 */
40187exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) {
40188 var moduleAttrs = traceOut._module.attributes;
40189 var attrs = moduleAttrs.hoverinfo ? moduleAttrs : baseTraceAttrs;
40190
40191 var valObj = attrs.hoverinfo;
40192 var dflt;
40193
40194 if(layoutOut._dataLength === 1) {
40195 var flags = valObj.dflt === 'all' ?
40196 valObj.flags.slice() :
40197 valObj.dflt.split('+');
40198
40199 flags.splice(flags.indexOf('name'), 1);
40200 dflt = flags.join('+');
40201 }
40202
40203 return exports.coerce(traceIn, traceOut, attrs, 'hoverinfo', dflt);
40204};
40205
40206/** Coerce shortcut for [un]selected.marker.opacity,
40207 * which has special default logic, to ensure that it corresponds to the
40208 * default selection behavior while allowing to be overtaken by any other
40209 * [un]selected attribute.
40210 *
40211 * N.B. This must be called *after* coercing all the other [un]selected attrs,
40212 * to give the intended result.
40213 *
40214 * @param {object} traceOut : fullData item
40215 * @param {function} coerce : lib.coerce wrapper with implied first three arguments
40216 */
40217exports.coerceSelectionMarkerOpacity = function(traceOut, coerce) {
40218 if(!traceOut.marker) return;
40219
40220 var mo = traceOut.marker.opacity;
40221 // you can still have a `marker` container with no markers if there's text
40222 if(mo === undefined) return;
40223
40224 var smoDflt;
40225 var usmoDflt;
40226
40227 // Don't give [un]selected.marker.opacity a default value if
40228 // marker.opacity is an array: handle this during style step.
40229 //
40230 // Only give [un]selected.marker.opacity a default value if you don't
40231 // set any other [un]selected attributes.
40232 if(!isArrayOrTypedArray(mo) && !traceOut.selected && !traceOut.unselected) {
40233 smoDflt = mo;
40234 usmoDflt = DESELECTDIM * mo;
40235 }
40236
40237 coerce('selected.marker.opacity', smoDflt);
40238 coerce('unselected.marker.opacity', usmoDflt);
40239};
40240
40241function validate(value, opts) {
40242 var valObjectDef = exports.valObjectMeta[opts.valType];
40243
40244 if(opts.arrayOk && isArrayOrTypedArray(value)) return true;
40245
40246 if(valObjectDef.validateFunction) {
40247 return valObjectDef.validateFunction(value, opts);
40248 }
40249
40250 var failed = {};
40251 var out = failed;
40252 var propMock = { set: function(v) { out = v; } };
40253
40254 // 'failed' just something mutable that won't be === anything else
40255
40256 valObjectDef.coerceFunction(value, propMock, failed, opts);
40257 return out !== failed;
40258}
40259exports.validate = validate;
40260
40261},{"../components/colorscale/scales":65,"../constants/interactions":154,"../plots/attributes":219,"./array":162,"./mod":184,"./nested_property":185,"./regex":192,"fast-isnumeric":15,"tinycolor2":32}],167:[function(_dereq_,module,exports){
40262/**
40263* Copyright 2012-2020, Plotly, Inc.
40264* All rights reserved.
40265*
40266* This source code is licensed under the MIT license found in the
40267* LICENSE file in the root directory of this source tree.
40268*/
40269
40270
40271'use strict';
40272
40273var d3 = _dereq_('d3');
40274var isNumeric = _dereq_('fast-isnumeric');
40275
40276var Loggers = _dereq_('./loggers');
40277var mod = _dereq_('./mod').mod;
40278
40279var constants = _dereq_('../constants/numerical');
40280var BADNUM = constants.BADNUM;
40281var ONEDAY = constants.ONEDAY;
40282var ONEHOUR = constants.ONEHOUR;
40283var ONEMIN = constants.ONEMIN;
40284var ONESEC = constants.ONESEC;
40285var EPOCHJD = constants.EPOCHJD;
40286
40287var Registry = _dereq_('../registry');
40288
40289var utcFormat = d3.time.format.utc;
40290
40291var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
40292// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months
40293var DATETIME_REGEXP_CN = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\di?)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
40294
40295// for 2-digit years, the first year we map them onto
40296var YFIRST = new Date().getFullYear() - 70;
40297
40298function isWorldCalendar(calendar) {
40299 return (
40300 calendar &&
40301 Registry.componentsRegistry.calendars &&
40302 typeof calendar === 'string' && calendar !== 'gregorian'
40303 );
40304}
40305
40306/*
40307 * dateTick0: get the canonical tick for this calendar
40308 *
40309 * bool sunday is for week ticks, shift it to a Sunday.
40310 */
40311exports.dateTick0 = function(calendar, sunday) {
40312 if(isWorldCalendar(calendar)) {
40313 return sunday ?
40314 Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
40315 Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
40316 } else {
40317 return sunday ? '2000-01-02' : '2000-01-01';
40318 }
40319};
40320
40321/*
40322 * dfltRange: for each calendar, give a valid default range
40323 */
40324exports.dfltRange = function(calendar) {
40325 if(isWorldCalendar(calendar)) {
40326 return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
40327 } else {
40328 return ['2000-01-01', '2001-01-01'];
40329 }
40330};
40331
40332// is an object a javascript date?
40333exports.isJSDate = function(v) {
40334 return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
40335};
40336
40337// The absolute limits of our date-time system
40338// This is a little weird: we use MIN_MS and MAX_MS in dateTime2ms
40339// but we use dateTime2ms to calculate them (after defining it!)
40340var MIN_MS, MAX_MS;
40341
40342/**
40343 * dateTime2ms - turn a date object or string s into milliseconds
40344 * (relative to 1970-01-01, per javascript standard)
40345 * optional calendar (string) to use a non-gregorian calendar
40346 *
40347 * Returns BADNUM if it doesn't find a date
40348 *
40349 * strings should have the form:
40350 *
40351 * -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
40352 *
40353 * <sep>: space (our normal standard) or T or t (ISO-8601)
40354 * <tzInfo>: Z, z, or [+\-]HH:?MM and we THROW IT AWAY
40355 * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
40356 * but we allow it even with a space as the separator
40357 *
40358 * May truncate after any full field, and sss can be any length
40359 * even >3 digits, though javascript dates truncate to milliseconds,
40360 * we keep as much as javascript numeric precision can hold, but we only
40361 * report back up to 100 microsecond precision, because most dates support
40362 * this precision (close to 1970 support more, very far away support less)
40363 *
40364 * Expanded to support negative years to -9999 but you must always
40365 * give 4 digits, except for 2-digit positive years which we assume are
40366 * near the present time.
40367 * Note that we follow ISO 8601:2004: there *is* a year 0, which
40368 * is 1BC/BCE, and -1===2BC etc.
40369 *
40370 * World calendars: not all of these *have* agreed extensions to this full range,
40371 * if you have another calendar system but want a date range outside its validity,
40372 * you can use a gregorian date string prefixed with 'G' or 'g'.
40373 *
40374 * Where to cut off 2-digit years between 1900s and 2000s?
40375 * from http://support.microsoft.com/kb/244664:
40376 * 1930-2029 (the most retro of all...)
40377 * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
40378 * 1950-2049
40379 * by Java, from http://stackoverflow.com/questions/2024273/:
40380 * now-80 - now+19
40381 * or FileMaker Pro, from
40382 * http://www.filemaker.com/12help/html/add_view_data.4.21.html:
40383 * now-70 - now+29
40384 * but python strptime etc, via
40385 * http://docs.python.org/py3k/library/time.html:
40386 * 1969-2068 (super forward-looking, but static, not sliding!)
40387 *
40388 * lets go with now-70 to now+29, and if anyone runs into this problem
40389 * they can learn the hard way not to use 2-digit years, as no choice we
40390 * make now will cover all possibilities. mostly this will all be taken
40391 * care of in initial parsing, should only be an issue for hand-entered data
40392 * currently (2016) this range is:
40393 * 1946-2045
40394 */
40395exports.dateTime2ms = function(s, calendar) {
40396 // first check if s is a date object
40397 if(exports.isJSDate(s)) {
40398 // Convert to the UTC milliseconds that give the same
40399 // hours as this date has in the local timezone
40400 var tzOffset = s.getTimezoneOffset() * ONEMIN;
40401 var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN +
40402 (s.getUTCSeconds() - s.getSeconds()) * ONESEC +
40403 (s.getUTCMilliseconds() - s.getMilliseconds());
40404
40405 if(offsetTweak) {
40406 var comb = 3 * ONEMIN;
40407 tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb);
40408 }
40409 s = Number(s) - tzOffset;
40410 if(s >= MIN_MS && s <= MAX_MS) return s;
40411 return BADNUM;
40412 }
40413 // otherwise only accept strings and numbers
40414 if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
40415
40416 s = String(s);
40417
40418 var isWorld = isWorldCalendar(calendar);
40419
40420 // to handle out-of-range dates in international calendars, accept
40421 // 'G' as a prefix to force the built-in gregorian calendar.
40422 var s0 = s.charAt(0);
40423 if(isWorld && (s0 === 'G' || s0 === 'g')) {
40424 s = s.substr(1);
40425 calendar = '';
40426 }
40427
40428 var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
40429
40430 var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
40431 if(!match) return BADNUM;
40432 var y = match[1];
40433 var m = match[3] || '1';
40434 var d = Number(match[5] || 1);
40435 var H = Number(match[7] || 0);
40436 var M = Number(match[9] || 0);
40437 var S = Number(match[11] || 0);
40438
40439 if(isWorld) {
40440 // disallow 2-digit years for world calendars
40441 if(y.length === 2) return BADNUM;
40442 y = Number(y);
40443
40444 var cDate;
40445 try {
40446 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
40447 if(isChinese) {
40448 var isIntercalary = m.charAt(m.length - 1) === 'i';
40449 m = parseInt(m, 10);
40450 cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
40451 } else {
40452 cDate = calInstance.newDate(y, Number(m), d);
40453 }
40454 } catch(e) { return BADNUM; } // Invalid ... date
40455
40456 if(!cDate) return BADNUM;
40457
40458 return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
40459 (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
40460 }
40461
40462 if(y.length === 2) {
40463 y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
40464 } else y = Number(y);
40465
40466 // new Date uses months from 0; subtract 1 here just so we
40467 // don't have to do it again during the validity test below
40468 m -= 1;
40469
40470 // javascript takes new Date(0..99,m,d) to mean 1900-1999, so
40471 // to support years 0-99 we need to use setFullYear explicitly
40472 // Note that 2000 is a leap year.
40473 var date = new Date(Date.UTC(2000, m, d, H, M));
40474 date.setUTCFullYear(y);
40475
40476 if(date.getUTCMonth() !== m) return BADNUM;
40477 if(date.getUTCDate() !== d) return BADNUM;
40478
40479 return date.getTime() + S * ONESEC;
40480};
40481
40482MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
40483MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
40484
40485// is string s a date? (see above)
40486exports.isDateTime = function(s, calendar) {
40487 return (exports.dateTime2ms(s, calendar) !== BADNUM);
40488};
40489
40490// pad a number with zeroes, to given # of digits before the decimal point
40491function lpad(val, digits) {
40492 return String(val + Math.pow(10, digits)).substr(1);
40493}
40494
40495/**
40496 * Turn ms into string of the form YYYY-mm-dd HH:MM:SS.ssss
40497 * Crop any trailing zeros in time, except never stop right after hours
40498 * (we could choose to crop '-01' from date too but for now we always
40499 * show the whole date)
40500 * Optional range r is the data range that applies, also in ms.
40501 * If rng is big, the later parts of time will be omitted
40502 */
40503var NINETYDAYS = 90 * ONEDAY;
40504var THREEHOURS = 3 * ONEHOUR;
40505var FIVEMIN = 5 * ONEMIN;
40506exports.ms2DateTime = function(ms, r, calendar) {
40507 if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
40508
40509 if(!r) r = 0;
40510
40511 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
40512 var msRounded = Math.round(ms - msecTenths / 10);
40513 var dateStr, h, m, s, msec10, d;
40514
40515 if(isWorldCalendar(calendar)) {
40516 var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD;
40517 var timeMs = Math.floor(mod(ms, ONEDAY));
40518 try {
40519 dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
40520 .fromJD(dateJD).formatDate('yyyy-mm-dd');
40521 } catch(e) {
40522 // invalid date in this calendar - fall back to Gyyyy-mm-dd
40523 dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
40524 }
40525
40526 // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
40527 // other things for a few calendars, so we can't trust it. Just pad
40528 // it manually (after the '-' if there is one)
40529 if(dateStr.charAt(0) === '-') {
40530 while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
40531 } else {
40532 while(dateStr.length < 10) dateStr = '0' + dateStr;
40533 }
40534
40535 // TODO: if this is faster, we could use this block for extracting
40536 // the time components of regular gregorian too
40537 h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
40538 m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
40539 s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
40540 msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
40541 } else {
40542 d = new Date(msRounded);
40543
40544 dateStr = utcFormat('%Y-%m-%d')(d);
40545
40546 // <90 days: add hours and minutes - never *only* add hours
40547 h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
40548 m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
40549 // <3 hours: add seconds
40550 s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
40551 // <5 minutes: add ms (plus one extra digit, this is msec*10)
40552 msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
40553 }
40554
40555 return includeTime(dateStr, h, m, s, msec10);
40556};
40557
40558// For converting old-style milliseconds to date strings,
40559// we use the local timezone rather than UTC like we use
40560// everywhere else, both for backward compatibility and
40561// because that's how people mostly use javasript date objects.
40562// Clip one extra day off our date range though so we can't get
40563// thrown beyond the range by the timezone shift.
40564exports.ms2DateTimeLocal = function(ms) {
40565 if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
40566
40567 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
40568 var d = new Date(Math.round(ms - msecTenths / 10));
40569 var dateStr = d3.time.format('%Y-%m-%d')(d);
40570 var h = d.getHours();
40571 var m = d.getMinutes();
40572 var s = d.getSeconds();
40573 var msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
40574
40575 return includeTime(dateStr, h, m, s, msec10);
40576};
40577
40578function includeTime(dateStr, h, m, s, msec10) {
40579 // include each part that has nonzero data in or after it
40580 if(h || m || s || msec10) {
40581 dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2);
40582 if(s || msec10) {
40583 dateStr += ':' + lpad(s, 2);
40584 if(msec10) {
40585 var digits = 4;
40586 while(msec10 % 10 === 0) {
40587 digits -= 1;
40588 msec10 /= 10;
40589 }
40590 dateStr += '.' + lpad(msec10, digits);
40591 }
40592 }
40593 }
40594 return dateStr;
40595}
40596
40597// normalize date format to date string, in case it starts as
40598// a Date object or milliseconds
40599// optional dflt is the return value if cleaning fails
40600exports.cleanDate = function(v, dflt, calendar) {
40601 // let us use cleanDate to provide a missing default without an error
40602 if(v === BADNUM) return dflt;
40603 if(exports.isJSDate(v) || (typeof v === 'number' && isFinite(v))) {
40604 // do not allow milliseconds (old) or jsdate objects (inherently
40605 // described as gregorian dates) with world calendars
40606 if(isWorldCalendar(calendar)) {
40607 Loggers.error('JS Dates and milliseconds are incompatible with world calendars', v);
40608 return dflt;
40609 }
40610
40611 // NOTE: if someone puts in a year as a number rather than a string,
40612 // this will mistakenly convert it thinking it's milliseconds from 1970
40613 // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
40614 v = exports.ms2DateTimeLocal(+v);
40615 if(!v && dflt !== undefined) return dflt;
40616 } else if(!exports.isDateTime(v, calendar)) {
40617 Loggers.error('unrecognized date', v);
40618 return dflt;
40619 }
40620 return v;
40621};
40622
40623/*
40624 * Date formatting for ticks and hovertext
40625 */
40626
40627/*
40628 * modDateFormat: Support world calendars, and add one item to
40629 * d3's vocabulary:
40630 * %{n}f where n is the max number of digits of fractional seconds
40631 */
40632var fracMatch = /%\d?f/g;
40633function modDateFormat(fmt, x, formatter, calendar) {
40634 fmt = fmt.replace(fracMatch, function(match) {
40635 var digits = Math.min(+(match.charAt(1)) || 6, 6);
40636 var fracSecs = ((x / 1000 % 1) + 2)
40637 .toFixed(digits)
40638 .substr(2).replace(/0+$/, '') || '0';
40639 return fracSecs;
40640 });
40641
40642 var d = new Date(Math.floor(x + 0.05));
40643
40644 if(isWorldCalendar(calendar)) {
40645 try {
40646 fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar);
40647 } catch(e) {
40648 return 'Invalid';
40649 }
40650 }
40651 return formatter(fmt)(d);
40652}
40653
40654/*
40655 * formatTime: create a time string from:
40656 * x: milliseconds
40657 * tr: tickround ('M', 'S', or # digits)
40658 * only supports UTC times (where every day is 24 hours and 0 is at midnight)
40659 */
40660var MAXSECONDS = [59, 59.9, 59.99, 59.999, 59.9999];
40661function formatTime(x, tr) {
40662 var timePart = mod(x + 0.05, ONEDAY);
40663
40664 var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' +
40665 lpad(mod(Math.floor(timePart / ONEMIN), 60), 2);
40666
40667 if(tr !== 'M') {
40668 if(!isNumeric(tr)) tr = 0; // should only be 'S'
40669
40670 /*
40671 * this is a weird one - and shouldn't come up unless people
40672 * monkey with tick0 in weird ways, but we need to do something!
40673 * IN PARTICULAR we had better not display garbage (see below)
40674 * for numbers we always round to the nearest increment of the
40675 * precision we're showing, and this seems like the right way to
40676 * handle seconds and milliseconds, as they have a decimal point
40677 * and people will interpret that to mean rounding like numbers.
40678 * but for larger increments we floor the value: it's always
40679 * 2013 until the ball drops on the new year. We could argue about
40680 * which field it is where we start rounding (should 12:08:59
40681 * round to 12:09 if we're stopping at minutes?) but for now I'll
40682 * say we round seconds but floor everything else. BUT that means
40683 * we need to never round up to 60 seconds, ie 23:59:60
40684 */
40685 var sec = Math.min(mod(x / ONESEC, 60), MAXSECONDS[tr]);
40686
40687 var secStr = (100 + sec).toFixed(tr).substr(1);
40688 if(tr > 0) {
40689 secStr = secStr.replace(/0+$/, '').replace(/[\.]$/, '');
40690 }
40691
40692 timeStr += ':' + secStr;
40693 }
40694 return timeStr;
40695}
40696
40697/*
40698 * formatDate: turn a date into tick or hover label text.
40699 *
40700 * x: milliseconds, the value to convert
40701 * fmt: optional, an explicit format string (d3 format, even for world calendars)
40702 * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits)
40703 * used if no explicit fmt is provided
40704 * formatter: locale-aware d3 date formatter for standard gregorian calendars
40705 * should be the result of exports.getD3DateFormat(gd)
40706 * calendar: optional string, the world calendar system to use
40707 *
40708 * returns the date/time as a string, potentially with the leading portion
40709 * on a separate line (after '\n')
40710 * Note that this means if you provide an explicit format which includes '\n'
40711 * the axis may choose to strip things after it when they don't change from
40712 * one tick to the next (as it does with automatic formatting)
40713 */
40714exports.formatDate = function(x, fmt, tr, formatter, calendar, extraFormat) {
40715 calendar = isWorldCalendar(calendar) && calendar;
40716
40717 if(!fmt) {
40718 if(tr === 'y') fmt = extraFormat.year;
40719 else if(tr === 'm') fmt = extraFormat.month;
40720 else if(tr === 'd') {
40721 fmt = extraFormat.dayMonth + '\n' + extraFormat.year;
40722 } else {
40723 return formatTime(x, tr) + '\n' + modDateFormat(extraFormat.dayMonthYear, x, formatter, calendar);
40724 }
40725 }
40726
40727 return modDateFormat(fmt, x, formatter, calendar);
40728};
40729
40730/*
40731 * incrementMonth: make a new milliseconds value from the given one,
40732 * having changed the month
40733 *
40734 * special case for world calendars: multiples of 12 are treated as years,
40735 * even for calendar systems that don't have (always or ever) 12 months/year
40736 * TODO: perhaps we need a different code for year increments to support this?
40737 *
40738 * ms (number): the initial millisecond value
40739 * dMonth (int): the (signed) number of months to shift
40740 * calendar (string): the calendar system to use
40741 *
40742 * changing month does not (and CANNOT) always preserve day, since
40743 * months have different lengths. The worst example of this is:
40744 * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3
40745 *
40746 * But we want to be able to iterate over the last day of each month,
40747 * regardless of what its number is.
40748 * So shift 3 days forward, THEN set the new month, then unshift:
40749 * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ...
40750 *
40751 * Note that odd behavior still exists if you start from the 26th-28th:
40752 * 1/28 -> 2/28 -> 3/31
40753 * but at least you can't shift any dates into the wrong month,
40754 * and ticks on these days incrementing by month would be very unusual
40755 */
40756var THREEDAYS = 3 * ONEDAY;
40757exports.incrementMonth = function(ms, dMonth, calendar) {
40758 calendar = isWorldCalendar(calendar) && calendar;
40759
40760 // pull time out and operate on pure dates, then add time back at the end
40761 // this gives maximum precision - not that we *normally* care if we're
40762 // incrementing by month, but better to be safe!
40763 var timeMs = mod(ms, ONEDAY);
40764 ms = Math.round(ms - timeMs);
40765
40766 if(calendar) {
40767 try {
40768 var dateJD = Math.round(ms / ONEDAY) + EPOCHJD;
40769 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
40770 var cDate = calInstance.fromJD(dateJD);
40771
40772 if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
40773 else calInstance.add(cDate, dMonth / 12, 'y');
40774
40775 return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
40776 } catch(e) {
40777 Loggers.error('invalid ms ' + ms + ' in calendar ' + calendar);
40778 // then keep going in gregorian even though the result will be 'Invalid'
40779 }
40780 }
40781
40782 var y = new Date(ms + THREEDAYS);
40783 return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
40784};
40785
40786/*
40787 * findExactDates: what fraction of data is exact days, months, or years?
40788 *
40789 * data: array of millisecond values
40790 * calendar (string) the calendar to test against
40791 */
40792exports.findExactDates = function(data, calendar) {
40793 var exactYears = 0;
40794 var exactMonths = 0;
40795 var exactDays = 0;
40796 var blankCount = 0;
40797 var d;
40798 var di;
40799
40800 var calInstance = (
40801 isWorldCalendar(calendar) &&
40802 Registry.getComponentMethod('calendars', 'getCal')(calendar)
40803 );
40804
40805 for(var i = 0; i < data.length; i++) {
40806 di = data[i];
40807
40808 // not date data at all
40809 if(!isNumeric(di)) {
40810 blankCount ++;
40811 continue;
40812 }
40813
40814 // not an exact date
40815 if(di % ONEDAY) continue;
40816
40817 if(calInstance) {
40818 try {
40819 d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
40820 if(d.day() === 1) {
40821 if(d.month() === 1) exactYears++;
40822 else exactMonths++;
40823 } else exactDays++;
40824 } catch(e) {
40825 // invalid date in this calendar - ignore it here.
40826 }
40827 } else {
40828 d = new Date(di);
40829 if(d.getUTCDate() === 1) {
40830 if(d.getUTCMonth() === 0) exactYears++;
40831 else exactMonths++;
40832 } else exactDays++;
40833 }
40834 }
40835 exactMonths += exactYears;
40836 exactDays += exactMonths;
40837
40838 var dataCount = data.length - blankCount;
40839
40840 return {
40841 exactYears: exactYears / dataCount,
40842 exactMonths: exactMonths / dataCount,
40843 exactDays: exactDays / dataCount
40844 };
40845};
40846
40847},{"../constants/numerical":155,"../registry":272,"./loggers":181,"./mod":184,"d3":13,"fast-isnumeric":15}],168:[function(_dereq_,module,exports){
40848/**
40849* Copyright 2012-2020, Plotly, Inc.
40850* All rights reserved.
40851*
40852* This source code is licensed under the MIT license found in the
40853* LICENSE file in the root directory of this source tree.
40854*/
40855
40856'use strict';
40857
40858var d3 = _dereq_('d3');
40859var loggers = _dereq_('./loggers');
40860
40861/**
40862 * Allow referencing a graph DOM element either directly
40863 * or by its id string
40864 *
40865 * @param {HTMLDivElement|string} gd: a graph element or its id
40866 *
40867 * @returns {HTMLDivElement} the DOM element of the graph
40868 */
40869function getGraphDiv(gd) {
40870 var gdElement;
40871
40872 if(typeof gd === 'string') {
40873 gdElement = document.getElementById(gd);
40874
40875 if(gdElement === null) {
40876 throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
40877 }
40878
40879 return gdElement;
40880 } else if(gd === null || gd === undefined) {
40881 throw new Error('DOM element provided is null or undefined');
40882 }
40883
40884 // otherwise assume that gd is a DOM element
40885 return gd;
40886}
40887
40888function isPlotDiv(el) {
40889 var el3 = d3.select(el);
40890 return el3.node() instanceof HTMLElement &&
40891 el3.size() &&
40892 el3.classed('js-plotly-plot');
40893}
40894
40895function removeElement(el) {
40896 var elParent = el && el.parentNode;
40897 if(elParent) elParent.removeChild(el);
40898}
40899
40900/**
40901 * for dynamically adding style rules
40902 * makes one stylesheet that contains all rules added
40903 * by all calls to this function
40904 */
40905function addStyleRule(selector, styleString) {
40906 addRelatedStyleRule('global', selector, styleString);
40907}
40908
40909/**
40910 * for dynamically adding style rules
40911 * to a stylesheet uniquely identified by a uid
40912 */
40913function addRelatedStyleRule(uid, selector, styleString) {
40914 var id = 'plotly.js-style-' + uid;
40915 var style = document.getElementById(id);
40916 if(!style) {
40917 style = document.createElement('style');
40918 style.setAttribute('id', id);
40919 // WebKit hack :(
40920 style.appendChild(document.createTextNode(''));
40921 document.head.appendChild(style);
40922 }
40923 var styleSheet = style.sheet;
40924
40925 if(styleSheet.insertRule) {
40926 styleSheet.insertRule(selector + '{' + styleString + '}', 0);
40927 } else if(styleSheet.addRule) {
40928 styleSheet.addRule(selector, styleString, 0);
40929 } else loggers.warn('addStyleRule failed');
40930}
40931
40932/**
40933 * to remove from the page a stylesheet identified by a given uid
40934 */
40935function deleteRelatedStyleRule(uid) {
40936 var id = 'plotly.js-style-' + uid;
40937 var style = document.getElementById(id);
40938 if(style) removeElement(style);
40939}
40940
40941module.exports = {
40942 getGraphDiv: getGraphDiv,
40943 isPlotDiv: isPlotDiv,
40944 removeElement: removeElement,
40945 addStyleRule: addStyleRule,
40946 addRelatedStyleRule: addRelatedStyleRule,
40947 deleteRelatedStyleRule: deleteRelatedStyleRule
40948};
40949
40950},{"./loggers":181,"d3":13}],169:[function(_dereq_,module,exports){
40951/**
40952* Copyright 2012-2020, Plotly, Inc.
40953* All rights reserved.
40954*
40955* This source code is licensed under the MIT license found in the
40956* LICENSE file in the root directory of this source tree.
40957*/
40958
40959
40960'use strict';
40961
40962/* global jQuery:false */
40963
40964var EventEmitter = _dereq_('events').EventEmitter;
40965
40966var Events = {
40967
40968 init: function(plotObj) {
40969 /*
40970 * If we have already instantiated an emitter for this plot
40971 * return early.
40972 */
40973 if(plotObj._ev instanceof EventEmitter) return plotObj;
40974
40975 var ev = new EventEmitter();
40976 var internalEv = new EventEmitter();
40977
40978 /*
40979 * Assign to plot._ev while we still live in a land
40980 * where plot is a DOM element with stuff attached to it.
40981 * In the future we can make plot the event emitter itself.
40982 */
40983 plotObj._ev = ev;
40984
40985 /*
40986 * Create a second event handler that will manage events *internally*.
40987 * This allows parts of plotly to respond to thing like relayout without
40988 * having to use the user-facing event handler. They cannot peacefully
40989 * coexist on the same handler because a user invoking
40990 * plotObj.removeAllListeners() would detach internal events, breaking
40991 * plotly.
40992 */
40993 plotObj._internalEv = internalEv;
40994
40995 /*
40996 * Assign bound methods from the ev to the plot object. These methods
40997 * will reference the 'this' of plot._ev even though they are methods
40998 * of plot. This will keep the event machinery away from the plot object
40999 * which currently is often a DOM element but presents an API that will
41000 * continue to function when plot becomes an emitter. Not all EventEmitter
41001 * methods have been bound to `plot` as some do not currently add value to
41002 * the Plotly event API.
41003 */
41004 plotObj.on = ev.on.bind(ev);
41005 plotObj.once = ev.once.bind(ev);
41006 plotObj.removeListener = ev.removeListener.bind(ev);
41007 plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
41008
41009 /*
41010 * Create functions for managing internal events. These are *only* triggered
41011 * by the mirroring of external events via the emit function.
41012 */
41013 plotObj._internalOn = internalEv.on.bind(internalEv);
41014 plotObj._internalOnce = internalEv.once.bind(internalEv);
41015 plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
41016 plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
41017
41018 /*
41019 * We must wrap emit to continue to support JQuery events. The idea
41020 * is to check to see if the user is using JQuery events, if they are
41021 * we emit JQuery events to trigger user handlers as well as the EventEmitter
41022 * events.
41023 */
41024 plotObj.emit = function(event, data) {
41025 if(typeof jQuery !== 'undefined') {
41026 jQuery(plotObj).trigger(event, data);
41027 }
41028
41029 ev.emit(event, data);
41030 internalEv.emit(event, data);
41031 };
41032
41033 return plotObj;
41034 },
41035
41036 /*
41037 * This function behaves like jQuery's triggerHandler. It calls
41038 * all handlers for a particular event and returns the return value
41039 * of the LAST handler. This function also triggers jQuery's
41040 * triggerHandler for backwards compatibility.
41041 */
41042 triggerHandler: function(plotObj, event, data) {
41043 var jQueryHandlerValue;
41044 var nodeEventHandlerValue;
41045
41046 /*
41047 * If jQuery exists run all its handlers for this event and
41048 * collect the return value of the LAST handler function
41049 */
41050 if(typeof jQuery !== 'undefined') {
41051 jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
41052 }
41053
41054 /*
41055 * Now run all the node style event handlers
41056 */
41057 var ev = plotObj._ev;
41058 if(!ev) return jQueryHandlerValue;
41059
41060 var handlers = ev._events[event];
41061 if(!handlers) return jQueryHandlerValue;
41062
41063 // making sure 'this' is the EventEmitter instance
41064 function apply(handler) {
41065 // The 'once' case, we can't just call handler() as we need
41066 // the return value here. So,
41067 // - remove handler
41068 // - call listener and grab return value!
41069 // - stash 'fired' key to not call handler twice
41070 if(handler.listener) {
41071 ev.removeListener(event, handler.listener);
41072 if(!handler.fired) {
41073 handler.fired = true;
41074 return handler.listener.apply(ev, [data]);
41075 }
41076 } else {
41077 return handler.apply(ev, [data]);
41078 }
41079 }
41080
41081 // handlers can be function or an array of functions
41082 handlers = Array.isArray(handlers) ? handlers : [handlers];
41083
41084 var i;
41085 for(i = 0; i < handlers.length - 1; i++) {
41086 apply(handlers[i]);
41087 }
41088 // now call the final handler and collect its value
41089 nodeEventHandlerValue = apply(handlers[i]);
41090
41091 /*
41092 * Return either the jQuery handler value if it exists or the
41093 * nodeEventHandler value. jQuery event value supersedes nodejs
41094 * events for backwards compatibility reasons.
41095 */
41096 return jQueryHandlerValue !== undefined ?
41097 jQueryHandlerValue :
41098 nodeEventHandlerValue;
41099 },
41100
41101 purge: function(plotObj) {
41102 delete plotObj._ev;
41103 delete plotObj.on;
41104 delete plotObj.once;
41105 delete plotObj.removeListener;
41106 delete plotObj.removeAllListeners;
41107 delete plotObj.emit;
41108
41109 delete plotObj._ev;
41110 delete plotObj._internalEv;
41111 delete plotObj._internalOn;
41112 delete plotObj._internalOnce;
41113 delete plotObj._removeInternalListener;
41114 delete plotObj._removeAllInternalListeners;
41115
41116 return plotObj;
41117 }
41118
41119};
41120
41121module.exports = Events;
41122
41123},{"events":11}],170:[function(_dereq_,module,exports){
41124/**
41125* Copyright 2012-2020, Plotly, Inc.
41126* All rights reserved.
41127*
41128* This source code is licensed under the MIT license found in the
41129* LICENSE file in the root directory of this source tree.
41130*/
41131
41132
41133'use strict';
41134
41135var isPlainObject = _dereq_('./is_plain_object.js');
41136var isArray = Array.isArray;
41137
41138function primitivesLoopSplice(source, target) {
41139 var i, value;
41140 for(i = 0; i < source.length; i++) {
41141 value = source[i];
41142 if(value !== null && typeof(value) === 'object') {
41143 return false;
41144 }
41145 if(value !== void(0)) {
41146 target[i] = value;
41147 }
41148 }
41149 return true;
41150}
41151
41152exports.extendFlat = function() {
41153 return _extend(arguments, false, false, false);
41154};
41155
41156exports.extendDeep = function() {
41157 return _extend(arguments, true, false, false);
41158};
41159
41160exports.extendDeepAll = function() {
41161 return _extend(arguments, true, true, false);
41162};
41163
41164exports.extendDeepNoArrays = function() {
41165 return _extend(arguments, true, false, true);
41166};
41167
41168/*
41169 * Inspired by https://github.com/justmoon/node-extend/blob/master/index.js
41170 * All credit to the jQuery authors for perfecting this amazing utility.
41171 *
41172 * API difference with jQuery version:
41173 * - No optional boolean (true -> deep extend) first argument,
41174 * use `extendFlat` for first-level only extend and
41175 * use `extendDeep` for a deep extend.
41176 *
41177 * Other differences with jQuery version:
41178 * - Uses a modern (and faster) isPlainObject routine.
41179 * - Expected to work with object {} and array [] arguments only.
41180 * - Does not check for circular structure.
41181 * FYI: jQuery only does a check across one level.
41182 * Warning: this might result in infinite loops.
41183 *
41184 */
41185function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) {
41186 var target = inputs[0];
41187 var length = inputs.length;
41188
41189 var input, key, src, copy, copyIsArray, clone, allPrimitives;
41190
41191 // TODO does this do the right thing for typed arrays?
41192
41193 if(length === 2 && isArray(target) && isArray(inputs[1]) && target.length === 0) {
41194 allPrimitives = primitivesLoopSplice(inputs[1], target);
41195
41196 if(allPrimitives) {
41197 return target;
41198 } else {
41199 target.splice(0, target.length); // reset target and continue to next block
41200 }
41201 }
41202
41203 for(var i = 1; i < length; i++) {
41204 input = inputs[i];
41205
41206 for(key in input) {
41207 src = target[key];
41208 copy = input[key];
41209
41210 if(noArrayCopies && isArray(copy)) {
41211 // Stop early and just transfer the array if array copies are disallowed:
41212
41213 target[key] = copy;
41214 } else if(isDeep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
41215 // recurse if we're merging plain objects or arrays
41216
41217 if(copyIsArray) {
41218 copyIsArray = false;
41219 clone = src && isArray(src) ? src : [];
41220 } else {
41221 clone = src && isPlainObject(src) ? src : {};
41222 }
41223
41224 // never move original objects, clone them
41225 target[key] = _extend([clone, copy], isDeep, keepAllKeys, noArrayCopies);
41226 } else if(typeof copy !== 'undefined' || keepAllKeys) {
41227 // don't bring in undefined values, except for extendDeepAll
41228
41229 target[key] = copy;
41230 }
41231 }
41232 }
41233
41234 return target;
41235}
41236
41237},{"./is_plain_object.js":178}],171:[function(_dereq_,module,exports){
41238/**
41239* Copyright 2012-2020, Plotly, Inc.
41240* All rights reserved.
41241*
41242* This source code is licensed under the MIT license found in the
41243* LICENSE file in the root directory of this source tree.
41244*/
41245
41246
41247'use strict';
41248
41249
41250/**
41251 * Return news array containing only the unique items
41252 * found in input array.
41253 *
41254 * IMPORTANT: Note that items are considered unique
41255 * if `String({})` is unique. For example;
41256 *
41257 * Lib.filterUnique([ { a: 1 }, { b: 2 } ])
41258 *
41259 * returns [{ a: 1 }]
41260 *
41261 * and
41262 *
41263 * Lib.filterUnique([ '1', 1 ])
41264 *
41265 * returns ['1']
41266 *
41267 *
41268 * @param {array} array base array
41269 * @return {array} new filtered array
41270 */
41271module.exports = function filterUnique(array) {
41272 var seen = {};
41273 var out = [];
41274 var j = 0;
41275
41276 for(var i = 0; i < array.length; i++) {
41277 var item = array[i];
41278
41279 if(seen[item] !== 1) {
41280 seen[item] = 1;
41281 out[j++] = item;
41282 }
41283 }
41284
41285 return out;
41286};
41287
41288},{}],172:[function(_dereq_,module,exports){
41289/**
41290* Copyright 2012-2020, Plotly, Inc.
41291* All rights reserved.
41292*
41293* This source code is licensed under the MIT license found in the
41294* LICENSE file in the root directory of this source tree.
41295*/
41296
41297'use strict';
41298
41299/** Filter out object items with visible !== true
41300 * insider array container.
41301 *
41302 * @param {array of objects} container
41303 * @return {array of objects} of length <= container
41304 *
41305 */
41306module.exports = function filterVisible(container) {
41307 var filterFn = isCalcData(container) ? calcDataFilter : baseFilter;
41308 var out = [];
41309
41310 for(var i = 0; i < container.length; i++) {
41311 var item = container[i];
41312 if(filterFn(item)) out.push(item);
41313 }
41314
41315 return out;
41316};
41317
41318function baseFilter(item) {
41319 return item.visible === true;
41320}
41321
41322function calcDataFilter(item) {
41323 var trace = item[0].trace;
41324 return trace.visible === true && trace._length !== 0;
41325}
41326
41327function isCalcData(cont) {
41328 return (
41329 Array.isArray(cont) &&
41330 Array.isArray(cont[0]) &&
41331 cont[0][0] &&
41332 cont[0][0].trace
41333 );
41334}
41335
41336},{}],173:[function(_dereq_,module,exports){
41337/**
41338* Copyright 2012-2020, Plotly, Inc.
41339* All rights reserved.
41340*
41341* This source code is licensed under the MIT license found in the
41342* LICENSE file in the root directory of this source tree.
41343*/
41344
41345'use strict';
41346
41347var d3 = _dereq_('d3');
41348var countryRegex = _dereq_('country-regex');
41349var turfArea = _dereq_('@turf/area');
41350var turfCentroid = _dereq_('@turf/centroid');
41351var turfBbox = _dereq_('@turf/bbox');
41352
41353var identity = _dereq_('./identity');
41354var loggers = _dereq_('./loggers');
41355var isPlainObject = _dereq_('./is_plain_object');
41356var nestedProperty = _dereq_('./nested_property');
41357var polygon = _dereq_('./polygon');
41358
41359// make list of all country iso3 ids from at runtime
41360var countryIds = Object.keys(countryRegex);
41361
41362var locationmodeToIdFinder = {
41363 'ISO-3': identity,
41364 'USA-states': identity,
41365 'country names': countryNameToISO3
41366};
41367
41368function countryNameToISO3(countryName) {
41369 for(var i = 0; i < countryIds.length; i++) {
41370 var iso3 = countryIds[i];
41371 var regex = new RegExp(countryRegex[iso3]);
41372
41373 if(regex.test(countryName.trim().toLowerCase())) return iso3;
41374 }
41375
41376 loggers.log('Unrecognized country name: ' + countryName + '.');
41377
41378 return false;
41379}
41380
41381function locationToFeature(locationmode, location, features) {
41382 if(!location || typeof location !== 'string') return false;
41383
41384 var locationId = locationmodeToIdFinder[locationmode](location);
41385 var filteredFeatures;
41386 var f, i;
41387
41388 if(locationId) {
41389 if(locationmode === 'USA-states') {
41390 // Filter out features out in USA
41391 //
41392 // This is important as the Natural Earth files
41393 // include state/provinces from USA, Canada, Australia and Brazil
41394 // which have some overlay in their two-letter ids. For example,
41395 // 'WA' is used for both Washington state and Western Australia.
41396 filteredFeatures = [];
41397 for(i = 0; i < features.length; i++) {
41398 f = features[i];
41399 if(f.properties && f.properties.gu && f.properties.gu === 'USA') {
41400 filteredFeatures.push(f);
41401 }
41402 }
41403 } else {
41404 filteredFeatures = features;
41405 }
41406
41407 for(i = 0; i < filteredFeatures.length; i++) {
41408 f = filteredFeatures[i];
41409 if(f.id === locationId) return f;
41410 }
41411
41412 loggers.log([
41413 'Location with id', locationId,
41414 'does not have a matching topojson feature at this resolution.'
41415 ].join(' '));
41416 }
41417
41418 return false;
41419}
41420
41421function feature2polygons(feature) {
41422 var geometry = feature.geometry;
41423 var coords = geometry.coordinates;
41424 var loc = feature.id;
41425
41426 var polygons = [];
41427 var appendPolygon, j, k, m;
41428
41429 function doesCrossAntiMerdian(pts) {
41430 for(var l = 0; l < pts.length - 1; l++) {
41431 if(pts[l][0] > 0 && pts[l + 1][0] < 0) return l;
41432 }
41433 return null;
41434 }
41435
41436 if(loc === 'RUS' || loc === 'FJI') {
41437 // Russia and Fiji have landmasses that cross the antimeridian,
41438 // we need to add +360 to their longitude coordinates, so that
41439 // polygon 'contains' doesn't get confused when crossing the antimeridian.
41440 //
41441 // Note that other countries have polygons on either side of the antimeridian
41442 // (e.g. some Aleutian island for the USA), but those don't confuse
41443 // the 'contains' method; these are skipped here.
41444 appendPolygon = function(_pts) {
41445 var pts;
41446
41447 if(doesCrossAntiMerdian(_pts) === null) {
41448 pts = _pts;
41449 } else {
41450 pts = new Array(_pts.length);
41451 for(m = 0; m < _pts.length; m++) {
41452 // do not mutate calcdata[i][j].geojson !!
41453 pts[m] = [
41454 _pts[m][0] < 0 ? _pts[m][0] + 360 : _pts[m][0],
41455 _pts[m][1]
41456 ];
41457 }
41458 }
41459
41460 polygons.push(polygon.tester(pts));
41461 };
41462 } else if(loc === 'ATA') {
41463 // Antarctica has a landmass that wraps around every longitudes which
41464 // confuses the 'contains' methods.
41465 appendPolygon = function(pts) {
41466 var crossAntiMeridianIndex = doesCrossAntiMerdian(pts);
41467
41468 // polygon that do not cross anti-meridian need no special handling
41469 if(crossAntiMeridianIndex === null) {
41470 return polygons.push(polygon.tester(pts));
41471 }
41472
41473 // stitch polygon by adding pt over South Pole,
41474 // so that it covers the projected region covers all latitudes
41475 //
41476 // Note that the algorithm below only works for polygons that
41477 // start and end on longitude -180 (like the ones built by
41478 // https://github.com/etpinard/sane-topojson).
41479 var stitch = new Array(pts.length + 1);
41480 var si = 0;
41481
41482 for(m = 0; m < pts.length; m++) {
41483 if(m > crossAntiMeridianIndex) {
41484 stitch[si++] = [pts[m][0] + 360, pts[m][1]];
41485 } else if(m === crossAntiMeridianIndex) {
41486 stitch[si++] = pts[m];
41487 stitch[si++] = [pts[m][0], -90];
41488 } else {
41489 stitch[si++] = pts[m];
41490 }
41491 }
41492
41493 // polygon.tester by default appends pt[0] to the points list,
41494 // we must remove it here, to avoid a jump in longitude from 180 to -180,
41495 // that would confuse the 'contains' method
41496 var tester = polygon.tester(stitch);
41497 tester.pts.pop();
41498 polygons.push(tester);
41499 };
41500 } else {
41501 // otherwise using same array ref is fine
41502 appendPolygon = function(pts) {
41503 polygons.push(polygon.tester(pts));
41504 };
41505 }
41506
41507 switch(geometry.type) {
41508 case 'MultiPolygon':
41509 for(j = 0; j < coords.length; j++) {
41510 for(k = 0; k < coords[j].length; k++) {
41511 appendPolygon(coords[j][k]);
41512 }
41513 }
41514 break;
41515 case 'Polygon':
41516 for(j = 0; j < coords.length; j++) {
41517 appendPolygon(coords[j]);
41518 }
41519 break;
41520 }
41521
41522 return polygons;
41523}
41524
41525function getTraceGeojson(trace) {
41526 var g = trace.geojson;
41527 var PlotlyGeoAssets = window.PlotlyGeoAssets || {};
41528 var geojsonIn = typeof g === 'string' ? PlotlyGeoAssets[g] : g;
41529
41530 // This should not happen, but just in case something goes
41531 // really wrong when fetching the GeoJSON
41532 if(!isPlainObject(geojsonIn)) {
41533 loggers.error('Oops ... something went wrong when fetching ' + g);
41534 return false;
41535 }
41536
41537 return geojsonIn;
41538}
41539
41540function extractTraceFeature(calcTrace) {
41541 var trace = calcTrace[0].trace;
41542
41543 var geojsonIn = getTraceGeojson(trace);
41544 if(!geojsonIn) return false;
41545
41546 var lookup = {};
41547 var featuresOut = [];
41548 var i;
41549
41550 for(i = 0; i < trace._length; i++) {
41551 var cdi = calcTrace[i];
41552 if(cdi.loc || cdi.loc === 0) {
41553 lookup[cdi.loc] = cdi;
41554 }
41555 }
41556
41557 function appendFeature(fIn) {
41558 var id = nestedProperty(fIn, trace.featureidkey || 'id').get();
41559 var cdi = lookup[id];
41560
41561 if(cdi) {
41562 var geometry = fIn.geometry;
41563
41564 if(geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
41565 var fOut = {
41566 type: 'Feature',
41567 id: id,
41568 geometry: geometry,
41569 properties: {}
41570 };
41571
41572 // Compute centroid, add it to the properties
41573 fOut.properties.ct = findCentroid(fOut);
41574
41575 // Mutate in in/out features into calcdata
41576 cdi.fIn = fIn;
41577 cdi.fOut = fOut;
41578
41579 featuresOut.push(fOut);
41580 } else {
41581 loggers.log([
41582 'Location', cdi.loc, 'does not have a valid GeoJSON geometry.',
41583 'Traces with locationmode *geojson-id* only support',
41584 '*Polygon* and *MultiPolygon* geometries.'
41585 ].join(' '));
41586 }
41587 }
41588
41589 // remove key from lookup, so that we can track (if any)
41590 // the locations that did not have a corresponding GeoJSON feature
41591 delete lookup[id];
41592 }
41593
41594 switch(geojsonIn.type) {
41595 case 'FeatureCollection':
41596 var featuresIn = geojsonIn.features;
41597 for(i = 0; i < featuresIn.length; i++) {
41598 appendFeature(featuresIn[i]);
41599 }
41600 break;
41601 case 'Feature':
41602 appendFeature(geojsonIn);
41603 break;
41604 default:
41605 loggers.warn([
41606 'Invalid GeoJSON type', (geojsonIn.type || 'none') + '.',
41607 'Traces with locationmode *geojson-id* only support',
41608 '*FeatureCollection* and *Feature* types.'
41609 ].join(' '));
41610 return false;
41611 }
41612
41613 for(var loc in lookup) {
41614 loggers.log([
41615 'Location *' + loc + '*',
41616 'does not have a matching feature with id-key',
41617 '*' + trace.featureidkey + '*.'
41618 ].join(' '));
41619 }
41620
41621 return featuresOut;
41622}
41623
41624// TODO this find the centroid of the polygon of maxArea
41625// (just like we currently do for geo choropleth polygons),
41626// maybe instead it would make more sense to compute the centroid
41627// of each polygon and consider those on hover/select
41628function findCentroid(feature) {
41629 var geometry = feature.geometry;
41630 var poly;
41631
41632 if(geometry.type === 'MultiPolygon') {
41633 var coords = geometry.coordinates;
41634 var maxArea = 0;
41635
41636 for(var i = 0; i < coords.length; i++) {
41637 var polyi = {type: 'Polygon', coordinates: coords[i]};
41638 var area = turfArea.default(polyi);
41639 if(area > maxArea) {
41640 maxArea = area;
41641 poly = polyi;
41642 }
41643 }
41644 } else {
41645 poly = geometry;
41646 }
41647
41648 return turfCentroid.default(poly).geometry.coordinates;
41649}
41650
41651function fetchTraceGeoData(calcData) {
41652 var PlotlyGeoAssets = window.PlotlyGeoAssets || {};
41653 var promises = [];
41654
41655 function fetch(url) {
41656 return new Promise(function(resolve, reject) {
41657 d3.json(url, function(err, d) {
41658 if(err) {
41659 delete PlotlyGeoAssets[url];
41660 var msg = err.status === 404 ?
41661 ('GeoJSON at URL "' + url + '" does not exist.') :
41662 ('Unexpected error while fetching from ' + url);
41663 return reject(new Error(msg));
41664 }
41665
41666 PlotlyGeoAssets[url] = d;
41667 return resolve(d);
41668 });
41669 });
41670 }
41671
41672 function wait(url) {
41673 return new Promise(function(resolve, reject) {
41674 var cnt = 0;
41675 var interval = setInterval(function() {
41676 if(PlotlyGeoAssets[url] && PlotlyGeoAssets[url] !== 'pending') {
41677 clearInterval(interval);
41678 return resolve(PlotlyGeoAssets[url]);
41679 }
41680 if(cnt > 100) {
41681 clearInterval(interval);
41682 return reject('Unexpected error while fetching from ' + url);
41683 }
41684 cnt++;
41685 }, 50);
41686 });
41687 }
41688
41689 for(var i = 0; i < calcData.length; i++) {
41690 var trace = calcData[i][0].trace;
41691 var url = trace.geojson;
41692
41693 if(typeof url === 'string') {
41694 if(!PlotlyGeoAssets[url]) {
41695 PlotlyGeoAssets[url] = 'pending';
41696 promises.push(fetch(url));
41697 } else if(PlotlyGeoAssets[url] === 'pending') {
41698 promises.push(wait(url));
41699 }
41700 }
41701 }
41702
41703 return promises;
41704}
41705
41706// TODO `turf/bbox` gives wrong result when the input feature/geometry
41707// crosses the anti-meridian. We should try to implement our own bbox logic.
41708function computeBbox(d) {
41709 return turfBbox.default(d);
41710}
41711
41712module.exports = {
41713 locationToFeature: locationToFeature,
41714 feature2polygons: feature2polygons,
41715 getTraceGeojson: getTraceGeojson,
41716 extractTraceFeature: extractTraceFeature,
41717 fetchTraceGeoData: fetchTraceGeoData,
41718 computeBbox: computeBbox
41719};
41720
41721},{"./identity":176,"./is_plain_object":178,"./loggers":181,"./nested_property":185,"./polygon":189,"@turf/area":6,"@turf/bbox":7,"@turf/centroid":8,"country-regex":12,"d3":13}],174:[function(_dereq_,module,exports){
41722/**
41723* Copyright 2012-2020, Plotly, Inc.
41724* All rights reserved.
41725*
41726* This source code is licensed under the MIT license found in the
41727* LICENSE file in the root directory of this source tree.
41728*/
41729
41730
41731'use strict';
41732
41733var BADNUM = _dereq_('../constants/numerical').BADNUM;
41734
41735/**
41736 * Convert calcTrace to GeoJSON 'MultiLineString' coordinate arrays
41737 *
41738 * @param {object} calcTrace
41739 * gd.calcdata item.
41740 * Note that calcTrace[i].lonlat is assumed to be defined
41741 *
41742 * @return {array}
41743 * return line coords array (or array of arrays)
41744 *
41745 */
41746exports.calcTraceToLineCoords = function(calcTrace) {
41747 var trace = calcTrace[0].trace;
41748 var connectgaps = trace.connectgaps;
41749
41750 var coords = [];
41751 var lineString = [];
41752
41753 for(var i = 0; i < calcTrace.length; i++) {
41754 var calcPt = calcTrace[i];
41755 var lonlat = calcPt.lonlat;
41756
41757 if(lonlat[0] !== BADNUM) {
41758 lineString.push(lonlat);
41759 } else if(!connectgaps && lineString.length > 0) {
41760 coords.push(lineString);
41761 lineString = [];
41762 }
41763 }
41764
41765 if(lineString.length > 0) {
41766 coords.push(lineString);
41767 }
41768
41769 return coords;
41770};
41771
41772
41773/**
41774 * Make line ('LineString' or 'MultiLineString') GeoJSON
41775 *
41776 * @param {array} coords
41777 * results form calcTraceToLineCoords
41778 * @return {object} out
41779 * GeoJSON object
41780 *
41781 */
41782exports.makeLine = function(coords) {
41783 if(coords.length === 1) {
41784 return {
41785 type: 'LineString',
41786 coordinates: coords[0]
41787 };
41788 } else {
41789 return {
41790 type: 'MultiLineString',
41791 coordinates: coords
41792 };
41793 }
41794};
41795
41796/**
41797 * Make polygon ('Polygon' or 'MultiPolygon') GeoJSON
41798 *
41799 * @param {array} coords
41800 * results form calcTraceToLineCoords
41801 * @return {object} out
41802 * GeoJSON object
41803 */
41804exports.makePolygon = function(coords) {
41805 if(coords.length === 1) {
41806 return {
41807 type: 'Polygon',
41808 coordinates: coords
41809 };
41810 } else {
41811 var _coords = new Array(coords.length);
41812
41813 for(var i = 0; i < coords.length; i++) {
41814 _coords[i] = [coords[i]];
41815 }
41816
41817 return {
41818 type: 'MultiPolygon',
41819 coordinates: _coords
41820 };
41821 }
41822};
41823
41824/**
41825 * Make blank GeoJSON
41826 *
41827 * @return {object}
41828 * Blank GeoJSON object
41829 *
41830 */
41831exports.makeBlank = function() {
41832 return {
41833 type: 'Point',
41834 coordinates: []
41835 };
41836};
41837
41838},{"../constants/numerical":155}],175:[function(_dereq_,module,exports){
41839/**
41840* Copyright 2012-2020, Plotly, Inc.
41841* All rights reserved.
41842*
41843* This source code is licensed under the MIT license found in the
41844* LICENSE file in the root directory of this source tree.
41845*/
41846
41847'use strict';
41848
41849var mod = _dereq_('./mod').mod;
41850
41851/*
41852 * look for intersection of two line segments
41853 * (1->2 and 3->4) - returns array [x,y] if they do, null if not
41854 */
41855exports.segmentsIntersect = segmentsIntersect;
41856function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
41857 var a = x2 - x1;
41858 var b = x3 - x1;
41859 var c = x4 - x3;
41860 var d = y2 - y1;
41861 var e = y3 - y1;
41862 var f = y4 - y3;
41863 var det = a * f - c * d;
41864 // parallel lines? intersection is undefined
41865 // ignore the case where they are colinear
41866 if(det === 0) return null;
41867 var t = (b * f - c * e) / det;
41868 var u = (b * d - a * e) / det;
41869 // segments do not intersect?
41870 if(u < 0 || u > 1 || t < 0 || t > 1) return null;
41871
41872 return {x: x1 + a * t, y: y1 + d * t};
41873}
41874
41875/*
41876 * find the minimum distance between two line segments (1->2 and 3->4)
41877 */
41878exports.segmentDistance = function segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4) {
41879 if(segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return 0;
41880
41881 // the two segments and their lengths squared
41882 var x12 = x2 - x1;
41883 var y12 = y2 - y1;
41884 var x34 = x4 - x3;
41885 var y34 = y4 - y3;
41886 var ll12 = x12 * x12 + y12 * y12;
41887 var ll34 = x34 * x34 + y34 * y34;
41888
41889 // calculate distance squared, then take the sqrt at the very end
41890 var dist2 = Math.min(
41891 perpDistance2(x12, y12, ll12, x3 - x1, y3 - y1),
41892 perpDistance2(x12, y12, ll12, x4 - x1, y4 - y1),
41893 perpDistance2(x34, y34, ll34, x1 - x3, y1 - y3),
41894 perpDistance2(x34, y34, ll34, x2 - x3, y2 - y3)
41895 );
41896
41897 return Math.sqrt(dist2);
41898};
41899
41900/*
41901 * distance squared from segment ab to point c
41902 * [xab, yab] is the vector b-a
41903 * [xac, yac] is the vector c-a
41904 * llab is the length squared of (b-a), just to simplify calculation
41905 */
41906function perpDistance2(xab, yab, llab, xac, yac) {
41907 var fcAB = (xac * xab + yac * yab);
41908 if(fcAB < 0) {
41909 // point c is closer to point a
41910 return xac * xac + yac * yac;
41911 } else if(fcAB > llab) {
41912 // point c is closer to point b
41913 var xbc = xac - xab;
41914 var ybc = yac - yab;
41915 return xbc * xbc + ybc * ybc;
41916 } else {
41917 // perpendicular distance is the shortest
41918 var crossProduct = xac * yab - yac * xab;
41919 return crossProduct * crossProduct / llab;
41920 }
41921}
41922
41923// a very short-term cache for getTextLocation, just because
41924// we're often looping over the same locations multiple times
41925// invalidated as soon as we look at a different path
41926var locationCache, workingPath, workingTextWidth;
41927
41928// turn a path and position along it into x, y, and angle for the given text
41929exports.getTextLocation = function getTextLocation(path, totalPathLen, positionOnPath, textWidth) {
41930 if(path !== workingPath || textWidth !== workingTextWidth) {
41931 locationCache = {};
41932 workingPath = path;
41933 workingTextWidth = textWidth;
41934 }
41935 if(locationCache[positionOnPath]) {
41936 return locationCache[positionOnPath];
41937 }
41938
41939 // for the angle, use points on the path separated by the text width
41940 // even though due to curvature, the text will cover a bit more than that
41941 var p0 = path.getPointAtLength(mod(positionOnPath - textWidth / 2, totalPathLen));
41942 var p1 = path.getPointAtLength(mod(positionOnPath + textWidth / 2, totalPathLen));
41943 // note: atan handles 1/0 nicely
41944 var theta = Math.atan((p1.y - p0.y) / (p1.x - p0.x));
41945 // center the text at 2/3 of the center position plus 1/3 the p0/p1 midpoint
41946 // that's the average position of this segment, assuming it's roughly quadratic
41947 var pCenter = path.getPointAtLength(mod(positionOnPath, totalPathLen));
41948 var x = (pCenter.x * 4 + p0.x + p1.x) / 6;
41949 var y = (pCenter.y * 4 + p0.y + p1.y) / 6;
41950
41951 var out = {x: x, y: y, theta: theta};
41952 locationCache[positionOnPath] = out;
41953 return out;
41954};
41955
41956exports.clearLocationCache = function() {
41957 workingPath = null;
41958};
41959
41960/*
41961 * Find the segment of `path` that's within the visible area
41962 * given by `bounds` {left, right, top, bottom}, to within a
41963 * precision of `buffer` px
41964 *
41965 * returns: undefined if nothing is visible, else object:
41966 * {
41967 * min: position where the path first enters bounds, or 0 if it
41968 * starts within bounds
41969 * max: position where the path last exits bounds, or the path length
41970 * if it finishes within bounds
41971 * len: max - min, ie the length of visible path
41972 * total: the total path length - just included so the caller doesn't
41973 * need to call path.getTotalLength() again
41974 * isClosed: true iff the start and end points of the path are both visible
41975 * and are at the same point
41976 * }
41977 *
41978 * Works by starting from either end and repeatedly finding the distance from
41979 * that point to the plot area, and if it's outside the plot, moving along the
41980 * path by that distance (because the plot must be at least that far away on
41981 * the path). Note that if a path enters, exits, and re-enters the plot, we
41982 * will not capture this behavior.
41983 */
41984exports.getVisibleSegment = function getVisibleSegment(path, bounds, buffer) {
41985 var left = bounds.left;
41986 var right = bounds.right;
41987 var top = bounds.top;
41988 var bottom = bounds.bottom;
41989
41990 var pMin = 0;
41991 var pTotal = path.getTotalLength();
41992 var pMax = pTotal;
41993
41994 var pt0, ptTotal;
41995
41996 function getDistToPlot(len) {
41997 var pt = path.getPointAtLength(len);
41998
41999 // hold on to the start and end points for `closed`
42000 if(len === 0) pt0 = pt;
42001 else if(len === pTotal) ptTotal = pt;
42002
42003 var dx = (pt.x < left) ? left - pt.x : (pt.x > right ? pt.x - right : 0);
42004 var dy = (pt.y < top) ? top - pt.y : (pt.y > bottom ? pt.y - bottom : 0);
42005 return Math.sqrt(dx * dx + dy * dy);
42006 }
42007
42008 var distToPlot = getDistToPlot(pMin);
42009 while(distToPlot) {
42010 pMin += distToPlot + buffer;
42011 if(pMin > pMax) return;
42012 distToPlot = getDistToPlot(pMin);
42013 }
42014
42015 distToPlot = getDistToPlot(pMax);
42016 while(distToPlot) {
42017 pMax -= distToPlot + buffer;
42018 if(pMin > pMax) return;
42019 distToPlot = getDistToPlot(pMax);
42020 }
42021
42022 return {
42023 min: pMin,
42024 max: pMax,
42025 len: pMax - pMin,
42026 total: pTotal,
42027 isClosed: pMin === 0 && pMax === pTotal &&
42028 Math.abs(pt0.x - ptTotal.x) < 0.1 &&
42029 Math.abs(pt0.y - ptTotal.y) < 0.1
42030 };
42031};
42032
42033/**
42034 * Find point on SVG path corresponding to a given constraint coordinate
42035 *
42036 * @param {SVGPathElement} path
42037 * @param {Number} val : constraint coordinate value
42038 * @param {String} coord : 'x' or 'y' the constraint coordinate
42039 * @param {Object} opts :
42040 * - {Number} pathLength : supply total path length before hand
42041 * - {Number} tolerance
42042 * - {Number} iterationLimit
42043 * @return {SVGPoint}
42044 */
42045exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) {
42046 opts = opts || {};
42047
42048 var pathLength = opts.pathLength || path.getTotalLength();
42049 var tolerance = opts.tolerance || 1e-3;
42050 var iterationLimit = opts.iterationLimit || 30;
42051
42052 // if path starts at a val greater than the path tail (like on vertical violins),
42053 // we must flip the sign of the computed diff.
42054 var mul = path.getPointAtLength(0)[coord] > path.getPointAtLength(pathLength)[coord] ? -1 : 1;
42055
42056 var i = 0;
42057 var b0 = 0;
42058 var b1 = pathLength;
42059 var mid;
42060 var pt;
42061 var diff;
42062
42063 while(i < iterationLimit) {
42064 mid = (b0 + b1) / 2;
42065 pt = path.getPointAtLength(mid);
42066 diff = pt[coord] - val;
42067
42068 if(Math.abs(diff) < tolerance) {
42069 return pt;
42070 } else {
42071 if(mul * diff > 0) {
42072 b1 = mid;
42073 } else {
42074 b0 = mid;
42075 }
42076 i++;
42077 }
42078 }
42079 return pt;
42080};
42081
42082},{"./mod":184}],176:[function(_dereq_,module,exports){
42083/**
42084* Copyright 2012-2020, Plotly, Inc.
42085* All rights reserved.
42086*
42087* This source code is licensed under the MIT license found in the
42088* LICENSE file in the root directory of this source tree.
42089*/
42090
42091'use strict';
42092
42093// Simple helper functions
42094// none of these need any external deps
42095
42096module.exports = function identity(d) { return d; };
42097
42098},{}],177:[function(_dereq_,module,exports){
42099/**
42100* Copyright 2012-2020, Plotly, Inc.
42101* All rights reserved.
42102*
42103* This source code is licensed under the MIT license found in the
42104* LICENSE file in the root directory of this source tree.
42105*/
42106
42107'use strict';
42108
42109var d3 = _dereq_('d3');
42110var isNumeric = _dereq_('fast-isnumeric');
42111
42112var numConstants = _dereq_('../constants/numerical');
42113var FP_SAFE = numConstants.FP_SAFE;
42114var BADNUM = numConstants.BADNUM;
42115
42116var lib = module.exports = {};
42117
42118lib.nestedProperty = _dereq_('./nested_property');
42119lib.keyedContainer = _dereq_('./keyed_container');
42120lib.relativeAttr = _dereq_('./relative_attr');
42121lib.isPlainObject = _dereq_('./is_plain_object');
42122lib.toLogRange = _dereq_('./to_log_range');
42123lib.relinkPrivateKeys = _dereq_('./relink_private');
42124
42125var arrayModule = _dereq_('./array');
42126lib.isTypedArray = arrayModule.isTypedArray;
42127lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray;
42128lib.isArray1D = arrayModule.isArray1D;
42129lib.ensureArray = arrayModule.ensureArray;
42130lib.concat = arrayModule.concat;
42131lib.maxRowLength = arrayModule.maxRowLength;
42132lib.minRowLength = arrayModule.minRowLength;
42133
42134var modModule = _dereq_('./mod');
42135lib.mod = modModule.mod;
42136lib.modHalf = modModule.modHalf;
42137
42138var coerceModule = _dereq_('./coerce');
42139lib.valObjectMeta = coerceModule.valObjectMeta;
42140lib.coerce = coerceModule.coerce;
42141lib.coerce2 = coerceModule.coerce2;
42142lib.coerceFont = coerceModule.coerceFont;
42143lib.coerceHoverinfo = coerceModule.coerceHoverinfo;
42144lib.coerceSelectionMarkerOpacity = coerceModule.coerceSelectionMarkerOpacity;
42145lib.validate = coerceModule.validate;
42146
42147var datesModule = _dereq_('./dates');
42148lib.dateTime2ms = datesModule.dateTime2ms;
42149lib.isDateTime = datesModule.isDateTime;
42150lib.ms2DateTime = datesModule.ms2DateTime;
42151lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal;
42152lib.cleanDate = datesModule.cleanDate;
42153lib.isJSDate = datesModule.isJSDate;
42154lib.formatDate = datesModule.formatDate;
42155lib.incrementMonth = datesModule.incrementMonth;
42156lib.dateTick0 = datesModule.dateTick0;
42157lib.dfltRange = datesModule.dfltRange;
42158lib.findExactDates = datesModule.findExactDates;
42159lib.MIN_MS = datesModule.MIN_MS;
42160lib.MAX_MS = datesModule.MAX_MS;
42161
42162var searchModule = _dereq_('./search');
42163lib.findBin = searchModule.findBin;
42164lib.sorterAsc = searchModule.sorterAsc;
42165lib.sorterDes = searchModule.sorterDes;
42166lib.distinctVals = searchModule.distinctVals;
42167lib.roundUp = searchModule.roundUp;
42168lib.sort = searchModule.sort;
42169lib.findIndexOfMin = searchModule.findIndexOfMin;
42170
42171var statsModule = _dereq_('./stats');
42172lib.aggNums = statsModule.aggNums;
42173lib.len = statsModule.len;
42174lib.mean = statsModule.mean;
42175lib.median = statsModule.median;
42176lib.midRange = statsModule.midRange;
42177lib.variance = statsModule.variance;
42178lib.stdev = statsModule.stdev;
42179lib.interp = statsModule.interp;
42180
42181var matrixModule = _dereq_('./matrix');
42182lib.init2dArray = matrixModule.init2dArray;
42183lib.transposeRagged = matrixModule.transposeRagged;
42184lib.dot = matrixModule.dot;
42185lib.translationMatrix = matrixModule.translationMatrix;
42186lib.rotationMatrix = matrixModule.rotationMatrix;
42187lib.rotationXYMatrix = matrixModule.rotationXYMatrix;
42188lib.apply2DTransform = matrixModule.apply2DTransform;
42189lib.apply2DTransform2 = matrixModule.apply2DTransform2;
42190
42191var anglesModule = _dereq_('./angles');
42192lib.deg2rad = anglesModule.deg2rad;
42193lib.rad2deg = anglesModule.rad2deg;
42194lib.angleDelta = anglesModule.angleDelta;
42195lib.angleDist = anglesModule.angleDist;
42196lib.isFullCircle = anglesModule.isFullCircle;
42197lib.isAngleInsideSector = anglesModule.isAngleInsideSector;
42198lib.isPtInsideSector = anglesModule.isPtInsideSector;
42199lib.pathArc = anglesModule.pathArc;
42200lib.pathSector = anglesModule.pathSector;
42201lib.pathAnnulus = anglesModule.pathAnnulus;
42202
42203var anchorUtils = _dereq_('./anchor_utils');
42204lib.isLeftAnchor = anchorUtils.isLeftAnchor;
42205lib.isCenterAnchor = anchorUtils.isCenterAnchor;
42206lib.isRightAnchor = anchorUtils.isRightAnchor;
42207lib.isTopAnchor = anchorUtils.isTopAnchor;
42208lib.isMiddleAnchor = anchorUtils.isMiddleAnchor;
42209lib.isBottomAnchor = anchorUtils.isBottomAnchor;
42210
42211var geom2dModule = _dereq_('./geometry2d');
42212lib.segmentsIntersect = geom2dModule.segmentsIntersect;
42213lib.segmentDistance = geom2dModule.segmentDistance;
42214lib.getTextLocation = geom2dModule.getTextLocation;
42215lib.clearLocationCache = geom2dModule.clearLocationCache;
42216lib.getVisibleSegment = geom2dModule.getVisibleSegment;
42217lib.findPointOnPath = geom2dModule.findPointOnPath;
42218
42219var extendModule = _dereq_('./extend');
42220lib.extendFlat = extendModule.extendFlat;
42221lib.extendDeep = extendModule.extendDeep;
42222lib.extendDeepAll = extendModule.extendDeepAll;
42223lib.extendDeepNoArrays = extendModule.extendDeepNoArrays;
42224
42225var loggersModule = _dereq_('./loggers');
42226lib.log = loggersModule.log;
42227lib.warn = loggersModule.warn;
42228lib.error = loggersModule.error;
42229
42230var regexModule = _dereq_('./regex');
42231lib.counterRegex = regexModule.counter;
42232
42233var throttleModule = _dereq_('./throttle');
42234lib.throttle = throttleModule.throttle;
42235lib.throttleDone = throttleModule.done;
42236lib.clearThrottle = throttleModule.clear;
42237
42238var domModule = _dereq_('./dom');
42239lib.getGraphDiv = domModule.getGraphDiv;
42240lib.isPlotDiv = domModule.isPlotDiv;
42241lib.removeElement = domModule.removeElement;
42242lib.addStyleRule = domModule.addStyleRule;
42243lib.addRelatedStyleRule = domModule.addRelatedStyleRule;
42244lib.deleteRelatedStyleRule = domModule.deleteRelatedStyleRule;
42245
42246lib.clearResponsive = _dereq_('./clear_responsive');
42247
42248lib.makeTraceGroups = _dereq_('./make_trace_groups');
42249
42250lib._ = _dereq_('./localize');
42251
42252lib.notifier = _dereq_('./notifier');
42253
42254lib.filterUnique = _dereq_('./filter_unique');
42255lib.filterVisible = _dereq_('./filter_visible');
42256lib.pushUnique = _dereq_('./push_unique');
42257
42258lib.cleanNumber = _dereq_('./clean_number');
42259
42260lib.ensureNumber = function ensureNumber(v) {
42261 if(!isNumeric(v)) return BADNUM;
42262 v = Number(v);
42263 if(v < -FP_SAFE || v > FP_SAFE) return BADNUM;
42264 return isNumeric(v) ? Number(v) : BADNUM;
42265};
42266
42267/**
42268 * Is v a valid array index? Accepts numeric strings as well as numbers.
42269 *
42270 * @param {any} v: the value to test
42271 * @param {Optional[integer]} len: the array length we are indexing
42272 *
42273 * @return {bool}: v is a valid array index
42274 */
42275lib.isIndex = function(v, len) {
42276 if(len !== undefined && v >= len) return false;
42277 return isNumeric(v) && (v >= 0) && (v % 1 === 0);
42278};
42279
42280lib.noop = _dereq_('./noop');
42281lib.identity = _dereq_('./identity');
42282
42283/**
42284 * create an array of length 'cnt' filled with 'v' at all indices
42285 *
42286 * @param {any} v
42287 * @param {number} cnt
42288 * @return {array}
42289 */
42290lib.repeat = function(v, cnt) {
42291 var out = new Array(cnt);
42292 for(var i = 0; i < cnt; i++) {
42293 out[i] = v;
42294 }
42295 return out;
42296};
42297
42298/**
42299 * swap x and y of the same attribute in container cont
42300 * specify attr with a ? in place of x/y
42301 * you can also swap other things than x/y by providing part1 and part2
42302 */
42303lib.swapAttrs = function(cont, attrList, part1, part2) {
42304 if(!part1) part1 = 'x';
42305 if(!part2) part2 = 'y';
42306 for(var i = 0; i < attrList.length; i++) {
42307 var attr = attrList[i];
42308 var xp = lib.nestedProperty(cont, attr.replace('?', part1));
42309 var yp = lib.nestedProperty(cont, attr.replace('?', part2));
42310 var temp = xp.get();
42311 xp.set(yp.get());
42312 yp.set(temp);
42313 }
42314};
42315
42316/**
42317 * SVG painter's algo worked around with reinsertion
42318 */
42319lib.raiseToTop = function raiseToTop(elem) {
42320 elem.parentNode.appendChild(elem);
42321};
42322
42323/**
42324 * cancel a possibly pending transition; returned selection may be used by caller
42325 */
42326lib.cancelTransition = function(selection) {
42327 return selection.transition().duration(0);
42328};
42329
42330// constrain - restrict a number v to be between v0 and v1
42331lib.constrain = function(v, v0, v1) {
42332 if(v0 > v1) return Math.max(v1, Math.min(v0, v));
42333 return Math.max(v0, Math.min(v1, v));
42334};
42335
42336/**
42337 * do two bounding boxes from getBoundingClientRect,
42338 * ie {left,right,top,bottom,width,height}, overlap?
42339 * takes optional padding pixels
42340 */
42341lib.bBoxIntersect = function(a, b, pad) {
42342 pad = pad || 0;
42343 return (a.left <= b.right + pad &&
42344 b.left <= a.right + pad &&
42345 a.top <= b.bottom + pad &&
42346 b.top <= a.bottom + pad);
42347};
42348
42349/*
42350 * simpleMap: alternative to Array.map that only
42351 * passes on the element and up to 2 extra args you
42352 * provide (but not the array index or the whole array)
42353 *
42354 * array: the array to map it to
42355 * func: the function to apply
42356 * x1, x2: optional extra args
42357 */
42358lib.simpleMap = function(array, func, x1, x2, opts) {
42359 var len = array.length;
42360 var out = new Array(len);
42361 for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2, opts);
42362 return out;
42363};
42364
42365/**
42366 * Random string generator
42367 *
42368 * @param {object} existing
42369 * pass in strings to avoid as keys with truthy values
42370 * @param {int} bits
42371 * bits of information in the output string, default 24
42372 * @param {int} base
42373 * base of string representation, default 16. Should be a power of 2.
42374 */
42375lib.randstr = function randstr(existing, bits, base, _recursion) {
42376 if(!base) base = 16;
42377 if(bits === undefined) bits = 24;
42378 if(bits <= 0) return '0';
42379
42380 var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
42381 var res = '';
42382 var i, b, x;
42383
42384 for(i = 2; digits === Infinity; i *= 2) {
42385 digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
42386 }
42387
42388 var rem = digits - Math.floor(digits);
42389
42390 for(i = 0; i < Math.floor(digits); i++) {
42391 x = Math.floor(Math.random() * base).toString(base);
42392 res = x + res;
42393 }
42394
42395 if(rem) {
42396 b = Math.pow(base, rem);
42397 x = Math.floor(Math.random() * b).toString(base);
42398 res = x + res;
42399 }
42400
42401 var parsed = parseInt(res, base);
42402 if((existing && existing[res]) ||
42403 (parsed !== Infinity && parsed >= Math.pow(2, bits))) {
42404 if(_recursion > 10) {
42405 lib.warn('randstr failed uniqueness');
42406 return res;
42407 }
42408 return randstr(existing, bits, base, (_recursion || 0) + 1);
42409 } else return res;
42410};
42411
42412lib.OptionControl = function(opt, optname) {
42413 /*
42414 * An environment to contain all option setters and
42415 * getters that collectively modify opts.
42416 *
42417 * You can call up opts from any function in new object
42418 * as this.optname || this.opt
42419 *
42420 * See FitOpts for example of usage
42421 */
42422 if(!opt) opt = {};
42423 if(!optname) optname = 'opt';
42424
42425 var self = {};
42426 self.optionList = [];
42427
42428 self._newoption = function(optObj) {
42429 optObj[optname] = opt;
42430 self[optObj.name] = optObj;
42431 self.optionList.push(optObj);
42432 };
42433
42434 self['_' + optname] = opt;
42435 return self;
42436};
42437
42438/**
42439 * lib.smooth: smooth arrayIn by convolving with
42440 * a hann window with given full width at half max
42441 * bounce the ends in, so the output has the same length as the input
42442 */
42443lib.smooth = function(arrayIn, FWHM) {
42444 FWHM = Math.round(FWHM) || 0; // only makes sense for integers
42445 if(FWHM < 2) return arrayIn;
42446
42447 var alen = arrayIn.length;
42448 var alen2 = 2 * alen;
42449 var wlen = 2 * FWHM - 1;
42450 var w = new Array(wlen);
42451 var arrayOut = new Array(alen);
42452 var i;
42453 var j;
42454 var k;
42455 var v;
42456
42457 // first make the window array
42458 for(i = 0; i < wlen; i++) {
42459 w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM);
42460 }
42461
42462 // now do the convolution
42463 for(i = 0; i < alen; i++) {
42464 v = 0;
42465 for(j = 0; j < wlen; j++) {
42466 k = i + j + 1 - FWHM;
42467
42468 // multibounce
42469 if(k < -alen) k -= alen2 * Math.round(k / alen2);
42470 else if(k >= alen2) k -= alen2 * Math.floor(k / alen2);
42471
42472 // single bounce
42473 if(k < 0) k = - 1 - k;
42474 else if(k >= alen) k = alen2 - 1 - k;
42475
42476 v += arrayIn[k] * w[j];
42477 }
42478 arrayOut[i] = v;
42479 }
42480
42481 return arrayOut;
42482};
42483
42484/**
42485 * syncOrAsync: run a sequence of functions synchronously
42486 * as long as its returns are not promises (ie have no .then)
42487 * includes one argument arg to send to all functions...
42488 * this is mainly just to prevent us having to make wrapper functions
42489 * when the only purpose of the wrapper is to reference gd
42490 * and a final step to be executed at the end
42491 * TODO: if there's an error and everything is sync,
42492 * this doesn't happen yet because we want to make sure
42493 * that it gets reported
42494 */
42495lib.syncOrAsync = function(sequence, arg, finalStep) {
42496 var ret, fni;
42497
42498 function continueAsync() {
42499 return lib.syncOrAsync(sequence, arg, finalStep);
42500 }
42501
42502 while(sequence.length) {
42503 fni = sequence.splice(0, 1)[0];
42504 ret = fni(arg);
42505
42506 if(ret && ret.then) {
42507 return ret.then(continueAsync)
42508 .then(undefined, lib.promiseError);
42509 }
42510 }
42511
42512 return finalStep && finalStep(arg);
42513};
42514
42515
42516/**
42517 * Helper to strip trailing slash, from
42518 * http://stackoverflow.com/questions/6680825/return-string-without-trailing-slash
42519 */
42520lib.stripTrailingSlash = function(str) {
42521 if(str.substr(-1) === '/') return str.substr(0, str.length - 1);
42522 return str;
42523};
42524
42525lib.noneOrAll = function(containerIn, containerOut, attrList) {
42526 /**
42527 * some attributes come together, so if you have one of them
42528 * in the input, you should copy the default values of the others
42529 * to the input as well.
42530 */
42531 if(!containerIn) return;
42532
42533 var hasAny = false;
42534 var hasAll = true;
42535 var i;
42536 var val;
42537
42538 for(i = 0; i < attrList.length; i++) {
42539 val = containerIn[attrList[i]];
42540 if(val !== undefined && val !== null) hasAny = true;
42541 else hasAll = false;
42542 }
42543
42544 if(hasAny && !hasAll) {
42545 for(i = 0; i < attrList.length; i++) {
42546 containerIn[attrList[i]] = containerOut[attrList[i]];
42547 }
42548 }
42549};
42550
42551/** merges calcdata field (given by cdAttr) with traceAttr values
42552 *
42553 * N.B. Loop over minimum of cd.length and traceAttr.length
42554 * i.e. it does not try to fill in beyond traceAttr.length-1
42555 *
42556 * @param {array} traceAttr : trace attribute
42557 * @param {object} cd : calcdata trace
42558 * @param {string} cdAttr : calcdata key
42559 */
42560lib.mergeArray = function(traceAttr, cd, cdAttr, fn) {
42561 var hasFn = typeof fn === 'function';
42562 if(lib.isArrayOrTypedArray(traceAttr)) {
42563 var imax = Math.min(traceAttr.length, cd.length);
42564 for(var i = 0; i < imax; i++) {
42565 var v = traceAttr[i];
42566 cd[i][cdAttr] = hasFn ? fn(v) : v;
42567 }
42568 }
42569};
42570
42571// cast numbers to positive numbers, returns 0 if not greater than 0
42572lib.mergeArrayCastPositive = function(traceAttr, cd, cdAttr) {
42573 return lib.mergeArray(traceAttr, cd, cdAttr, function(v) {
42574 var w = +v;
42575 return !isFinite(w) ? 0 : w > 0 ? w : 0;
42576 });
42577};
42578
42579/** fills calcdata field (given by cdAttr) with traceAttr values
42580 * or function of traceAttr values (e.g. some fallback)
42581 *
42582 * N.B. Loops over all cd items.
42583 *
42584 * @param {array} traceAttr : trace attribute
42585 * @param {object} cd : calcdata trace
42586 * @param {string} cdAttr : calcdata key
42587 * @param {function} [fn] : optional function to apply to each array item
42588 */
42589lib.fillArray = function(traceAttr, cd, cdAttr, fn) {
42590 fn = fn || lib.identity;
42591
42592 if(lib.isArrayOrTypedArray(traceAttr)) {
42593 for(var i = 0; i < cd.length; i++) {
42594 cd[i][cdAttr] = fn(traceAttr[i]);
42595 }
42596 }
42597};
42598
42599/** Handler for trace-wide vs per-point options
42600 *
42601 * @param {object} trace : (full) trace object
42602 * @param {number} ptNumber : index of the point in question
42603 * @param {string} astr : attribute string
42604 * @param {function} [fn] : optional function to apply to each array item
42605 *
42606 * @return {any}
42607 */
42608lib.castOption = function(trace, ptNumber, astr, fn) {
42609 fn = fn || lib.identity;
42610
42611 var val = lib.nestedProperty(trace, astr).get();
42612
42613 if(lib.isArrayOrTypedArray(val)) {
42614 if(Array.isArray(ptNumber) && lib.isArrayOrTypedArray(val[ptNumber[0]])) {
42615 return fn(val[ptNumber[0]][ptNumber[1]]);
42616 } else {
42617 return fn(val[ptNumber]);
42618 }
42619 } else {
42620 return val;
42621 }
42622};
42623
42624/** Extract option from calcdata item, correctly falling back to
42625 * trace value if not found.
42626 *
42627 * @param {object} calcPt : calcdata[i][j] item
42628 * @param {object} trace : (full) trace object
42629 * @param {string} calcKey : calcdata key
42630 * @param {string} traceKey : aka trace attribute string
42631 * @return {any}
42632 */
42633lib.extractOption = function(calcPt, trace, calcKey, traceKey) {
42634 if(calcKey in calcPt) return calcPt[calcKey];
42635
42636 // fallback to trace value,
42637 // must check if value isn't itself an array
42638 // which means the trace attribute has a corresponding
42639 // calcdata key, but its value is falsy
42640 var traceVal = lib.nestedProperty(trace, traceKey).get();
42641 if(!Array.isArray(traceVal)) return traceVal;
42642};
42643
42644function makePtIndex2PtNumber(indexToPoints) {
42645 var ptIndex2ptNumber = {};
42646 for(var k in indexToPoints) {
42647 var pts = indexToPoints[k];
42648 for(var j = 0; j < pts.length; j++) {
42649 ptIndex2ptNumber[pts[j]] = +k;
42650 }
42651 }
42652 return ptIndex2ptNumber;
42653}
42654
42655/** Tag selected calcdata items
42656 *
42657 * N.B. note that point 'index' corresponds to input data array index
42658 * whereas 'number' is its post-transform version.
42659 *
42660 * @param {array} calcTrace
42661 * @param {object} trace
42662 * - selectedpoints {array}
42663 * - _indexToPoints {object}
42664 * @param {ptNumber2cdIndex} ptNumber2cdIndex (optional)
42665 * optional map object for trace types that do not have 1-to-1 point number to
42666 * calcdata item index correspondence (e.g. histogram)
42667 */
42668lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) {
42669 var selectedpoints = trace.selectedpoints;
42670 var indexToPoints = trace._indexToPoints;
42671 var ptIndex2ptNumber;
42672
42673 // make pt index-to-number map object, which takes care of transformed traces
42674 if(indexToPoints) {
42675 ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
42676 }
42677
42678 function isCdIndexValid(v) {
42679 return v !== undefined && v < calcTrace.length;
42680 }
42681
42682 for(var i = 0; i < selectedpoints.length; i++) {
42683 var ptIndex = selectedpoints[i];
42684
42685 if(lib.isIndex(ptIndex) ||
42686 (lib.isArrayOrTypedArray(ptIndex) && lib.isIndex(ptIndex[0]) && lib.isIndex(ptIndex[1]))
42687 ) {
42688 var ptNumber = ptIndex2ptNumber ? ptIndex2ptNumber[ptIndex] : ptIndex;
42689 var cdIndex = ptNumber2cdIndex ? ptNumber2cdIndex[ptNumber] : ptNumber;
42690
42691 if(isCdIndexValid(cdIndex)) {
42692 calcTrace[cdIndex].selected = 1;
42693 }
42694 }
42695 }
42696};
42697
42698lib.selIndices2selPoints = function(trace) {
42699 var selectedpoints = trace.selectedpoints;
42700 var indexToPoints = trace._indexToPoints;
42701
42702 if(indexToPoints) {
42703 var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
42704 var out = [];
42705
42706 for(var i = 0; i < selectedpoints.length; i++) {
42707 var ptIndex = selectedpoints[i];
42708 if(lib.isIndex(ptIndex)) {
42709 var ptNumber = ptIndex2ptNumber[ptIndex];
42710 if(lib.isIndex(ptNumber)) {
42711 out.push(ptNumber);
42712 }
42713 }
42714 }
42715
42716 return out;
42717 } else {
42718 return selectedpoints;
42719 }
42720};
42721
42722/** Returns target as set by 'target' transform attribute
42723 *
42724 * @param {object} trace : full trace object
42725 * @param {object} transformOpts : transform option object
42726 * - target (string} :
42727 * either an attribute string referencing an array in the trace object, or
42728 * a set array.
42729 *
42730 * @return {array or false} : the target array (NOT a copy!!) or false if invalid
42731 */
42732lib.getTargetArray = function(trace, transformOpts) {
42733 var target = transformOpts.target;
42734
42735 if(typeof target === 'string' && target) {
42736 var array = lib.nestedProperty(trace, target).get();
42737 return Array.isArray(array) ? array : false;
42738 } else if(Array.isArray(target)) {
42739 return target;
42740 }
42741
42742 return false;
42743};
42744
42745/**
42746 * modified version of jQuery's extend to strip out private objs and functions,
42747 * and cut arrays down to first <arraylen> or 1 elements
42748 * because extend-like algorithms are hella slow
42749 * obj2 is assumed to already be clean of these things (including no arrays)
42750 */
42751lib.minExtend = function(obj1, obj2) {
42752 var objOut = {};
42753 if(typeof obj2 !== 'object') obj2 = {};
42754 var arrayLen = 3;
42755 var keys = Object.keys(obj1);
42756 var i, k, v;
42757
42758 for(i = 0; i < keys.length; i++) {
42759 k = keys[i];
42760 v = obj1[k];
42761 if(k.charAt(0) === '_' || typeof v === 'function') continue;
42762 else if(k === 'module') objOut[k] = v;
42763 else if(Array.isArray(v)) {
42764 if(k === 'colorscale') {
42765 objOut[k] = v.slice();
42766 } else {
42767 objOut[k] = v.slice(0, arrayLen);
42768 }
42769 } else if(lib.isTypedArray(v)) {
42770 objOut[k] = v.subarray(0, arrayLen);
42771 } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
42772 else objOut[k] = v;
42773 }
42774
42775 keys = Object.keys(obj2);
42776 for(i = 0; i < keys.length; i++) {
42777 k = keys[i];
42778 v = obj2[k];
42779 if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') {
42780 objOut[k] = v;
42781 }
42782 }
42783
42784 return objOut;
42785};
42786
42787lib.titleCase = function(s) {
42788 return s.charAt(0).toUpperCase() + s.substr(1);
42789};
42790
42791lib.containsAny = function(s, fragments) {
42792 for(var i = 0; i < fragments.length; i++) {
42793 if(s.indexOf(fragments[i]) !== -1) return true;
42794 }
42795 return false;
42796};
42797
42798lib.isIE = function() {
42799 return typeof window.navigator.msSaveBlob !== 'undefined';
42800};
42801
42802var IS_IE9_OR_BELOW_REGEX = /MSIE [1-9]\./;
42803lib.isIE9orBelow = function() {
42804 return lib.isIE() && IS_IE9_OR_BELOW_REGEX.test(window.navigator.userAgent);
42805};
42806
42807var IS_SAFARI_REGEX = /Version\/[\d\.]+.*Safari/;
42808lib.isSafari = function() {
42809 return IS_SAFARI_REGEX.test(window.navigator.userAgent);
42810};
42811
42812/**
42813 * Duck typing to recognize a d3 selection, mostly for IE9's benefit
42814 * because it doesn't handle instanceof like modern browsers
42815 */
42816lib.isD3Selection = function(obj) {
42817 return obj && (typeof obj.classed === 'function');
42818};
42819
42820/**
42821 * Append element to DOM only if not present.
42822 *
42823 * @param {d3 selection} parent : parent selection of the element in question
42824 * @param {string} nodeType : node type of element to append
42825 * @param {string} className (optional) : class name of element in question
42826 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
42827 * @return {d3 selection} selection of new layer
42828 *
42829 * Previously, we were using the following pattern:
42830 *
42831 * ```
42832 * var sel = parent.selectAll('.' + className)
42833 * .data([0]);
42834 *
42835 * sel.enter().append(nodeType)
42836 * .classed(className, true);
42837 *
42838 * return sel;
42839 * ```
42840 *
42841 * in numerous places in our codebase to achieve the same behavior.
42842 *
42843 * The logic below performs much better, mostly as we are using
42844 * `.select` instead `.selectAll` that is `querySelector` instead of
42845 * `querySelectorAll`.
42846 *
42847 */
42848lib.ensureSingle = function(parent, nodeType, className, enterFn) {
42849 var sel = parent.select(nodeType + (className ? '.' + className : ''));
42850 if(sel.size()) return sel;
42851
42852 var layer = parent.append(nodeType);
42853 if(className) layer.classed(className, true);
42854 if(enterFn) layer.call(enterFn);
42855
42856 return layer;
42857};
42858
42859/**
42860 * Same as Lib.ensureSingle, but using id as selector.
42861 * This version is mostly used for clipPath nodes.
42862 *
42863 * @param {d3 selection} parent : parent selection of the element in question
42864 * @param {string} nodeType : node type of element to append
42865 * @param {string} id : id of element in question
42866 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
42867 * @return {d3 selection} selection of new layer
42868 */
42869lib.ensureSingleById = function(parent, nodeType, id, enterFn) {
42870 var sel = parent.select(nodeType + '#' + id);
42871 if(sel.size()) return sel;
42872
42873 var layer = parent.append(nodeType).attr('id', id);
42874 if(enterFn) layer.call(enterFn);
42875
42876 return layer;
42877};
42878
42879/**
42880 * Converts a string path to an object.
42881 *
42882 * When given a string containing an array element, it will create a `null`
42883 * filled array of the given size.
42884 *
42885 * @example
42886 * lib.objectFromPath('nested.test[2].path', 'value');
42887 * // returns { nested: { test: [null, null, { path: 'value' }]}
42888 *
42889 * @param {string} path to nested value
42890 * @param {*} any value to be set
42891 *
42892 * @return {Object} the constructed object with a full nested path
42893 */
42894lib.objectFromPath = function(path, value) {
42895 var keys = path.split('.');
42896 var tmpObj;
42897 var obj = tmpObj = {};
42898
42899 for(var i = 0; i < keys.length; i++) {
42900 var key = keys[i];
42901 var el = null;
42902
42903 var parts = keys[i].match(/(.*)\[([0-9]+)\]/);
42904
42905 if(parts) {
42906 key = parts[1];
42907 el = parts[2];
42908
42909 tmpObj = tmpObj[key] = [];
42910
42911 if(i === keys.length - 1) {
42912 tmpObj[el] = value;
42913 } else {
42914 tmpObj[el] = {};
42915 }
42916
42917 tmpObj = tmpObj[el];
42918 } else {
42919 if(i === keys.length - 1) {
42920 tmpObj[key] = value;
42921 } else {
42922 tmpObj[key] = {};
42923 }
42924
42925 tmpObj = tmpObj[key];
42926 }
42927 }
42928
42929 return obj;
42930};
42931
42932/**
42933 * Iterate through an object in-place, converting dotted properties to objects.
42934 *
42935 * Examples:
42936 *
42937 * lib.expandObjectPaths({'nested.test.path': 'value'});
42938 * => { nested: { test: {path: 'value'}}}
42939 *
42940 * It also handles array notation, e.g.:
42941 *
42942 * lib.expandObjectPaths({'foo[1].bar': 'value'});
42943 * => { foo: [null, {bar: value}] }
42944 *
42945 * It handles merges the results when two properties are specified in parallel:
42946 *
42947 * lib.expandObjectPaths({'foo[1].bar': 10, 'foo[0].bar': 20});
42948 * => { foo: [{bar: 10}, {bar: 20}] }
42949 *
42950 * It does NOT, however, merge mulitple mutliply-nested arrays::
42951 *
42952 * lib.expandObjectPaths({'marker[1].range[1]': 5, 'marker[1].range[0]': 4})
42953 * => { marker: [null, {range: 4}] }
42954 */
42955
42956// Store this to avoid recompiling regex on *every* prop since this may happen many
42957// many times for animations. Could maybe be inside the function. Not sure about
42958// scoping vs. recompilation tradeoff, but at least it's not just inlining it into
42959// the inner loop.
42960var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/;
42961var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/;
42962
42963lib.expandObjectPaths = function(data) {
42964 var match, key, prop, datum, idx, dest, trailingPath;
42965 if(typeof data === 'object' && !Array.isArray(data)) {
42966 for(key in data) {
42967 if(data.hasOwnProperty(key)) {
42968 if((match = key.match(dottedPropertyRegex))) {
42969 datum = data[key];
42970 prop = match[1];
42971
42972 delete data[key];
42973
42974 data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[prop]);
42975 } else if((match = key.match(indexedPropertyRegex))) {
42976 datum = data[key];
42977
42978 prop = match[1];
42979 idx = parseInt(match[2]);
42980
42981 delete data[key];
42982
42983 data[prop] = data[prop] || [];
42984
42985 if(match[3] === '.') {
42986 // This is the case where theere are subsequent properties into which
42987 // we must recurse, e.g. transforms[0].value
42988 trailingPath = match[4];
42989 dest = data[prop][idx] = data[prop][idx] || {};
42990
42991 // NB: Extend deep no arrays prevents this from working on multiple
42992 // nested properties in the same object, e.g.
42993 //
42994 // {
42995 // foo[0].bar[1].range
42996 // foo[0].bar[0].range
42997 // }
42998 //
42999 // In this case, the extendDeepNoArrays will overwrite one array with
43000 // the other, so that both properties *will not* be present in the
43001 // result. Fixing this would require a more intelligent tracking
43002 // of changes and merging than extendDeepNoArrays currently accomplishes.
43003 lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum)));
43004 } else {
43005 // This is the case where this property is the end of the line,
43006 // e.g. xaxis.range[0]
43007 data[prop][idx] = lib.expandObjectPaths(datum);
43008 }
43009 } else {
43010 data[key] = lib.expandObjectPaths(data[key]);
43011 }
43012 }
43013 }
43014 }
43015
43016 return data;
43017};
43018
43019/**
43020 * Converts value to string separated by the provided separators.
43021 *
43022 * @example
43023 * lib.numSeparate(2016, '.,');
43024 * // returns '2016'
43025 *
43026 * @example
43027 * lib.numSeparate(3000, '.,', true);
43028 * // returns '3,000'
43029 *
43030 * @example
43031 * lib.numSeparate(1234.56, '|,')
43032 * // returns '1,234|56'
43033 *
43034 * @param {string|number} value the value to be converted
43035 * @param {string} separators string of decimal, then thousands separators
43036 * @param {boolean} separatethousands boolean, 4-digit integers are separated if true
43037 *
43038 * @return {string} the value that has been separated
43039 */
43040lib.numSeparate = function(value, separators, separatethousands) {
43041 if(!separatethousands) separatethousands = false;
43042
43043 if(typeof separators !== 'string' || separators.length === 0) {
43044 throw new Error('Separator string required for formatting!');
43045 }
43046
43047 if(typeof value === 'number') {
43048 value = String(value);
43049 }
43050
43051 var thousandsRe = /(\d+)(\d{3})/;
43052 var decimalSep = separators.charAt(0);
43053 var thouSep = separators.charAt(1);
43054
43055 var x = value.split('.');
43056 var x1 = x[0];
43057 var x2 = x.length > 1 ? decimalSep + x[1] : '';
43058
43059 // Years are ignored for thousands separators
43060 if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) {
43061 while(thousandsRe.test(x1)) {
43062 x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2');
43063 }
43064 }
43065
43066 return x1 + x2;
43067};
43068
43069lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)([:|\|][^}]*)?}/g;
43070var SIMPLE_PROPERTY_REGEX = /^\w*$/;
43071
43072/**
43073 * Substitute values from an object into a string
43074 *
43075 * Examples:
43076 * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
43077 * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
43078 *
43079 * @param {string} input string containing %{...} template strings
43080 * @param {obj} data object containing substitution values
43081 *
43082 * @return {string} templated string
43083 */
43084lib.templateString = function(string, obj) {
43085 // Not all that useful, but cache nestedProperty instantiation
43086 // just in case it speeds things up *slightly*:
43087 var getterCache = {};
43088
43089 return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) {
43090 var v;
43091 if(SIMPLE_PROPERTY_REGEX.test(key)) {
43092 v = obj[key];
43093 } else {
43094 getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
43095 v = getterCache[key]();
43096 }
43097 return lib.isValidTextValue(v) ? v : '';
43098 });
43099};
43100
43101var hovertemplateWarnings = {
43102 max: 10,
43103 count: 0,
43104 name: 'hovertemplate'
43105};
43106lib.hovertemplateString = function() {
43107 return templateFormatString.apply(hovertemplateWarnings, arguments);
43108};
43109
43110var texttemplateWarnings = {
43111 max: 10,
43112 count: 0,
43113 name: 'texttemplate'
43114};
43115lib.texttemplateString = function() {
43116 return templateFormatString.apply(texttemplateWarnings, arguments);
43117};
43118
43119var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/;
43120/**
43121 * Substitute values from an object into a string and optionally formats them using d3-format,
43122 * or fallback to associated labels.
43123 *
43124 * Examples:
43125 * Lib.hovertemplateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
43126 * Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
43127 * Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
43128 *
43129 * @param {string} input string containing %{...:...} template strings
43130 * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
43131 * @param {obj} d3 locale
43132 * @param {obj} data objects containing substitution values
43133 *
43134 * @return {string} templated string
43135 */
43136function templateFormatString(string, labels, d3locale) {
43137 var opts = this;
43138 var args = arguments;
43139 if(!labels) labels = {};
43140 // Not all that useful, but cache nestedProperty instantiation
43141 // just in case it speeds things up *slightly*:
43142 var getterCache = {};
43143
43144 return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, key, format) {
43145 var obj, value, i;
43146 for(i = 3; i < args.length; i++) {
43147 obj = args[i];
43148 if(!obj) continue;
43149 if(obj.hasOwnProperty(key)) {
43150 value = obj[key];
43151 break;
43152 }
43153
43154 if(!SIMPLE_PROPERTY_REGEX.test(key)) {
43155 value = getterCache[key] || lib.nestedProperty(obj, key).get();
43156 if(value) getterCache[key] = value;
43157 }
43158 if(value !== undefined) break;
43159 }
43160
43161 if(value === undefined && opts) {
43162 if(opts.count < opts.max) {
43163 lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!');
43164 value = match;
43165 }
43166
43167 if(opts.count === opts.max) {
43168 lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed');
43169 }
43170 opts.count++;
43171
43172 return match;
43173 }
43174
43175 if(format) {
43176 var fmt;
43177 if(format[0] === ':') {
43178 fmt = d3locale ? d3locale.numberFormat : d3.format;
43179 value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value);
43180 }
43181
43182 if(format[0] === '|') {
43183 fmt = d3locale ? d3locale.timeFormat.utc : d3.time.format.utc;
43184 var ms = lib.dateTime2ms(value);
43185 value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt);
43186 }
43187 } else {
43188 if(labels.hasOwnProperty(key + 'Label')) value = labels[key + 'Label'];
43189 }
43190 return value;
43191 });
43192}
43193
43194/*
43195 * alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc
43196 */
43197var char0 = 48;
43198var char9 = 57;
43199lib.subplotSort = function(a, b) {
43200 var l = Math.min(a.length, b.length) + 1;
43201 var numA = 0;
43202 var numB = 0;
43203 for(var i = 0; i < l; i++) {
43204 var charA = a.charCodeAt(i) || 0;
43205 var charB = b.charCodeAt(i) || 0;
43206 var isNumA = charA >= char0 && charA <= char9;
43207 var isNumB = charB >= char0 && charB <= char9;
43208
43209 if(isNumA) numA = 10 * numA + charA - char0;
43210 if(isNumB) numB = 10 * numB + charB - char0;
43211
43212 if(!isNumA || !isNumB) {
43213 if(numA !== numB) return numA - numB;
43214 if(charA !== charB) return charA - charB;
43215 }
43216 }
43217 return numB - numA;
43218};
43219
43220// repeatable pseudorandom generator
43221var randSeed = 2000000000;
43222
43223lib.seedPseudoRandom = function() {
43224 randSeed = 2000000000;
43225};
43226
43227lib.pseudoRandom = function() {
43228 var lastVal = randSeed;
43229 randSeed = (69069 * randSeed + 1) % 4294967296;
43230 // don't let consecutive vals be too close together
43231 // gets away from really trying to be random, in favor of better local uniformity
43232 if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
43233 return randSeed / 4294967296;
43234};
43235
43236
43237/** Fill hover 'pointData' container with 'correct' hover text value
43238 *
43239 * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
43240 * the text elements will be seen in the hover labels.
43241 *
43242 * - If trace hoverinfo contains a 'text' flag and hovertext is set,
43243 * hovertext takes precedence over text
43244 * i.e. the hoverinfo elements will be seen in the hover labels
43245 *
43246 * @param {object} calcPt
43247 * @param {object} trace
43248 * @param {object || array} contOut (mutated here)
43249 */
43250lib.fillText = function(calcPt, trace, contOut) {
43251 var fill = Array.isArray(contOut) ?
43252 function(v) { contOut.push(v); } :
43253 function(v) { contOut.text = v; };
43254
43255 var htx = lib.extractOption(calcPt, trace, 'htx', 'hovertext');
43256 if(lib.isValidTextValue(htx)) return fill(htx);
43257
43258 var tx = lib.extractOption(calcPt, trace, 'tx', 'text');
43259 if(lib.isValidTextValue(tx)) return fill(tx);
43260};
43261
43262// accept all truthy values and 0 (which gets cast to '0' in the hover labels)
43263lib.isValidTextValue = function(v) {
43264 return v || v === 0;
43265};
43266
43267/**
43268 * @param {number} ratio
43269 * @param {number} n (number of decimal places)
43270 */
43271lib.formatPercent = function(ratio, n) {
43272 n = n || 0;
43273 var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%';
43274 for(var i = 0; i < n; i++) {
43275 if(str.indexOf('.') !== -1) {
43276 str = str.replace('0%', '%');
43277 str = str.replace('.%', '%');
43278 }
43279 }
43280 return str;
43281};
43282
43283lib.isHidden = function(gd) {
43284 var display = window.getComputedStyle(gd).display;
43285 return !display || display === 'none';
43286};
43287
43288/** Return transform text for bar bar-like rectangles and pie-like slices
43289 * @param {object} transform
43290 * - targetX: desired position on the x-axis
43291 * - targetY: desired position on the y-axis
43292 * - textX: text middle position on the x-axis
43293 * - textY: text middle position on the y-axis
43294 * - anchorX: (optional) text anchor position on the x-axis (computed from textX), zero for middle anchor
43295 * - anchorY: (optional) text anchor position on the y-axis (computed from textY), zero for middle anchor
43296 * - scale: (optional) scale applied after translate
43297 * - rotate: (optional) rotation applied after scale
43298 * - noCenter: when defined no extra arguments needed in rotation
43299 */
43300lib.getTextTransform = function(transform) {
43301 var noCenter = transform.noCenter;
43302 var textX = transform.textX;
43303 var textY = transform.textY;
43304 var targetX = transform.targetX;
43305 var targetY = transform.targetY;
43306 var anchorX = transform.anchorX || 0;
43307 var anchorY = transform.anchorY || 0;
43308 var rotate = transform.rotate;
43309 var scale = transform.scale;
43310 if(!scale) scale = 0;
43311 else if(scale > 1) scale = 1;
43312
43313 return (
43314 'translate(' +
43315 (targetX - scale * (textX + anchorX)) + ',' +
43316 (targetY - scale * (textY + anchorY)) +
43317 ')' +
43318 (scale < 1 ?
43319 'scale(' + scale + ')' : ''
43320 ) +
43321 (rotate ?
43322 'rotate(' + rotate +
43323 (noCenter ? '' : ' ' + textX + ' ' + textY) +
43324 ')' : ''
43325 )
43326 );
43327};
43328
43329lib.ensureUniformFontSize = function(gd, baseFont) {
43330 var out = lib.extendFlat({}, baseFont);
43331 out.size = Math.max(
43332 baseFont.size,
43333 gd._fullLayout.uniformtext.minsize || 0
43334 );
43335 return out;
43336};
43337
43338},{"../constants/numerical":155,"./anchor_utils":160,"./angles":161,"./array":162,"./clean_number":163,"./clear_responsive":165,"./coerce":166,"./dates":167,"./dom":168,"./extend":170,"./filter_unique":171,"./filter_visible":172,"./geometry2d":175,"./identity":176,"./is_plain_object":178,"./keyed_container":179,"./localize":180,"./loggers":181,"./make_trace_groups":182,"./matrix":183,"./mod":184,"./nested_property":185,"./noop":186,"./notifier":187,"./push_unique":190,"./regex":192,"./relative_attr":193,"./relink_private":194,"./search":195,"./stats":197,"./throttle":199,"./to_log_range":200,"d3":13,"fast-isnumeric":15}],178:[function(_dereq_,module,exports){
43339/**
43340* Copyright 2012-2020, Plotly, Inc.
43341* All rights reserved.
43342*
43343* This source code is licensed under the MIT license found in the
43344* LICENSE file in the root directory of this source tree.
43345*/
43346
43347
43348'use strict';
43349
43350// more info: http://stackoverflow.com/questions/18531624/isplainobject-thing
43351module.exports = function isPlainObject(obj) {
43352 // We need to be a little less strict in the `imagetest` container because
43353 // of how async image requests are handled.
43354 //
43355 // N.B. isPlainObject(new Constructor()) will return true in `imagetest`
43356 if(window && window.process && window.process.versions) {
43357 return Object.prototype.toString.call(obj) === '[object Object]';
43358 }
43359
43360 return (
43361 Object.prototype.toString.call(obj) === '[object Object]' &&
43362 Object.getPrototypeOf(obj) === Object.prototype
43363 );
43364};
43365
43366},{}],179:[function(_dereq_,module,exports){
43367/**
43368* Copyright 2012-2020, Plotly, Inc.
43369* All rights reserved.
43370*
43371* This source code is licensed under the MIT license found in the
43372* LICENSE file in the root directory of this source tree.
43373*/
43374
43375'use strict';
43376
43377var nestedProperty = _dereq_('./nested_property');
43378
43379var SIMPLE_PROPERTY_REGEX = /^\w*$/;
43380
43381// bitmask for deciding what's updated. Sometimes the name needs to be updated,
43382// sometimes the value needs to be updated, and sometimes both do. This is just
43383// a simple way to track what's updated such that it's a simple OR operation to
43384// assimilate new updates.
43385//
43386// The only exception is the UNSET bit that tracks when we need to explicitly
43387// unset and remove the property. This concrn arises because of the special
43388// way in which nestedProperty handles null/undefined. When you specify `null`,
43389// it prunes any unused items in the tree. I ran into some issues with it getting
43390// null vs undefined confused, so UNSET is just a bit that forces the property
43391// update to send `null`, removing the property explicitly rather than setting
43392// it to undefined.
43393var NONE = 0;
43394var NAME = 1;
43395var VALUE = 2;
43396var BOTH = 3;
43397var UNSET = 4;
43398
43399module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
43400 keyName = keyName || 'name';
43401 valueName = valueName || 'value';
43402 var i, arr, baseProp;
43403 var changeTypes = {};
43404
43405 if(path && path.length) {
43406 baseProp = nestedProperty(baseObj, path);
43407 arr = baseProp.get();
43408 } else {
43409 arr = baseObj;
43410 }
43411
43412 path = path || '';
43413
43414 // Construct an index:
43415 var indexLookup = {};
43416 if(arr) {
43417 for(i = 0; i < arr.length; i++) {
43418 indexLookup[arr[i][keyName]] = i;
43419 }
43420 }
43421
43422 var isSimpleValueProp = SIMPLE_PROPERTY_REGEX.test(valueName);
43423
43424 var obj = {
43425 set: function(name, value) {
43426 var changeType = value === null ? UNSET : NONE;
43427
43428 // create the base array if necessary
43429 if(!arr) {
43430 if(!baseProp || changeType === UNSET) return;
43431
43432 arr = [];
43433 baseProp.set(arr);
43434 }
43435
43436 var idx = indexLookup[name];
43437 if(idx === undefined) {
43438 if(changeType === UNSET) return;
43439
43440 changeType = changeType | BOTH;
43441 idx = arr.length;
43442 indexLookup[name] = idx;
43443 } else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) {
43444 changeType = changeType | VALUE;
43445 }
43446
43447 var newValue = arr[idx] = arr[idx] || {};
43448 newValue[keyName] = name;
43449
43450 if(isSimpleValueProp) {
43451 newValue[valueName] = value;
43452 } else {
43453 nestedProperty(newValue, valueName).set(value);
43454 }
43455
43456 // If it's not an unset, force that bit to be unset. This is all related to the fact
43457 // that undefined and null are a bit specially implemented in nestedProperties.
43458 if(value !== null) {
43459 changeType = changeType & ~UNSET;
43460 }
43461
43462 changeTypes[idx] = changeTypes[idx] | changeType;
43463
43464 return obj;
43465 },
43466 get: function(name) {
43467 if(!arr) return;
43468
43469 var idx = indexLookup[name];
43470
43471 if(idx === undefined) {
43472 return undefined;
43473 } else if(isSimpleValueProp) {
43474 return arr[idx][valueName];
43475 } else {
43476 return nestedProperty(arr[idx], valueName).get();
43477 }
43478 },
43479 rename: function(name, newName) {
43480 var idx = indexLookup[name];
43481
43482 if(idx === undefined) return obj;
43483 changeTypes[idx] = changeTypes[idx] | NAME;
43484
43485 indexLookup[newName] = idx;
43486 delete indexLookup[name];
43487
43488 arr[idx][keyName] = newName;
43489
43490 return obj;
43491 },
43492 remove: function(name) {
43493 var idx = indexLookup[name];
43494
43495 if(idx === undefined) return obj;
43496
43497 var object = arr[idx];
43498 if(Object.keys(object).length > 2) {
43499 // This object contains more than just the key/value, so unset
43500 // the value without modifying the entry otherwise:
43501 changeTypes[idx] = changeTypes[idx] | VALUE;
43502 return obj.set(name, null);
43503 }
43504
43505 if(isSimpleValueProp) {
43506 for(i = idx; i < arr.length; i++) {
43507 changeTypes[i] = changeTypes[i] | BOTH;
43508 }
43509 for(i = idx; i < arr.length; i++) {
43510 indexLookup[arr[i][keyName]]--;
43511 }
43512 arr.splice(idx, 1);
43513 delete(indexLookup[name]);
43514 } else {
43515 // Perform this update *strictly* so we can check whether the result's
43516 // been pruned. If so, it's a removal. If not, it's a value unset only.
43517 nestedProperty(object, valueName).set(null);
43518
43519 // Now check if the top level nested property has any keys left. If so,
43520 // the object still has values so we only want to unset the key. If not,
43521 // the entire object can be removed since there's no other data.
43522 // var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []);
43523
43524 changeTypes[idx] = changeTypes[idx] | VALUE | UNSET;
43525 }
43526
43527 return obj;
43528 },
43529 constructUpdate: function() {
43530 var astr, idx;
43531 var update = {};
43532 var changed = Object.keys(changeTypes);
43533 for(var i = 0; i < changed.length; i++) {
43534 idx = changed[i];
43535 astr = path + '[' + idx + ']';
43536 if(arr[idx]) {
43537 if(changeTypes[idx] & NAME) {
43538 update[astr + '.' + keyName] = arr[idx][keyName];
43539 }
43540 if(changeTypes[idx] & VALUE) {
43541 if(isSimpleValueProp) {
43542 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName];
43543 } else {
43544 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get();
43545 }
43546 }
43547 } else {
43548 update[astr] = null;
43549 }
43550 }
43551
43552 return update;
43553 }
43554 };
43555
43556 return obj;
43557};
43558
43559},{"./nested_property":185}],180:[function(_dereq_,module,exports){
43560/**
43561* Copyright 2012-2020, Plotly, Inc.
43562* All rights reserved.
43563*
43564* This source code is licensed under the MIT license found in the
43565* LICENSE file in the root directory of this source tree.
43566*/
43567
43568
43569'use strict';
43570
43571var Registry = _dereq_('../registry');
43572
43573/**
43574 * localize: translate a string for the current locale
43575 *
43576 * @param {object} gd: the graphDiv for context
43577 * gd._context.locale determines the language (& optional region/country)
43578 * the dictionary for each locale may either be supplied in
43579 * gd._context.locales or globally via Plotly.register
43580 * @param {string} s: the string to translate
43581 */
43582module.exports = function localize(gd, s) {
43583 var locale = gd._context.locale;
43584
43585 /*
43586 * Priority of lookup:
43587 * contextDicts[locale],
43588 * registeredDicts[locale],
43589 * contextDicts[baseLocale], (if baseLocale is distinct)
43590 * registeredDicts[baseLocale]
43591 * Return the first translation we find.
43592 * This way if you have a regionalization you are allowed to specify
43593 * only what's different from the base locale, everything else will
43594 * fall back on the base.
43595 */
43596 for(var i = 0; i < 2; i++) {
43597 var locales = gd._context.locales;
43598 for(var j = 0; j < 2; j++) {
43599 var dict = (locales[locale] || {}).dictionary;
43600 if(dict) {
43601 var out = dict[s];
43602 if(out) return out;
43603 }
43604 locales = Registry.localeRegistry;
43605 }
43606
43607 var baseLocale = locale.split('-')[0];
43608 if(baseLocale === locale) break;
43609 locale = baseLocale;
43610 }
43611
43612 return s;
43613};
43614
43615},{"../registry":272}],181:[function(_dereq_,module,exports){
43616/**
43617* Copyright 2012-2020, Plotly, Inc.
43618* All rights reserved.
43619*
43620* This source code is licensed under the MIT license found in the
43621* LICENSE file in the root directory of this source tree.
43622*/
43623
43624'use strict';
43625
43626/* eslint-disable no-console */
43627
43628var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
43629
43630var notifier = _dereq_('./notifier');
43631
43632var loggers = module.exports = {};
43633
43634/**
43635 * ------------------------------------------
43636 * debugging tools
43637 * ------------------------------------------
43638 */
43639
43640loggers.log = function() {
43641 var i;
43642
43643 if(dfltConfig.logging > 1) {
43644 var messages = ['LOG:'];
43645 for(i = 0; i < arguments.length; i++) {
43646 messages.push(arguments[i]);
43647 }
43648 apply(console.trace || console.log, messages);
43649 }
43650
43651 if(dfltConfig.notifyOnLogging > 1) {
43652 var lines = [];
43653 for(i = 0; i < arguments.length; i++) {
43654 lines.push(arguments[i]);
43655 }
43656 notifier(lines.join('<br>'), 'long');
43657 }
43658};
43659
43660loggers.warn = function() {
43661 var i;
43662
43663 if(dfltConfig.logging > 0) {
43664 var messages = ['WARN:'];
43665 for(i = 0; i < arguments.length; i++) {
43666 messages.push(arguments[i]);
43667 }
43668 apply(console.trace || console.log, messages);
43669 }
43670
43671 if(dfltConfig.notifyOnLogging > 0) {
43672 var lines = [];
43673 for(i = 0; i < arguments.length; i++) {
43674 lines.push(arguments[i]);
43675 }
43676 notifier(lines.join('<br>'), 'stick');
43677 }
43678};
43679
43680loggers.error = function() {
43681 var i;
43682
43683 if(dfltConfig.logging > 0) {
43684 var messages = ['ERROR:'];
43685 for(i = 0; i < arguments.length; i++) {
43686 messages.push(arguments[i]);
43687 }
43688 apply(console.error, messages);
43689 }
43690
43691 if(dfltConfig.notifyOnLogging > 0) {
43692 var lines = [];
43693 for(i = 0; i < arguments.length; i++) {
43694 lines.push(arguments[i]);
43695 }
43696 notifier(lines.join('<br>'), 'stick');
43697 }
43698};
43699
43700/*
43701 * Robust apply, for IE9 where console.log doesn't support
43702 * apply like other functions do
43703 */
43704function apply(f, args) {
43705 if(f && f.apply) {
43706 try {
43707 // `this` should always be console, since here we're always
43708 // applying a method of the console object.
43709 f.apply(console, args);
43710 return;
43711 } catch(e) { /* in case apply failed, fall back on the code below */ }
43712 }
43713
43714 // no apply - just try calling the function on each arg independently
43715 for(var i = 0; i < args.length; i++) {
43716 try {
43717 f(args[i]);
43718 } catch(e) {
43719 // still fails - last resort simple console.log
43720 console.log(args[i]);
43721 }
43722 }
43723}
43724
43725},{"../plot_api/plot_config":210,"./notifier":187}],182:[function(_dereq_,module,exports){
43726/**
43727* Copyright 2012-2020, Plotly, Inc.
43728* All rights reserved.
43729*
43730* This source code is licensed under the MIT license found in the
43731* LICENSE file in the root directory of this source tree.
43732*/
43733
43734'use strict';
43735
43736var d3 = _dereq_('d3');
43737
43738/**
43739 * General helper to manage trace groups based on calcdata
43740 *
43741 * @param {d3.selection} traceLayer: a selection containing a single group
43742 * to draw these traces into
43743 * @param {array} cdModule: array of calcdata items for this
43744 * module and subplot combination. Assumes the calcdata item for each
43745 * trace is an array with the fullData trace attached to the first item.
43746 * @param {string} cls: the class attribute to give each trace group
43747 * so you can give multiple classes separated by spaces
43748 */
43749module.exports = function makeTraceGroups(traceLayer, cdModule, cls) {
43750 var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.'))
43751 .data(cdModule, function(cd) { return cd[0].trace.uid; });
43752
43753 traces.exit().remove();
43754
43755 traces.enter().append('g')
43756 .attr('class', cls);
43757
43758 traces.order();
43759
43760 // stash ref node to trace group in calcdata,
43761 // useful for (fast) styleOnSelect
43762 var k = traceLayer.classed('rangeplot') ? 'nodeRangePlot3' : 'node3';
43763 traces.each(function(cd) { cd[0][k] = d3.select(this); });
43764
43765 return traces;
43766};
43767
43768},{"d3":13}],183:[function(_dereq_,module,exports){
43769/**
43770* Copyright 2012-2020, Plotly, Inc.
43771* All rights reserved.
43772*
43773* This source code is licensed under the MIT license found in the
43774* LICENSE file in the root directory of this source tree.
43775*/
43776
43777
43778'use strict';
43779
43780
43781exports.init2dArray = function(rowLength, colLength) {
43782 var array = new Array(rowLength);
43783 for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
43784 return array;
43785};
43786
43787/**
43788 * transpose a (possibly ragged) 2d array z. inspired by
43789 * http://stackoverflow.com/questions/17428587/
43790 * transposing-a-2d-array-in-javascript
43791 */
43792exports.transposeRagged = function(z) {
43793 var maxlen = 0;
43794 var zlen = z.length;
43795 var i, j;
43796 // Maximum row length:
43797 for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
43798
43799 var t = new Array(maxlen);
43800 for(i = 0; i < maxlen; i++) {
43801 t[i] = new Array(zlen);
43802 for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
43803 }
43804
43805 return t;
43806};
43807
43808// our own dot function so that we don't need to include numeric
43809exports.dot = function(x, y) {
43810 if(!(x.length && y.length) || x.length !== y.length) return null;
43811
43812 var len = x.length;
43813 var out;
43814 var i;
43815
43816 if(x[0].length) {
43817 // mat-vec or mat-mat
43818 out = new Array(len);
43819 for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
43820 } else if(y[0].length) {
43821 // vec-mat
43822 var yTranspose = exports.transposeRagged(y);
43823 out = new Array(yTranspose.length);
43824 for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
43825 } else {
43826 // vec-vec
43827 out = 0;
43828 for(i = 0; i < len; i++) out += x[i] * y[i];
43829 }
43830
43831 return out;
43832};
43833
43834// translate by (x,y)
43835exports.translationMatrix = function(x, y) {
43836 return [[1, 0, x], [0, 1, y], [0, 0, 1]];
43837};
43838
43839// rotate by alpha around (0,0)
43840exports.rotationMatrix = function(alpha) {
43841 var a = alpha * Math.PI / 180;
43842 return [[Math.cos(a), -Math.sin(a), 0],
43843 [Math.sin(a), Math.cos(a), 0],
43844 [0, 0, 1]];
43845};
43846
43847// rotate by alpha around (x,y)
43848exports.rotationXYMatrix = function(a, x, y) {
43849 return exports.dot(
43850 exports.dot(exports.translationMatrix(x, y),
43851 exports.rotationMatrix(a)),
43852 exports.translationMatrix(-x, -y));
43853};
43854
43855// applies a 2D transformation matrix to either x and y params or an [x,y] array
43856exports.apply2DTransform = function(transform) {
43857 return function() {
43858 var args = arguments;
43859 if(args.length === 3) {
43860 args = args[0];
43861 }// from map
43862 var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
43863 return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
43864 };
43865};
43866
43867// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
43868exports.apply2DTransform2 = function(transform) {
43869 var at = exports.apply2DTransform(transform);
43870 return function(xys) {
43871 return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
43872 };
43873};
43874
43875},{}],184:[function(_dereq_,module,exports){
43876/**
43877* Copyright 2012-2020, Plotly, Inc.
43878* All rights reserved.
43879*
43880* This source code is licensed under the MIT license found in the
43881* LICENSE file in the root directory of this source tree.
43882*/
43883
43884'use strict';
43885
43886/**
43887 * sanitized modulus function that always returns in the range [0, d)
43888 * rather than (-d, 0] if v is negative
43889 */
43890function mod(v, d) {
43891 var out = v % d;
43892 return out < 0 ? out + d : out;
43893}
43894
43895/**
43896 * sanitized modulus function that always returns in the range [-d/2, d/2]
43897 * rather than (-d, 0] if v is negative
43898 */
43899function modHalf(v, d) {
43900 return Math.abs(v) > (d / 2) ?
43901 v - Math.round(v / d) * d :
43902 v;
43903}
43904
43905module.exports = {
43906 mod: mod,
43907 modHalf: modHalf
43908};
43909
43910},{}],185:[function(_dereq_,module,exports){
43911/**
43912* Copyright 2012-2020, Plotly, Inc.
43913* All rights reserved.
43914*
43915* This source code is licensed under the MIT license found in the
43916* LICENSE file in the root directory of this source tree.
43917*/
43918
43919
43920'use strict';
43921
43922var isNumeric = _dereq_('fast-isnumeric');
43923var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
43924
43925/**
43926 * convert a string s (such as 'xaxis.range[0]')
43927 * representing a property of nested object into set and get methods
43928 * also return the string and object so we don't have to keep track of them
43929 * allows [-1] for an array index, to set a property inside all elements
43930 * of an array
43931 * eg if obj = {arr: [{a: 1}, {a: 2}]}
43932 * you can do p = nestedProperty(obj, 'arr[-1].a')
43933 * but you cannot set the array itself this way, to do that
43934 * just set the whole array.
43935 * eg if obj = {arr: [1, 2, 3]}
43936 * you can't do nestedProperty(obj, 'arr[-1]').set(5)
43937 * but you can do nestedProperty(obj, 'arr').set([5, 5, 5])
43938 */
43939module.exports = function nestedProperty(container, propStr) {
43940 if(isNumeric(propStr)) propStr = String(propStr);
43941 else if(typeof propStr !== 'string' ||
43942 propStr.substr(propStr.length - 4) === '[-1]') {
43943 throw 'bad property string';
43944 }
43945
43946 var j = 0;
43947 var propParts = propStr.split('.');
43948 var indexed;
43949 var indices;
43950 var i;
43951
43952 // check for parts of the nesting hierarchy that are numbers (ie array elements)
43953 while(j < propParts.length) {
43954 // look for non-bracket chars, then any number of [##] blocks
43955 indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
43956 if(indexed) {
43957 if(indexed[1]) propParts[j] = indexed[1];
43958 // allow propStr to start with bracketed array indices
43959 else if(j === 0) propParts.splice(0, 1);
43960 else throw 'bad property string';
43961
43962 indices = indexed[2]
43963 .substr(1, indexed[2].length - 2)
43964 .split('][');
43965
43966 for(i = 0; i < indices.length; i++) {
43967 j++;
43968 propParts.splice(j, 0, Number(indices[i]));
43969 }
43970 }
43971 j++;
43972 }
43973
43974 if(typeof container !== 'object') {
43975 return badContainer(container, propStr, propParts);
43976 }
43977
43978 return {
43979 set: npSet(container, propParts, propStr),
43980 get: npGet(container, propParts),
43981 astr: propStr,
43982 parts: propParts,
43983 obj: container
43984 };
43985};
43986
43987function npGet(cont, parts) {
43988 return function() {
43989 var curCont = cont;
43990 var curPart;
43991 var allSame;
43992 var out;
43993 var i;
43994 var j;
43995
43996 for(i = 0; i < parts.length - 1; i++) {
43997 curPart = parts[i];
43998 if(curPart === -1) {
43999 allSame = true;
44000 out = [];
44001 for(j = 0; j < curCont.length; j++) {
44002 out[j] = npGet(curCont[j], parts.slice(i + 1))();
44003 if(out[j] !== out[0]) allSame = false;
44004 }
44005 return allSame ? out[0] : out;
44006 }
44007 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
44008 return undefined;
44009 }
44010 curCont = curCont[curPart];
44011 if(typeof curCont !== 'object' || curCont === null) {
44012 return undefined;
44013 }
44014 }
44015
44016 // only hit this if parts.length === 1
44017 if(typeof curCont !== 'object' || curCont === null) return undefined;
44018
44019 out = curCont[parts[i]];
44020 if(out === null) return undefined;
44021 return out;
44022 };
44023}
44024
44025/*
44026 * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an
44027 * *args* array.
44028 *
44029 * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset
44030 * a net noop; but this causes far more complication than it's worth, and still had
44031 * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410
44032 *
44033 * *args* arrays get passed directly to API methods and we should respect null if
44034 * the user put it there, but otherwise null is deleted as we use it as code
44035 * in restyle/relayout/update for "delete this value" whereas undefined means
44036 * "ignore this edit"
44037 */
44038var ARGS_PATTERN = /(^|\.)args\[/;
44039function isDeletable(val, propStr) {
44040 return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN));
44041}
44042
44043function npSet(cont, parts, propStr) {
44044 return function(val) {
44045 var curCont = cont;
44046 var propPart = '';
44047 var containerLevels = [[cont, propPart]];
44048 var toDelete = isDeletable(val, propStr);
44049 var curPart;
44050 var i;
44051
44052 for(i = 0; i < parts.length - 1; i++) {
44053 curPart = parts[i];
44054
44055 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
44056 throw 'array index but container is not an array';
44057 }
44058
44059 // handle special -1 array index
44060 if(curPart === -1) {
44061 toDelete = !setArrayAll(curCont, parts.slice(i + 1), val, propStr);
44062 if(toDelete) break;
44063 else return;
44064 }
44065
44066 if(!checkNewContainer(curCont, curPart, parts[i + 1], toDelete)) {
44067 break;
44068 }
44069
44070 curCont = curCont[curPart];
44071
44072 if(typeof curCont !== 'object' || curCont === null) {
44073 throw 'container is not an object';
44074 }
44075
44076 propPart = joinPropStr(propPart, curPart);
44077
44078 containerLevels.push([curCont, propPart]);
44079 }
44080
44081 if(toDelete) {
44082 if(i === parts.length - 1) {
44083 delete curCont[parts[i]];
44084
44085 // The one bit of pruning we still do: drop `undefined` from the end of arrays.
44086 // In case someone has already unset previous items, continue until we hit a
44087 // non-undefined value.
44088 if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) {
44089 while(curCont.length && curCont[curCont.length - 1] === undefined) {
44090 curCont.pop();
44091 }
44092 }
44093 }
44094 } else curCont[parts[i]] = val;
44095 };
44096}
44097
44098function joinPropStr(propStr, newPart) {
44099 var toAdd = newPart;
44100 if(isNumeric(newPart)) toAdd = '[' + newPart + ']';
44101 else if(propStr) toAdd = '.' + newPart;
44102
44103 return propStr + toAdd;
44104}
44105
44106// handle special -1 array index
44107function setArrayAll(containerArray, innerParts, val, propStr) {
44108 var arrayVal = isArrayOrTypedArray(val);
44109 var allSet = true;
44110 var thisVal = val;
44111 var thisPropStr = propStr.replace('-1', 0);
44112 var deleteThis = arrayVal ? false : isDeletable(val, thisPropStr);
44113 var firstPart = innerParts[0];
44114 var i;
44115
44116 for(i = 0; i < containerArray.length; i++) {
44117 thisPropStr = propStr.replace('-1', i);
44118 if(arrayVal) {
44119 thisVal = val[i % val.length];
44120 deleteThis = isDeletable(thisVal, thisPropStr);
44121 }
44122 if(deleteThis) allSet = false;
44123 if(!checkNewContainer(containerArray, i, firstPart, deleteThis)) {
44124 continue;
44125 }
44126 npSet(containerArray[i], innerParts, propStr.replace('-1', i))(thisVal);
44127 }
44128 return allSet;
44129}
44130
44131/**
44132 * make new sub-container as needed.
44133 * returns false if there's no container and none is needed
44134 * because we're only deleting an attribute
44135 */
44136function checkNewContainer(container, part, nextPart, toDelete) {
44137 if(container[part] === undefined) {
44138 if(toDelete) return false;
44139
44140 if(typeof nextPart === 'number') container[part] = [];
44141 else container[part] = {};
44142 }
44143 return true;
44144}
44145
44146function badContainer(container, propStr, propParts) {
44147 return {
44148 set: function() { throw 'bad container'; },
44149 get: function() {},
44150 astr: propStr,
44151 parts: propParts,
44152 obj: container
44153 };
44154}
44155
44156},{"./array":162,"fast-isnumeric":15}],186:[function(_dereq_,module,exports){
44157/**
44158* Copyright 2012-2020, Plotly, Inc.
44159* All rights reserved.
44160*
44161* This source code is licensed under the MIT license found in the
44162* LICENSE file in the root directory of this source tree.
44163*/
44164
44165'use strict';
44166
44167// Simple helper functions
44168// none of these need any external deps
44169
44170module.exports = function noop() {};
44171
44172},{}],187:[function(_dereq_,module,exports){
44173/**
44174* Copyright 2012-2020, Plotly, Inc.
44175* All rights reserved.
44176*
44177* This source code is licensed under the MIT license found in the
44178* LICENSE file in the root directory of this source tree.
44179*/
44180
44181
44182'use strict';
44183
44184var d3 = _dereq_('d3');
44185var isNumeric = _dereq_('fast-isnumeric');
44186
44187var NOTEDATA = [];
44188
44189/**
44190 * notifier
44191 * @param {String} text The person's user name
44192 * @param {Number} [delay=1000] The delay time in milliseconds
44193 * or 'long' which provides 2000 ms delay time.
44194 * @return {undefined} this function does not return a value
44195 */
44196module.exports = function(text, displayLength) {
44197 if(NOTEDATA.indexOf(text) !== -1) return;
44198
44199 NOTEDATA.push(text);
44200
44201 var ts = 1000;
44202 if(isNumeric(displayLength)) ts = displayLength;
44203 else if(displayLength === 'long') ts = 3000;
44204
44205 var notifierContainer = d3.select('body')
44206 .selectAll('.plotly-notifier')
44207 .data([0]);
44208 notifierContainer.enter()
44209 .append('div')
44210 .classed('plotly-notifier', true);
44211
44212 var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
44213
44214 function killNote(transition) {
44215 transition
44216 .duration(700)
44217 .style('opacity', 0)
44218 .each('end', function(thisText) {
44219 var thisIndex = NOTEDATA.indexOf(thisText);
44220 if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
44221 d3.select(this).remove();
44222 });
44223 }
44224
44225 notes.enter().append('div')
44226 .classed('notifier-note', true)
44227 .style('opacity', 0)
44228 .each(function(thisText) {
44229 var note = d3.select(this);
44230
44231 note.append('button')
44232 .classed('notifier-close', true)
44233 .html('&times;')
44234 .on('click', function() {
44235 note.transition().call(killNote);
44236 });
44237
44238 var p = note.append('p');
44239 var lines = thisText.split(/<br\s*\/?>/g);
44240 for(var i = 0; i < lines.length; i++) {
44241 if(i) p.append('br');
44242 p.append('span').text(lines[i]);
44243 }
44244
44245 if(displayLength === 'stick') {
44246 note.transition()
44247 .duration(350)
44248 .style('opacity', 1);
44249 } else {
44250 note.transition()
44251 .duration(700)
44252 .style('opacity', 1)
44253 .transition()
44254 .delay(ts)
44255 .call(killNote);
44256 }
44257 });
44258};
44259
44260},{"d3":13,"fast-isnumeric":15}],188:[function(_dereq_,module,exports){
44261/**
44262* Copyright 2012-2020, Plotly, Inc.
44263* All rights reserved.
44264*
44265* This source code is licensed under the MIT license found in the
44266* LICENSE file in the root directory of this source tree.
44267*/
44268
44269
44270'use strict';
44271
44272var setCursor = _dereq_('./setcursor');
44273
44274var STASHATTR = 'data-savedcursor';
44275var NO_CURSOR = '!!';
44276
44277/*
44278 * works with our CSS cursor classes (see css/_cursor.scss)
44279 * to override a previous cursor set on d3 single-element selections,
44280 * by moving the name of the original cursor to the data-savedcursor attr.
44281 * omit cursor to revert to the previously set value.
44282 */
44283module.exports = function overrideCursor(el3, csr) {
44284 var savedCursor = el3.attr(STASHATTR);
44285 if(csr) {
44286 if(!savedCursor) {
44287 var classes = (el3.attr('class') || '').split(' ');
44288 for(var i = 0; i < classes.length; i++) {
44289 var cls = classes[i];
44290 if(cls.indexOf('cursor-') === 0) {
44291 el3.attr(STASHATTR, cls.substr(7))
44292 .classed(cls, false);
44293 }
44294 }
44295 if(!el3.attr(STASHATTR)) {
44296 el3.attr(STASHATTR, NO_CURSOR);
44297 }
44298 }
44299 setCursor(el3, csr);
44300 } else if(savedCursor) {
44301 el3.attr(STASHATTR, null);
44302
44303 if(savedCursor === NO_CURSOR) setCursor(el3);
44304 else setCursor(el3, savedCursor);
44305 }
44306};
44307
44308},{"./setcursor":196}],189:[function(_dereq_,module,exports){
44309/**
44310* Copyright 2012-2020, Plotly, Inc.
44311* All rights reserved.
44312*
44313* This source code is licensed under the MIT license found in the
44314* LICENSE file in the root directory of this source tree.
44315*/
44316
44317
44318'use strict';
44319
44320var dot = _dereq_('./matrix').dot;
44321var BADNUM = _dereq_('../constants/numerical').BADNUM;
44322
44323var polygon = module.exports = {};
44324
44325/**
44326 * Turn an array of [x, y] pairs into a polygon object
44327 * that can test if points are inside it
44328 *
44329 * @param ptsIn Array of [x, y] pairs
44330 *
44331 * @returns polygon Object {xmin, xmax, ymin, ymax, pts, contains}
44332 * (x|y)(min|max) are the bounding rect of the polygon
44333 * pts is the original array, with the first pair repeated at the end
44334 * contains is a function: (pt, omitFirstEdge)
44335 * pt is the [x, y] pair to test
44336 * omitFirstEdge truthy means points exactly on the first edge don't
44337 * count. This is for use adding one polygon to another so we
44338 * don't double-count the edge where they meet.
44339 * returns boolean: is pt inside the polygon (including on its edges)
44340 */
44341polygon.tester = function tester(ptsIn) {
44342 var pts = ptsIn.slice();
44343 var xmin = pts[0][0];
44344 var xmax = xmin;
44345 var ymin = pts[0][1];
44346 var ymax = ymin;
44347 var i;
44348
44349 pts.push(pts[0]);
44350 for(i = 1; i < pts.length; i++) {
44351 xmin = Math.min(xmin, pts[i][0]);
44352 xmax = Math.max(xmax, pts[i][0]);
44353 ymin = Math.min(ymin, pts[i][1]);
44354 ymax = Math.max(ymax, pts[i][1]);
44355 }
44356
44357 // do we have a rectangle? Handle this here, so we can use the same
44358 // tester for the rectangular case without sacrificing speed
44359
44360 var isRect = false;
44361 var rectFirstEdgeTest;
44362
44363 if(pts.length === 5) {
44364 if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz
44365 if(pts[2][0] === pts[3][0] &&
44366 pts[0][1] === pts[3][1] &&
44367 pts[1][1] === pts[2][1]) {
44368 isRect = true;
44369 rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; };
44370 }
44371 } else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert
44372 if(pts[2][1] === pts[3][1] &&
44373 pts[0][0] === pts[3][0] &&
44374 pts[1][0] === pts[2][0]) {
44375 isRect = true;
44376 rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; };
44377 }
44378 }
44379 }
44380
44381 function rectContains(pt, omitFirstEdge) {
44382 var x = pt[0];
44383 var y = pt[1];
44384
44385 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
44386 // pt is outside the bounding box of polygon
44387 return false;
44388 }
44389 if(omitFirstEdge && rectFirstEdgeTest(pt)) return false;
44390
44391 return true;
44392 }
44393
44394 function contains(pt, omitFirstEdge) {
44395 var x = pt[0];
44396 var y = pt[1];
44397
44398 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
44399 // pt is outside the bounding box of polygon
44400 return false;
44401 }
44402
44403 var imax = pts.length;
44404 var x1 = pts[0][0];
44405 var y1 = pts[0][1];
44406 var crossings = 0;
44407 var i;
44408 var x0;
44409 var y0;
44410 var xmini;
44411 var ycross;
44412
44413 for(i = 1; i < imax; i++) {
44414 // find all crossings of a vertical line upward from pt with
44415 // polygon segments
44416 // crossings exactly at xmax don't count, unless the point is
44417 // exactly on the segment, then it counts as inside.
44418 x0 = x1;
44419 y0 = y1;
44420 x1 = pts[i][0];
44421 y1 = pts[i][1];
44422 xmini = Math.min(x0, x1);
44423
44424 if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) {
44425 // outside the bounding box of this segment, it's only a crossing
44426 // if it's below the box.
44427
44428 continue;
44429 } else if(y < Math.min(y0, y1)) {
44430 // don't count the left-most point of the segment as a crossing
44431 // because we don't want to double-count adjacent crossings
44432 // UNLESS the polygon turns past vertical at exactly this x
44433 // Note that this is repeated below, but we can't factor it out
44434 // because
44435 if(x !== xmini) crossings++;
44436 } else {
44437 // inside the bounding box, check the actual line intercept
44438
44439 // vertical segment - we know already that the point is exactly
44440 // on the segment, so mark the crossing as exactly at the point.
44441 if(x1 === x0) ycross = y;
44442 // any other angle
44443 else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
44444
44445 // exactly on the edge: counts as inside the polygon, unless it's the
44446 // first edge and we're omitting it.
44447 if(y === ycross) {
44448 if(i === 1 && omitFirstEdge) return false;
44449 return true;
44450 }
44451
44452 if(y <= ycross && x !== xmini) crossings++;
44453 }
44454 }
44455
44456 // if we've gotten this far, odd crossings means inside, even is outside
44457 return crossings % 2 === 1;
44458 }
44459
44460 // detect if poly is degenerate
44461 var degenerate = true;
44462 var lastPt = pts[0];
44463 for(i = 1; i < pts.length; i++) {
44464 if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) {
44465 degenerate = false;
44466 break;
44467 }
44468 }
44469
44470 return {
44471 xmin: xmin,
44472 xmax: xmax,
44473 ymin: ymin,
44474 ymax: ymax,
44475 pts: pts,
44476 contains: isRect ? rectContains : contains,
44477 isRect: isRect,
44478 degenerate: degenerate
44479 };
44480};
44481
44482/**
44483 * Test if a segment of a points array is bent or straight
44484 *
44485 * @param pts Array of [x, y] pairs
44486 * @param start the index of the proposed start of the straight section
44487 * @param end the index of the proposed end point
44488 * @param tolerance the max distance off the line connecting start and end
44489 * before the line counts as bent
44490 * @returns boolean: true means this segment is bent, false means straight
44491 */
44492polygon.isSegmentBent = function isSegmentBent(pts, start, end, tolerance) {
44493 var startPt = pts[start];
44494 var segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]];
44495 var segmentSquared = dot(segment, segment);
44496 var segmentLen = Math.sqrt(segmentSquared);
44497 var unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen];
44498 var i;
44499 var part;
44500 var partParallel;
44501
44502 for(i = start + 1; i < end; i++) {
44503 part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]];
44504 partParallel = dot(part, segment);
44505
44506 if(partParallel < 0 || partParallel > segmentSquared ||
44507 Math.abs(dot(part, unitPerp)) > tolerance) return true;
44508 }
44509 return false;
44510};
44511
44512/**
44513 * Make a filtering polygon, to minimize the number of segments
44514 *
44515 * @param pts Array of [x, y] pairs (must start with at least 1 pair)
44516 * @param tolerance the maximum deviation from straight allowed for
44517 * removing points to simplify the polygon
44518 *
44519 * @returns Object {addPt, raw, filtered}
44520 * addPt is a function(pt: [x, y] pair) to add a raw point and
44521 * continue filtering
44522 * raw is all the input points
44523 * filtered is the resulting filtered Array of [x, y] pairs
44524 */
44525polygon.filter = function filter(pts, tolerance) {
44526 var ptsFiltered = [pts[0]];
44527 var doneRawIndex = 0;
44528 var doneFilteredIndex = 0;
44529
44530 function addPt(pt) {
44531 pts.push(pt);
44532 var prevFilterLen = ptsFiltered.length;
44533 var iLast = doneRawIndex;
44534 ptsFiltered.splice(doneFilteredIndex + 1);
44535
44536 for(var i = iLast + 1; i < pts.length; i++) {
44537 if(i === pts.length - 1 || polygon.isSegmentBent(pts, iLast, i + 1, tolerance)) {
44538 ptsFiltered.push(pts[i]);
44539 if(ptsFiltered.length < prevFilterLen - 2) {
44540 doneRawIndex = i;
44541 doneFilteredIndex = ptsFiltered.length - 1;
44542 }
44543 iLast = i;
44544 }
44545 }
44546 }
44547
44548 if(pts.length > 1) {
44549 var lastPt = pts.pop();
44550 addPt(lastPt);
44551 }
44552
44553 return {
44554 addPt: addPt,
44555 raw: pts,
44556 filtered: ptsFiltered
44557 };
44558};
44559
44560},{"../constants/numerical":155,"./matrix":183}],190:[function(_dereq_,module,exports){
44561/**
44562* Copyright 2012-2020, Plotly, Inc.
44563* All rights reserved.
44564*
44565* This source code is licensed under the MIT license found in the
44566* LICENSE file in the root directory of this source tree.
44567*/
44568
44569'use strict';
44570
44571/**
44572 * Push array with unique items
44573 *
44574 * Ignores falsy items, except 0 so we can use it to construct arrays of indices.
44575 *
44576 * @param {array} array
44577 * array to be filled
44578 * @param {any} item
44579 * item to be or not to be inserted
44580 * @return {array}
44581 * ref to array (now possibly containing one more item)
44582 *
44583 */
44584module.exports = function pushUnique(array, item) {
44585 if(item instanceof RegExp) {
44586 var itemStr = item.toString();
44587 for(var i = 0; i < array.length; i++) {
44588 if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
44589 return array;
44590 }
44591 }
44592 array.push(item);
44593 } else if((item || item === 0) && array.indexOf(item) === -1) array.push(item);
44594
44595 return array;
44596};
44597
44598},{}],191:[function(_dereq_,module,exports){
44599/**
44600* Copyright 2012-2020, Plotly, Inc.
44601* All rights reserved.
44602*
44603* This source code is licensed under the MIT license found in the
44604* LICENSE file in the root directory of this source tree.
44605*/
44606
44607'use strict';
44608
44609var Lib = _dereq_('../lib');
44610var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
44611
44612/**
44613 * Copy arg array *without* removing `undefined` values from objects.
44614 *
44615 * @param gd
44616 * @param args
44617 * @returns {Array}
44618 */
44619function copyArgArray(gd, args) {
44620 var copy = [];
44621 var arg;
44622
44623 for(var i = 0; i < args.length; i++) {
44624 arg = args[i];
44625
44626 if(arg === gd) copy[i] = arg;
44627 else if(typeof arg === 'object') {
44628 copy[i] = Array.isArray(arg) ?
44629 Lib.extendDeep([], arg) :
44630 Lib.extendDeepAll({}, arg);
44631 } else copy[i] = arg;
44632 }
44633
44634 return copy;
44635}
44636
44637
44638// -----------------------------------------------------
44639// Undo/Redo queue for plots
44640// -----------------------------------------------------
44641
44642
44643var queue = {};
44644
44645// TODO: disable/enable undo and redo buttons appropriately
44646
44647/**
44648 * Add an item to the undoQueue for a graphDiv
44649 *
44650 * @param gd
44651 * @param undoFunc Function undo this operation
44652 * @param undoArgs Args to supply undoFunc with
44653 * @param redoFunc Function to redo this operation
44654 * @param redoArgs Args to supply redoFunc with
44655 */
44656queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) {
44657 var queueObj,
44658 queueIndex;
44659
44660 // make sure we have the queue and our position in it
44661 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
44662 queueIndex = gd.undoQueue.index;
44663
44664 // if we're already playing an undo or redo, or if this is an auto operation
44665 // (like pane resize... any others?) then we don't save this to the undo queue
44666 if(gd.autoplay) {
44667 if(!gd.undoQueue.inSequence) gd.autoplay = false;
44668 return;
44669 }
44670
44671 // if we're not in a sequence or are just starting, we need a new queue item
44672 if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) {
44673 queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}};
44674 gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj);
44675 gd.undoQueue.index += 1;
44676 } else {
44677 queueObj = gd.undoQueue.queue[queueIndex - 1];
44678 }
44679 gd.undoQueue.beginSequence = false;
44680
44681 // we unshift to handle calls for undo in a forward for loop later
44682 if(queueObj) {
44683 queueObj.undo.calls.unshift(undoFunc);
44684 queueObj.undo.args.unshift(undoArgs);
44685 queueObj.redo.calls.push(redoFunc);
44686 queueObj.redo.args.push(redoArgs);
44687 }
44688
44689 if(gd.undoQueue.queue.length > dfltConfig.queueLength) {
44690 gd.undoQueue.queue.shift();
44691 gd.undoQueue.index--;
44692 }
44693};
44694
44695/**
44696 * Begin a sequence of undoQueue changes
44697 *
44698 * @param gd
44699 */
44700queue.startSequence = function(gd) {
44701 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
44702 gd.undoQueue.sequence = true;
44703 gd.undoQueue.beginSequence = true;
44704};
44705
44706/**
44707 * Stop a sequence of undoQueue changes
44708 *
44709 * Call this *after* you're sure your undo chain has ended
44710 *
44711 * @param gd
44712 */
44713queue.stopSequence = function(gd) {
44714 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
44715 gd.undoQueue.sequence = false;
44716 gd.undoQueue.beginSequence = false;
44717};
44718
44719/**
44720 * Move one step back in the undo queue, and undo the object there.
44721 *
44722 * @param gd
44723 */
44724queue.undo = function undo(gd) {
44725 var queueObj, i;
44726
44727 if(gd.framework && gd.framework.isPolar) {
44728 gd.framework.undo();
44729 return;
44730 }
44731 if(gd.undoQueue === undefined ||
44732 isNaN(gd.undoQueue.index) ||
44733 gd.undoQueue.index <= 0) {
44734 return;
44735 }
44736
44737 // index is pointing to next *forward* queueObj, point to the one we're undoing
44738 gd.undoQueue.index--;
44739
44740 // get the queueObj for instructions on how to undo
44741 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
44742
44743 // this sequence keeps things from adding to the queue during undo/redo
44744 gd.undoQueue.inSequence = true;
44745 for(i = 0; i < queueObj.undo.calls.length; i++) {
44746 queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
44747 }
44748 gd.undoQueue.inSequence = false;
44749 gd.autoplay = false;
44750};
44751
44752/**
44753 * Redo the current object in the undo, then move forward in the queue.
44754 *
44755 * @param gd
44756 */
44757queue.redo = function redo(gd) {
44758 var queueObj, i;
44759
44760 if(gd.framework && gd.framework.isPolar) {
44761 gd.framework.redo();
44762 return;
44763 }
44764 if(gd.undoQueue === undefined ||
44765 isNaN(gd.undoQueue.index) ||
44766 gd.undoQueue.index >= gd.undoQueue.queue.length) {
44767 return;
44768 }
44769
44770 // get the queueObj for instructions on how to undo
44771 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
44772
44773 // this sequence keeps things from adding to the queue during undo/redo
44774 gd.undoQueue.inSequence = true;
44775 for(i = 0; i < queueObj.redo.calls.length; i++) {
44776 queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]);
44777 }
44778 gd.undoQueue.inSequence = false;
44779 gd.autoplay = false;
44780
44781 // index is pointing to the thing we just redid, move it
44782 gd.undoQueue.index++;
44783};
44784
44785/**
44786 * Called by undo/redo to make the actual changes.
44787 *
44788 * Not meant to be called publically, but included for mocking out in tests.
44789 *
44790 * @param gd
44791 * @param func
44792 * @param args
44793 */
44794queue.plotDo = function(gd, func, args) {
44795 gd.autoplay = true;
44796
44797 // this *won't* copy gd and it preserves `undefined` properties!
44798 args = copyArgArray(gd, args);
44799
44800 // call the supplied function
44801 func.apply(null, args);
44802};
44803
44804module.exports = queue;
44805
44806},{"../lib":177,"../plot_api/plot_config":210}],192:[function(_dereq_,module,exports){
44807/**
44808* Copyright 2012-2020, Plotly, Inc.
44809* All rights reserved.
44810*
44811* This source code is licensed under the MIT license found in the
44812* LICENSE file in the root directory of this source tree.
44813*/
44814
44815'use strict';
44816
44817/*
44818 * make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10...
44819 *
44820 * @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc.
44821 * 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc
44822 * @param {Optional(string)} tail: a fixed piece after the id
44823 * eg counterRegex('scene', '.annotations') for scene2.annotations etc.
44824 * @param {boolean} openEnded: if true, the string may continue past the match.
44825 * @param {boolean} matchBeginning: if false, the string may start before the match.
44826 */
44827exports.counter = function(head, tail, openEnded, matchBeginning) {
44828 var fullTail = (tail || '') + (openEnded ? '' : '$');
44829 var startWithPrefix = matchBeginning === false ? '' : '^';
44830 if(head === 'xy') {
44831 return new RegExp(startWithPrefix + 'x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail);
44832 }
44833 return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail);
44834};
44835
44836},{}],193:[function(_dereq_,module,exports){
44837/**
44838* Copyright 2012-2020, Plotly, Inc.
44839* All rights reserved.
44840*
44841* This source code is licensed under the MIT license found in the
44842* LICENSE file in the root directory of this source tree.
44843*/
44844
44845
44846'use strict';
44847
44848// ASCEND: chop off the last nesting level - either [<n>] or .<key> - to ascend
44849// the attribute tree. the remaining attrString is in match[1]
44850var ASCEND = /^(.*)(\.[^\.\[\]]+|\[\d\])$/;
44851
44852// SIMPLEATTR: is this an un-nested attribute? (no dots or brackets)
44853var SIMPLEATTR = /^[^\.\[\]]+$/;
44854
44855/*
44856 * calculate a relative attribute string, similar to a relative path
44857 *
44858 * @param {string} baseAttr:
44859 * an attribute string, such as 'annotations[3].x'. The "current location"
44860 * is the attribute string minus the last component ('annotations[3]')
44861 * @param {string} relativeAttr:
44862 * a route to the desired attribute string, using '^' to ascend
44863 *
44864 * @return {string} attrString:
44865 * for example:
44866 * relativeAttr('annotations[3].x', 'y') = 'annotations[3].y'
44867 * relativeAttr('annotations[3].x', '^[2].z') = 'annotations[2].z'
44868 * relativeAttr('annotations[3].x', '^^margin') = 'margin'
44869 * relativeAttr('annotations[3].x', '^^margin.r') = 'margin.r'
44870 */
44871module.exports = function(baseAttr, relativeAttr) {
44872 while(relativeAttr) {
44873 var match = baseAttr.match(ASCEND);
44874
44875 if(match) baseAttr = match[1];
44876 else if(baseAttr.match(SIMPLEATTR)) baseAttr = '';
44877 else throw new Error('bad relativeAttr call:' + [baseAttr, relativeAttr]);
44878
44879 if(relativeAttr.charAt(0) === '^') relativeAttr = relativeAttr.slice(1);
44880 else break;
44881 }
44882
44883 if(baseAttr && relativeAttr.charAt(0) !== '[') {
44884 return baseAttr + '.' + relativeAttr;
44885 }
44886 return baseAttr + relativeAttr;
44887};
44888
44889},{}],194:[function(_dereq_,module,exports){
44890/**
44891* Copyright 2012-2020, Plotly, Inc.
44892* All rights reserved.
44893*
44894* This source code is licensed under the MIT license found in the
44895* LICENSE file in the root directory of this source tree.
44896*/
44897
44898
44899'use strict';
44900
44901var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
44902var isPlainObject = _dereq_('./is_plain_object');
44903
44904/**
44905 * Relink private _keys and keys with a function value from one container
44906 * to the new container.
44907 * Relink means copying if object is pass-by-value and adding a reference
44908 * if object is pass-by-ref.
44909 * This prevents deepCopying massive structures like a webgl context.
44910 */
44911module.exports = function relinkPrivateKeys(toContainer, fromContainer) {
44912 for(var k in fromContainer) {
44913 var fromVal = fromContainer[k];
44914 var toVal = toContainer[k];
44915
44916 if(toVal === fromVal) continue;
44917 if(toContainer.matches && k === '_categoriesMap') continue;
44918
44919 if(k.charAt(0) === '_' || typeof fromVal === 'function') {
44920 // if it already exists at this point, it's something
44921 // that we recreate each time around, so ignore it
44922 if(k in toContainer) continue;
44923
44924 toContainer[k] = fromVal;
44925 } else if(isArrayOrTypedArray(fromVal) && isArrayOrTypedArray(toVal) && isPlainObject(fromVal[0])) {
44926 // filter out data_array items that can contain user objects
44927 // most of the time the toVal === fromVal check will catch these early
44928 // but if the user makes new ones we also don't want to recurse in.
44929 if(k === 'customdata' || k === 'ids') continue;
44930
44931 // recurse into arrays containers
44932 var minLen = Math.min(fromVal.length, toVal.length);
44933 for(var j = 0; j < minLen; j++) {
44934 if((toVal[j] !== fromVal[j]) && isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) {
44935 relinkPrivateKeys(toVal[j], fromVal[j]);
44936 }
44937 }
44938 } else if(isPlainObject(fromVal) && isPlainObject(toVal)) {
44939 // recurse into objects, but only if they still exist
44940 relinkPrivateKeys(toVal, fromVal);
44941
44942 if(!Object.keys(toVal).length) delete toContainer[k];
44943 }
44944 }
44945};
44946
44947},{"./array":162,"./is_plain_object":178}],195:[function(_dereq_,module,exports){
44948/**
44949* Copyright 2012-2020, Plotly, Inc.
44950* All rights reserved.
44951*
44952* This source code is licensed under the MIT license found in the
44953* LICENSE file in the root directory of this source tree.
44954*/
44955
44956
44957'use strict';
44958
44959var isNumeric = _dereq_('fast-isnumeric');
44960var loggers = _dereq_('./loggers');
44961var identity = _dereq_('./identity');
44962var BADNUM = _dereq_('../constants/numerical').BADNUM;
44963
44964// don't trust floating point equality - fraction of bin size to call
44965// "on the line" and ensure that they go the right way specified by
44966// linelow
44967var roundingError = 1e-9;
44968
44969
44970/**
44971 * findBin - find the bin for val - note that it can return outside the
44972 * bin range any pos. or neg. integer for linear bins, or -1 or
44973 * bins.length-1 for explicit.
44974 * bins is either an object {start,size,end} or an array length #bins+1
44975 * bins can be either increasing or decreasing but must be monotonic
44976 * for linear bins, we can just calculate. For listed bins, run a binary
44977 * search linelow (truthy) says the bin boundary should be attributed to
44978 * the lower bin rather than the default upper bin
44979 */
44980exports.findBin = function(val, bins, linelow) {
44981 if(isNumeric(bins.start)) {
44982 return linelow ?
44983 Math.ceil((val - bins.start) / bins.size - roundingError) - 1 :
44984 Math.floor((val - bins.start) / bins.size + roundingError);
44985 } else {
44986 var n1 = 0;
44987 var n2 = bins.length;
44988 var c = 0;
44989 var binSize = (n2 > 1) ? (bins[n2 - 1] - bins[0]) / (n2 - 1) : 1;
44990 var n, test;
44991 if(binSize >= 0) {
44992 test = linelow ? lessThan : lessOrEqual;
44993 } else {
44994 test = linelow ? greaterOrEqual : greaterThan;
44995 }
44996 val += binSize * roundingError * (linelow ? -1 : 1) * (binSize >= 0 ? 1 : -1);
44997 // c is just to avoid infinite loops if there's an error
44998 while(n1 < n2 && c++ < 100) {
44999 n = Math.floor((n1 + n2) / 2);
45000 if(test(bins[n], val)) n1 = n + 1;
45001 else n2 = n;
45002 }
45003 if(c > 90) loggers.log('Long binary search...');
45004 return n1 - 1;
45005 }
45006};
45007
45008function lessThan(a, b) { return a < b; }
45009function lessOrEqual(a, b) { return a <= b; }
45010function greaterThan(a, b) { return a > b; }
45011function greaterOrEqual(a, b) { return a >= b; }
45012
45013exports.sorterAsc = function(a, b) { return a - b; };
45014exports.sorterDes = function(a, b) { return b - a; };
45015
45016/**
45017 * find distinct values in an array, lumping together ones that appear to
45018 * just be off by a rounding error
45019 * return the distinct values and the minimum difference between any two
45020 */
45021exports.distinctVals = function(valsIn) {
45022 var vals = valsIn.slice(); // otherwise we sort the original array...
45023 vals.sort(exports.sorterAsc); // undefined listed in the end - also works on IE11
45024
45025 var last;
45026 for(last = vals.length - 1; last > -1; last--) {
45027 if(vals[last] !== BADNUM) break;
45028 }
45029
45030 var minDiff = (vals[last] - vals[0]) || 1;
45031 var errDiff = minDiff / (last || 1) / 10000;
45032 var newVals = [];
45033 var preV;
45034 for(var i = 0; i <= last; i++) {
45035 var v = vals[i];
45036
45037 // make sure values aren't just off by a rounding error
45038 var diff = v - preV;
45039
45040 if(preV === undefined) {
45041 newVals.push(v);
45042 preV = v;
45043 } else if(diff > errDiff) {
45044 minDiff = Math.min(minDiff, diff);
45045
45046 newVals.push(v);
45047 preV = v;
45048 }
45049 }
45050
45051 return {vals: newVals, minDiff: minDiff};
45052};
45053
45054/**
45055 * return the smallest element from (sorted) array arrayIn that's bigger than val,
45056 * or (reverse) the largest element smaller than val
45057 * used to find the best tick given the minimum (non-rounded) tick
45058 * particularly useful for date/time where things are not powers of 10
45059 * binary search is probably overkill here...
45060 */
45061exports.roundUp = function(val, arrayIn, reverse) {
45062 var low = 0;
45063 var high = arrayIn.length - 1;
45064 var mid;
45065 var c = 0;
45066 var dlow = reverse ? 0 : 1;
45067 var dhigh = reverse ? 1 : 0;
45068 var rounded = reverse ? Math.ceil : Math.floor;
45069 // c is just to avoid infinite loops if there's an error
45070 while(low < high && c++ < 100) {
45071 mid = rounded((low + high) / 2);
45072 if(arrayIn[mid] <= val) low = mid + dlow;
45073 else high = mid - dhigh;
45074 }
45075 return arrayIn[low];
45076};
45077
45078/**
45079 * Tweak to Array.sort(sortFn) that improves performance for pre-sorted arrays
45080 *
45081 * Note that newer browsers (such as Chrome v70+) are starting to pick up
45082 * on pre-sorted arrays which may render the following optimization unnecessary
45083 * in the future.
45084 *
45085 * Motivation: sometimes we need to sort arrays but the input is likely to
45086 * already be sorted. Browsers don't seem to pick up on pre-sorted arrays,
45087 * and in fact Chrome is actually *slower* sorting pre-sorted arrays than purely
45088 * random arrays. FF is at least faster if the array is pre-sorted, but still
45089 * not as fast as it could be.
45090 * Here's how this plays out sorting a length-1e6 array:
45091 *
45092 * Calls to Sort FN | Chrome bare | FF bare | Chrome tweak | FF tweak
45093 * | v68.0 Mac | v61.0 Mac| |
45094 * ------------------+---------------+-----------+----------------+------------
45095 * ordered | 30.4e6 | 10.1e6 | 1e6 | 1e6
45096 * reversed | 29.4e6 | 9.9e6 | 1e6 + reverse | 1e6 + reverse
45097 * random | ~21e6 | ~18.7e6 | ~21e6 | ~18.7e6
45098 *
45099 * So this is a substantial win for pre-sorted (ordered or exactly reversed)
45100 * arrays. Including this wrapper on an unsorted array adds a penalty that will
45101 * in general be only a few calls to the sort function. The only case this
45102 * penalty will be significant is if the array is mostly sorted but there are
45103 * a few unsorted items near the end, but the penalty is still at most N calls
45104 * out of (for N=1e6) ~20N total calls
45105 *
45106 * @param {Array} array: the array, to be sorted in place
45107 * @param {function} sortFn: As in Array.sort, function(a, b) that puts
45108 * item a before item b if the return is negative, a after b if positive,
45109 * and no change if zero.
45110 * @return {Array}: the original array, sorted in place.
45111 */
45112exports.sort = function(array, sortFn) {
45113 var notOrdered = 0;
45114 var notReversed = 0;
45115 for(var i = 1; i < array.length; i++) {
45116 var pairOrder = sortFn(array[i], array[i - 1]);
45117 if(pairOrder < 0) notOrdered = 1;
45118 else if(pairOrder > 0) notReversed = 1;
45119 if(notOrdered && notReversed) return array.sort(sortFn);
45120 }
45121 return notReversed ? array : array.reverse();
45122};
45123
45124/**
45125 * find index in array 'arr' that minimizes 'fn'
45126 *
45127 * @param {array} arr : array where to search
45128 * @param {fn (optional)} fn : function to minimize,
45129 * if not given, fn is the identity function
45130 * @return {integer}
45131 */
45132exports.findIndexOfMin = function(arr, fn) {
45133 fn = fn || identity;
45134
45135 var min = Infinity;
45136 var ind;
45137
45138 for(var i = 0; i < arr.length; i++) {
45139 var v = fn(arr[i]);
45140 if(v < min) {
45141 min = v;
45142 ind = i;
45143 }
45144 }
45145 return ind;
45146};
45147
45148},{"../constants/numerical":155,"./identity":176,"./loggers":181,"fast-isnumeric":15}],196:[function(_dereq_,module,exports){
45149/**
45150* Copyright 2012-2020, Plotly, Inc.
45151* All rights reserved.
45152*
45153* This source code is licensed under the MIT license found in the
45154* LICENSE file in the root directory of this source tree.
45155*/
45156
45157
45158'use strict';
45159
45160// works with our CSS cursor classes (see css/_cursor.scss)
45161// to apply cursors to d3 single-element selections.
45162// omit cursor to revert to the default.
45163module.exports = function setCursor(el3, csr) {
45164 (el3.attr('class') || '').split(' ').forEach(function(cls) {
45165 if(cls.indexOf('cursor-') === 0) el3.classed(cls, false);
45166 });
45167
45168 if(csr) el3.classed('cursor-' + csr, true);
45169};
45170
45171},{}],197:[function(_dereq_,module,exports){
45172/**
45173* Copyright 2012-2020, Plotly, Inc.
45174* All rights reserved.
45175*
45176* This source code is licensed under the MIT license found in the
45177* LICENSE file in the root directory of this source tree.
45178*/
45179
45180
45181'use strict';
45182
45183var isNumeric = _dereq_('fast-isnumeric');
45184var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
45185
45186/**
45187 * aggNums() returns the result of an aggregate function applied to an array of
45188 * values, where non-numerical values have been tossed out.
45189 *
45190 * @param {function} f - aggregation function (e.g., Math.min)
45191 * @param {Number} v - initial value (continuing from previous calls)
45192 * if there's no continuing value, use null for selector-type
45193 * functions (max,min), or 0 for summations
45194 * @param {Array} a - array to aggregate (may be nested, we will recurse,
45195 * but all elements must have the same dimension)
45196 * @param {Number} len - maximum length of a to aggregate
45197 * @return {Number} - result of f applied to a starting from v
45198 */
45199exports.aggNums = function(f, v, a, len) {
45200 var i,
45201 b;
45202 if(!len || len > a.length) len = a.length;
45203 if(!isNumeric(v)) v = false;
45204 if(isArrayOrTypedArray(a[0])) {
45205 b = new Array(len);
45206 for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
45207 a = b;
45208 }
45209
45210 for(i = 0; i < len; i++) {
45211 if(!isNumeric(v)) v = a[i];
45212 else if(isNumeric(a[i])) v = f(+v, +a[i]);
45213 }
45214 return v;
45215};
45216
45217/**
45218 * mean & std dev functions using aggNums, so it handles non-numerics nicely
45219 * even need to use aggNums instead of .length, to toss out non-numerics
45220 */
45221exports.len = function(data) {
45222 return exports.aggNums(function(a) { return a + 1; }, 0, data);
45223};
45224
45225exports.mean = function(data, len) {
45226 if(!len) len = exports.len(data);
45227 return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
45228};
45229
45230exports.midRange = function(numArr) {
45231 if(numArr === undefined || numArr.length === 0) return undefined;
45232 return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2;
45233};
45234
45235exports.variance = function(data, len, mean) {
45236 if(!len) len = exports.len(data);
45237 if(!isNumeric(mean)) mean = exports.mean(data, len);
45238
45239 return exports.aggNums(function(a, b) {
45240 return a + Math.pow(b - mean, 2);
45241 }, 0, data) / len;
45242};
45243
45244exports.stdev = function(data, len, mean) {
45245 return Math.sqrt(exports.variance(data, len, mean));
45246};
45247
45248/**
45249 * median of a finite set of numbers
45250 * reference page: https://en.wikipedia.org/wiki/Median#Finite_set_of_numbers
45251**/
45252exports.median = function(data) {
45253 var b = data.slice().sort();
45254 return exports.interp(b, 0.5);
45255};
45256
45257/**
45258 * interp() computes a percentile (quantile) for a given distribution.
45259 * We interpolate the distribution (to compute quantiles, we follow method #10 here:
45260 * http://www.amstat.org/publications/jse/v14n3/langford.html).
45261 * Typically the index or rank (n * arr.length) may be non-integer.
45262 * For reference: ends are clipped to the extreme values in the array;
45263 * For box plots: index you get is half a point too high (see
45264 * http://en.wikipedia.org/wiki/Percentile#Nearest_rank) but note that this definition
45265 * indexes from 1 rather than 0, so we subtract 1/2 (instead of add).
45266 *
45267 * @param {Array} arr - This array contains the values that make up the distribution.
45268 * @param {Number} n - Between 0 and 1, n = p/100 is such that we compute the p^th percentile.
45269 * For example, the 50th percentile (or median) corresponds to n = 0.5
45270 * @return {Number} - percentile
45271 */
45272exports.interp = function(arr, n) {
45273 if(!isNumeric(n)) throw 'n should be a finite number';
45274 n = n * arr.length - 0.5;
45275 if(n < 0) return arr[0];
45276 if(n > arr.length - 1) return arr[arr.length - 1];
45277 var frac = n % 1;
45278 return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
45279};
45280
45281},{"./array":162,"fast-isnumeric":15}],198:[function(_dereq_,module,exports){
45282/**
45283* Copyright 2012-2020, Plotly, Inc.
45284* All rights reserved.
45285*
45286* This source code is licensed under the MIT license found in the
45287* LICENSE file in the root directory of this source tree.
45288*/
45289
45290
45291'use strict';
45292
45293/* global MathJax:false */
45294
45295var d3 = _dereq_('d3');
45296
45297var Lib = _dereq_('../lib');
45298var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
45299var LINE_SPACING = _dereq_('../constants/alignment').LINE_SPACING;
45300
45301// text converter
45302
45303function getSize(_selection, _dimension) {
45304 return _selection.node().getBoundingClientRect()[_dimension];
45305}
45306
45307var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
45308
45309exports.convertToTspans = function(_context, gd, _callback) {
45310 var str = _context.text();
45311
45312 // Until we get tex integrated more fully (so it can be used along with non-tex)
45313 // allow some elements to prohibit it by attaching 'data-notex' to the original
45314 var tex = (!_context.attr('data-notex')) &&
45315 (typeof MathJax !== 'undefined') &&
45316 str.match(FIND_TEX);
45317
45318 var parent = d3.select(_context.node().parentNode);
45319 if(parent.empty()) return;
45320 var svgClass = (_context.attr('class')) ? _context.attr('class').split(' ')[0] : 'text';
45321 svgClass += '-math';
45322 parent.selectAll('svg.' + svgClass).remove();
45323 parent.selectAll('g.' + svgClass + '-group').remove();
45324 _context.style('display', null)
45325 .attr({
45326 // some callers use data-unformatted *from the <text> element* in 'cancel'
45327 // so we need it here even if we're going to turn it into math
45328 // these two (plus style and text-anchor attributes) form the key we're
45329 // going to use for Drawing.bBox
45330 'data-unformatted': str,
45331 'data-math': 'N'
45332 });
45333
45334 function showText() {
45335 if(!parent.empty()) {
45336 svgClass = _context.attr('class') + '-math';
45337 parent.select('svg.' + svgClass).remove();
45338 }
45339 _context.text('')
45340 .style('white-space', 'pre');
45341
45342 var hasLink = buildSVGText(_context.node(), str);
45343
45344 if(hasLink) {
45345 // at least in Chrome, pointer-events does not seem
45346 // to be honored in children of <text> elements
45347 // so if we have an anchor, we have to make the
45348 // whole element respond
45349 _context.style('pointer-events', 'all');
45350 }
45351
45352 exports.positionText(_context);
45353
45354 if(_callback) _callback.call(_context);
45355 }
45356
45357 if(tex) {
45358 ((gd && gd._promises) || []).push(new Promise(function(resolve) {
45359 _context.style('display', 'none');
45360 var fontSize = parseInt(_context.node().style.fontSize, 10);
45361 var config = {fontSize: fontSize};
45362
45363 texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
45364 parent.selectAll('svg.' + svgClass).remove();
45365 parent.selectAll('g.' + svgClass + '-group').remove();
45366
45367 var newSvg = _svgEl && _svgEl.select('svg');
45368 if(!newSvg || !newSvg.node()) {
45369 showText();
45370 resolve();
45371 return;
45372 }
45373
45374 var mathjaxGroup = parent.append('g')
45375 .classed(svgClass + '-group', true)
45376 .attr({
45377 'pointer-events': 'none',
45378 'data-unformatted': str,
45379 'data-math': 'Y'
45380 });
45381
45382 mathjaxGroup.node().appendChild(newSvg.node());
45383
45384 // stitch the glyph defs
45385 if(_glyphDefs && _glyphDefs.node()) {
45386 newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true),
45387 newSvg.node().firstChild);
45388 }
45389
45390 newSvg.attr({
45391 'class': svgClass,
45392 height: _svgBBox.height,
45393 preserveAspectRatio: 'xMinYMin meet'
45394 })
45395 .style({overflow: 'visible', 'pointer-events': 'none'});
45396
45397 var fill = _context.node().style.fill || 'black';
45398 var g = newSvg.select('g');
45399 g.attr({fill: fill, stroke: fill});
45400
45401 var newSvgW = getSize(g, 'width');
45402 var newSvgH = getSize(g, 'height');
45403 var newX = +_context.attr('x') - newSvgW *
45404 {start: 0, middle: 0.5, end: 1}[_context.attr('text-anchor') || 'start'];
45405 // font baseline is about 1/4 fontSize below centerline
45406 var textHeight = fontSize || getSize(_context, 'height');
45407 var dy = -textHeight / 4;
45408
45409 if(svgClass[0] === 'y') {
45410 mathjaxGroup.attr({
45411 transform: 'rotate(' + [-90, +_context.attr('x'), +_context.attr('y')] +
45412 ') translate(' + [-newSvgW / 2, dy - newSvgH / 2] + ')'
45413 });
45414 newSvg.attr({x: +_context.attr('x'), y: +_context.attr('y')});
45415 } else if(svgClass[0] === 'l') {
45416 newSvg.attr({x: _context.attr('x'), y: dy - (newSvgH / 2)});
45417 } else if(svgClass[0] === 'a' && svgClass.indexOf('atitle') !== 0) {
45418 newSvg.attr({x: 0, y: dy});
45419 } else {
45420 newSvg.attr({x: newX, y: (+_context.attr('y') + dy - newSvgH / 2)});
45421 }
45422
45423 if(_callback) _callback.call(_context, mathjaxGroup);
45424 resolve(mathjaxGroup);
45425 });
45426 }));
45427 } else showText();
45428
45429 return _context;
45430};
45431
45432
45433// MathJax
45434
45435var LT_MATCH = /(<|&lt;|&#60;)/g;
45436var GT_MATCH = /(>|&gt;|&#62;)/g;
45437
45438function cleanEscapesForTex(s) {
45439 return s.replace(LT_MATCH, '\\lt ')
45440 .replace(GT_MATCH, '\\gt ');
45441}
45442
45443function texToSVG(_texString, _config, _callback) {
45444 var originalRenderer,
45445 originalConfig,
45446 originalProcessSectionDelay,
45447 tmpDiv;
45448
45449 MathJax.Hub.Queue(
45450 function() {
45451 originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config);
45452
45453 originalProcessSectionDelay = MathJax.Hub.processSectionDelay;
45454 if(MathJax.Hub.processSectionDelay !== undefined) {
45455 // MathJax 2.5+
45456 MathJax.Hub.processSectionDelay = 0;
45457 }
45458
45459 return MathJax.Hub.Config({
45460 messageStyle: 'none',
45461 tex2jax: {
45462 inlineMath: [['$', '$'], ['\\(', '\\)']]
45463 },
45464 displayAlign: 'left',
45465 });
45466 },
45467 function() {
45468 // Get original renderer
45469 originalRenderer = MathJax.Hub.config.menuSettings.renderer;
45470 if(originalRenderer !== 'SVG') {
45471 return MathJax.Hub.setRenderer('SVG');
45472 }
45473 },
45474 function() {
45475 var randomID = 'math-output-' + Lib.randstr({}, 64);
45476 tmpDiv = d3.select('body').append('div')
45477 .attr({id: randomID})
45478 .style({visibility: 'hidden', position: 'absolute'})
45479 .style({'font-size': _config.fontSize + 'px'})
45480 .text(cleanEscapesForTex(_texString));
45481
45482 return MathJax.Hub.Typeset(tmpDiv.node());
45483 },
45484 function() {
45485 var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs');
45486
45487 if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) {
45488 Lib.log('There was an error in the tex syntax.', _texString);
45489 _callback();
45490 } else {
45491 var svgBBox = tmpDiv.select('svg').node().getBoundingClientRect();
45492 _callback(tmpDiv.select('.MathJax_SVG'), glyphDefs, svgBBox);
45493 }
45494
45495 tmpDiv.remove();
45496
45497 if(originalRenderer !== 'SVG') {
45498 return MathJax.Hub.setRenderer(originalRenderer);
45499 }
45500 },
45501 function() {
45502 if(originalProcessSectionDelay !== undefined) {
45503 MathJax.Hub.processSectionDelay = originalProcessSectionDelay;
45504 }
45505 return MathJax.Hub.Config(originalConfig);
45506 });
45507}
45508
45509var TAG_STYLES = {
45510 // would like to use baseline-shift for sub/sup but FF doesn't support it
45511 // so we need to use dy along with the uber hacky shift-back-to
45512 // baseline below
45513 sup: 'font-size:70%',
45514 sub: 'font-size:70%',
45515 b: 'font-weight:bold',
45516 i: 'font-style:italic',
45517 a: 'cursor:pointer',
45518 span: '',
45519 em: 'font-style:italic;font-weight:bold'
45520};
45521
45522// baseline shifts for sub and sup
45523var SHIFT_DY = {
45524 sub: '0.3em',
45525 sup: '-0.6em'
45526};
45527// reset baseline by adding a tspan (empty except for a zero-width space)
45528// with dy of -70% * SHIFT_DY (because font-size=70%)
45529var RESET_DY = {
45530 sub: '-0.21em',
45531 sup: '0.42em'
45532};
45533var ZERO_WIDTH_SPACE = '\u200b';
45534
45535/*
45536 * Whitelist of protocols in user-supplied urls. Mostly we want to avoid javascript
45537 * and related attack vectors. The empty items are there for IE, that in various
45538 * versions treats relative paths as having different flavors of no protocol, while
45539 * other browsers have these explicitly inherit the protocol of the page they're in.
45540 */
45541var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':'];
45542
45543var NEWLINES = exports.NEWLINES = /(\r\n?|\n)/g;
45544
45545var SPLIT_TAGS = /(<[^<>]*>)/;
45546
45547var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i;
45548
45549var BR_TAG = /<br(\s+.*)?>/i;
45550exports.BR_TAG_ALL = /<br(\s+.*)?>/gi;
45551
45552/*
45553 * style and href: pull them out of either single or double quotes. Also
45554 * - target: (_blank|_self|_parent|_top|framename)
45555 * note that you can't use target to get a popup but if you use popup,
45556 * a `framename` will be passed along as the name of the popup window.
45557 * per the spec, cannot contain whitespace.
45558 * for backward compatibility we default to '_blank'
45559 * - popup: a custom one for us to enable popup (new window) links. String
45560 * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550'
45561 * note that at least in Chrome, you need to give at least one property
45562 * in this string or the page will open in a new tab anyway. We follow this
45563 * convention and will not make a popup if this string is empty.
45564 * per the spec, cannot contain whitespace.
45565 *
45566 * Because we hack in other attributes with style (sub & sup), drop any trailing
45567 * semicolon in user-supplied styles so we can consistently append the tag-dependent style
45568 *
45569 * These are for tag attributes; Chrome anyway will convert entities in
45570 * attribute values, but not in attribute names
45571 * you can test this by for example:
45572 * > p = document.createElement('p')
45573 * > p.innerHTML = '<span styl&#x65;="font-color:r&#x65;d;">Hi</span>'
45574 * > p.innerHTML
45575 * <- '<span styl&#x65;="font-color:red;">Hi</span>'
45576 */
45577var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i;
45578var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i;
45579var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i;
45580var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i;
45581
45582// dedicated matcher for these quoted regexes, that can return their results
45583// in two different places
45584function getQuotedMatch(_str, re) {
45585 if(!_str) return null;
45586 var match = _str.match(re);
45587 var result = match && (match[3] || match[4]);
45588 return result && convertEntities(result);
45589}
45590
45591var COLORMATCH = /(^|;)\s*color:/;
45592
45593/**
45594 * Strip string of tags
45595 *
45596 * @param {string} _str : input string
45597 * @param {object} opts :
45598 * - len {number} max length of output string
45599 * - allowedTags {array} list of pseudo-html tags to NOT strip
45600 * @return {string}
45601 */
45602exports.plainText = function(_str, opts) {
45603 opts = opts || {};
45604
45605 var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity;
45606 var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br'];
45607
45608 var ellipsis = '...';
45609 var eLen = ellipsis.length;
45610
45611 var oldParts = _str.split(SPLIT_TAGS);
45612 var newParts = [];
45613 var prevTag = '';
45614 var l = 0;
45615
45616 for(var i = 0; i < oldParts.length; i++) {
45617 var p = oldParts[i];
45618 var match = p.match(ONE_TAG);
45619 var tagType = match && match[2].toLowerCase();
45620
45621 if(tagType) {
45622 // N.B. tags do not count towards string length
45623 if(allowedTags.indexOf(tagType) !== -1) {
45624 newParts.push(p);
45625 prevTag = tagType;
45626 }
45627 } else {
45628 var pLen = p.length;
45629
45630 if((l + pLen) < len) {
45631 newParts.push(p);
45632 l += pLen;
45633 } else if(l < len) {
45634 var pLen2 = len - l;
45635
45636 if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) {
45637 newParts.pop();
45638 }
45639
45640 if(len > eLen) {
45641 newParts.push(p.substr(0, pLen2 - eLen) + ellipsis);
45642 } else {
45643 newParts.push(p.substr(0, pLen2));
45644 }
45645 break;
45646 }
45647
45648 prevTag = '';
45649 }
45650 }
45651
45652 return newParts.join('');
45653};
45654
45655/*
45656 * N.B. HTML entities are listed without the leading '&' and trailing ';'
45657 * https://www.freeformatter.com/html-entities.html
45658 *
45659 * FWIW if we wanted to support the full set, it has 2261 entries:
45660 * https://www.w3.org/TR/html5/entities.json
45661 * though I notice that some of these are duplicates and/or are missing ";"
45662 * eg: "&amp;", "&amp", "&AMP;", and "&AMP" all map to "&"
45663 * We no longer need to include numeric entities here, these are now handled
45664 * by String.fromCodePoint/fromCharCode
45665 *
45666 * Anyway the only ones that are really important to allow are the HTML special
45667 * chars <, >, and &, because these ones can trigger special processing if not
45668 * replaced by the corresponding entity.
45669 */
45670var entityToUnicode = {
45671 mu: 'μ',
45672 amp: '&',
45673 lt: '<',
45674 gt: '>',
45675 nbsp: ' ',
45676 times: '×',
45677 plusmn: '±',
45678 deg: '°'
45679};
45680
45681// NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the
45682// ones we support use only lowercase. If we ever change that, update the regex.
45683var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g;
45684function convertEntities(_str) {
45685 return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) {
45686 var outChar;
45687 if(innerMatch.charAt(0) === '#') {
45688 // cannot use String.fromCodePoint in IE
45689 outChar = fromCodePoint(
45690 innerMatch.charAt(1) === 'x' ?
45691 parseInt(innerMatch.substr(2), 16) :
45692 parseInt(innerMatch.substr(1), 10)
45693 );
45694 } else outChar = entityToUnicode[innerMatch];
45695
45696 // as in regular HTML, if we didn't decode the entity just
45697 // leave the raw text in place.
45698 return outChar || fullMatch;
45699 });
45700}
45701exports.convertEntities = convertEntities;
45702
45703function fromCodePoint(code) {
45704 // Don't allow overflow. In Chrome this turns into � but I feel like it's
45705 // more useful to just not convert it at all.
45706 if(code > 0x10FFFF) return;
45707 var stringFromCodePoint = String.fromCodePoint;
45708 if(stringFromCodePoint) return stringFromCodePoint(code);
45709
45710 // IE doesn't have String.fromCodePoint
45711 // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
45712 var stringFromCharCode = String.fromCharCode;
45713 if(code <= 0xFFFF) return stringFromCharCode(code);
45714 return stringFromCharCode(
45715 (code >> 10) + 0xD7C0,
45716 (code % 0x400) + 0xDC00
45717 );
45718}
45719
45720/*
45721 * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these
45722 * to containerNode
45723 *
45724 * @param {svg text element} containerNode: the <text> node to insert this text into
45725 * @param {string} str: the pseudo-html string to convert to svg
45726 *
45727 * @returns {bool}: does the result contain any links? We need to handle the text element
45728 * somewhat differently if it does, so just keep track of this when it happens.
45729 */
45730function buildSVGText(containerNode, str) {
45731 /*
45732 * Normalize behavior between IE and others wrt newlines and whitespace:pre
45733 * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
45734 * Chrome and FF display \n, \r, or \r\n as a space in this mode.
45735 * I feel like at some point we turned these into <br> but currently we don't so
45736 * I'm just going to cement what we do now in Chrome and FF
45737 */
45738 str = str.replace(NEWLINES, ' ');
45739
45740 var hasLink = false;
45741
45742 // as we're building the text, keep track of what elements we're nested inside
45743 // nodeStack will be an array of {node, type, style, href, target, popup}
45744 // where only type: 'a' gets the last 3 and node is only added when it's created
45745 var nodeStack = [];
45746 var currentNode;
45747 var currentLine = -1;
45748
45749 function newLine() {
45750 currentLine++;
45751
45752 var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
45753 d3.select(lineNode).attr({
45754 class: 'line',
45755 dy: (currentLine * LINE_SPACING) + 'em'
45756 });
45757 containerNode.appendChild(lineNode);
45758
45759 currentNode = lineNode;
45760
45761 var oldNodeStack = nodeStack;
45762 nodeStack = [{node: lineNode}];
45763
45764 if(oldNodeStack.length > 1) {
45765 for(var i = 1; i < oldNodeStack.length; i++) {
45766 enterNode(oldNodeStack[i]);
45767 }
45768 }
45769 }
45770
45771 function enterNode(nodeSpec) {
45772 var type = nodeSpec.type;
45773 var nodeAttrs = {};
45774 var nodeType;
45775
45776 if(type === 'a') {
45777 nodeType = 'a';
45778 var target = nodeSpec.target;
45779 var href = nodeSpec.href;
45780 var popup = nodeSpec.popup;
45781 if(href) {
45782 nodeAttrs = {
45783 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace',
45784 target: target,
45785 'xlink:xlink:href': href
45786 };
45787 if(popup) {
45788 // security: href and target are not inserted as code but
45789 // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/
45790 nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' +
45791 popup + '");return false;';
45792 }
45793 }
45794 } else nodeType = 'tspan';
45795
45796 if(nodeSpec.style) nodeAttrs.style = nodeSpec.style;
45797
45798 var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType);
45799
45800 if(type === 'sup' || type === 'sub') {
45801 addTextNode(currentNode, ZERO_WIDTH_SPACE);
45802 currentNode.appendChild(newNode);
45803
45804 var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
45805 addTextNode(resetter, ZERO_WIDTH_SPACE);
45806 d3.select(resetter).attr('dy', RESET_DY[type]);
45807 nodeAttrs.dy = SHIFT_DY[type];
45808
45809 currentNode.appendChild(newNode);
45810 currentNode.appendChild(resetter);
45811 } else {
45812 currentNode.appendChild(newNode);
45813 }
45814
45815 d3.select(newNode).attr(nodeAttrs);
45816
45817 currentNode = nodeSpec.node = newNode;
45818 nodeStack.push(nodeSpec);
45819 }
45820
45821 function addTextNode(node, text) {
45822 node.appendChild(document.createTextNode(text));
45823 }
45824
45825 function exitNode(type) {
45826 // A bare closing tag can't close the root node. If we encounter this it
45827 // means there's an extra closing tag that can just be ignored:
45828 if(nodeStack.length === 1) {
45829 Lib.log('Ignoring unexpected end tag </' + type + '>.', str);
45830 return;
45831 }
45832
45833 var innerNode = nodeStack.pop();
45834
45835 if(type !== innerNode.type) {
45836 Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' +
45837 type + '>. Pretending it did match.', str);
45838 }
45839 currentNode = nodeStack[nodeStack.length - 1].node;
45840 }
45841
45842 var hasLines = BR_TAG.test(str);
45843
45844 if(hasLines) newLine();
45845 else {
45846 currentNode = containerNode;
45847 nodeStack = [{node: containerNode}];
45848 }
45849
45850 var parts = str.split(SPLIT_TAGS);
45851 for(var i = 0; i < parts.length; i++) {
45852 var parti = parts[i];
45853 var match = parti.match(ONE_TAG);
45854 var tagType = match && match[2].toLowerCase();
45855 var tagStyle = TAG_STYLES[tagType];
45856
45857 if(tagType === 'br') {
45858 newLine();
45859 } else if(tagStyle === undefined) {
45860 addTextNode(currentNode, convertEntities(parti));
45861 } else {
45862 // tag - open or close
45863 if(match[1]) {
45864 exitNode(tagType);
45865 } else {
45866 var extra = match[4];
45867
45868 var nodeSpec = {type: tagType};
45869
45870 // now add style, from both the tag name and any extra css
45871 // Most of the svg css that users will care about is just like html,
45872 // but font color is different (uses fill). Let our users ignore this.
45873 var css = getQuotedMatch(extra, STYLEMATCH);
45874 if(css) {
45875 css = css.replace(COLORMATCH, '$1 fill:');
45876 if(tagStyle) css += ';' + tagStyle;
45877 } else if(tagStyle) css = tagStyle;
45878
45879 if(css) nodeSpec.style = css;
45880
45881 if(tagType === 'a') {
45882 hasLink = true;
45883
45884 var href = getQuotedMatch(extra, HREFMATCH);
45885
45886 if(href) {
45887 // check safe protocols
45888 var dummyAnchor = document.createElement('a');
45889 dummyAnchor.href = href;
45890 if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
45891 // Decode href to allow both already encoded and not encoded
45892 // URIs. Without decoding prior encoding, an already encoded
45893 // URI would be encoded twice producing a semantically different URI.
45894 nodeSpec.href = encodeURI(decodeURI(href));
45895 nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank';
45896 nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH);
45897 }
45898 }
45899 }
45900
45901 enterNode(nodeSpec);
45902 }
45903 }
45904 }
45905
45906 return hasLink;
45907}
45908
45909/*
45910 * sanitizeHTML: port of buildSVGText aimed at providing a clean subset of HTML
45911 * @param {string} str: the html string to clean
45912 * @returns {string}: a cleaned and normalized version of the input,
45913 * supporting only a small subset of html
45914 */
45915exports.sanitizeHTML = function sanitizeHTML(str) {
45916 str = str.replace(NEWLINES, ' ');
45917
45918 var rootNode = document.createElement('p');
45919 var currentNode = rootNode;
45920 var nodeStack = [];
45921
45922 var parts = str.split(SPLIT_TAGS);
45923 for(var i = 0; i < parts.length; i++) {
45924 var parti = parts[i];
45925 var match = parti.match(ONE_TAG);
45926 var tagType = match && match[2].toLowerCase();
45927
45928 if(tagType in TAG_STYLES) {
45929 if(match[1]) {
45930 if(nodeStack.length) {
45931 currentNode = nodeStack.pop();
45932 }
45933 } else {
45934 var extra = match[4];
45935
45936 var css = getQuotedMatch(extra, STYLEMATCH);
45937 var nodeAttrs = css ? {style: css} : {};
45938
45939 if(tagType === 'a') {
45940 var href = getQuotedMatch(extra, HREFMATCH);
45941
45942 if(href) {
45943 var dummyAnchor = document.createElement('a');
45944 dummyAnchor.href = href;
45945 if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
45946 nodeAttrs.href = encodeURI(decodeURI(href));
45947 var target = getQuotedMatch(extra, TARGETMATCH);
45948 if(target) {
45949 nodeAttrs.target = target;
45950 }
45951 }
45952 }
45953 }
45954
45955 var newNode = document.createElement(tagType);
45956 currentNode.appendChild(newNode);
45957 d3.select(newNode).attr(nodeAttrs);
45958
45959 currentNode = newNode;
45960 nodeStack.push(newNode);
45961 }
45962 } else {
45963 currentNode.appendChild(
45964 document.createTextNode(convertEntities(parti))
45965 );
45966 }
45967 }
45968 var key = 'innerHTML'; // i.e. to avoid pass test-syntax
45969 return rootNode[key];
45970};
45971
45972exports.lineCount = function lineCount(s) {
45973 return s.selectAll('tspan.line').size() || 1;
45974};
45975
45976exports.positionText = function positionText(s, x, y) {
45977 return s.each(function() {
45978 var text = d3.select(this);
45979
45980 function setOrGet(attr, val) {
45981 if(val === undefined) {
45982 val = text.attr(attr);
45983 if(val === null) {
45984 text.attr(attr, 0);
45985 val = 0;
45986 }
45987 } else text.attr(attr, val);
45988 return val;
45989 }
45990
45991 var thisX = setOrGet('x', x);
45992 var thisY = setOrGet('y', y);
45993
45994 if(this.nodeName === 'text') {
45995 text.selectAll('tspan.line').attr({x: thisX, y: thisY});
45996 }
45997 });
45998};
45999
46000function alignHTMLWith(_base, container, options) {
46001 var alignH = options.horizontalAlign;
46002 var alignV = options.verticalAlign || 'top';
46003 var bRect = _base.node().getBoundingClientRect();
46004 var cRect = container.node().getBoundingClientRect();
46005 var thisRect;
46006 var getTop;
46007 var getLeft;
46008
46009 if(alignV === 'bottom') {
46010 getTop = function() { return bRect.bottom - thisRect.height; };
46011 } else if(alignV === 'middle') {
46012 getTop = function() { return bRect.top + (bRect.height - thisRect.height) / 2; };
46013 } else { // default: top
46014 getTop = function() { return bRect.top; };
46015 }
46016
46017 if(alignH === 'right') {
46018 getLeft = function() { return bRect.right - thisRect.width; };
46019 } else if(alignH === 'center') {
46020 getLeft = function() { return bRect.left + (bRect.width - thisRect.width) / 2; };
46021 } else { // default: left
46022 getLeft = function() { return bRect.left; };
46023 }
46024
46025 return function() {
46026 thisRect = this.node().getBoundingClientRect();
46027 this.style({
46028 top: (getTop() - cRect.top) + 'px',
46029 left: (getLeft() - cRect.left) + 'px',
46030 'z-index': 1000
46031 });
46032 return this;
46033 };
46034}
46035
46036/*
46037 * Editable title
46038 * @param {d3.selection} context: the element being edited. Normally text,
46039 * but if it isn't, you should provide the styling options
46040 * @param {object} options:
46041 * @param {div} options.gd: graphDiv
46042 * @param {d3.selection} options.delegate: item to bind events to if not this
46043 * @param {boolean} options.immediate: start editing now (true) or on click (false, default)
46044 * @param {string} options.fill: font color if not as shown
46045 * @param {string} options.background: background color if not as shown
46046 * @param {string} options.text: initial text, if not as shown
46047 * @param {string} options.horizontalAlign: alignment of the edit box wrt. the bound element
46048 * @param {string} options.verticalAlign: alignment of the edit box wrt. the bound element
46049 */
46050
46051exports.makeEditable = function(context, options) {
46052 var gd = options.gd;
46053 var _delegate = options.delegate;
46054 var dispatch = d3.dispatch('edit', 'input', 'cancel');
46055 var handlerElement = _delegate || context;
46056
46057 context.style({'pointer-events': _delegate ? 'none' : 'all'});
46058
46059 if(context.size() !== 1) throw new Error('boo');
46060
46061 function handleClick() {
46062 appendEditable();
46063 context.style({opacity: 0});
46064 // also hide any mathjax svg
46065 var svgClass = handlerElement.attr('class');
46066 var mathjaxClass;
46067 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
46068 else mathjaxClass = '[class*=-math-group]';
46069 if(mathjaxClass) {
46070 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
46071 }
46072 }
46073
46074 function selectElementContents(_el) {
46075 var el = _el.node();
46076 var range = document.createRange();
46077 range.selectNodeContents(el);
46078 var sel = window.getSelection();
46079 sel.removeAllRanges();
46080 sel.addRange(range);
46081 el.focus();
46082 }
46083
46084 function appendEditable() {
46085 var plotDiv = d3.select(gd);
46086 var container = plotDiv.select('.svg-container');
46087 var div = container.append('div');
46088 var cStyle = context.node().style;
46089 var fontSize = parseFloat(cStyle.fontSize || 12);
46090
46091 var initialText = options.text;
46092 if(initialText === undefined) initialText = context.attr('data-unformatted');
46093
46094 div.classed('plugin-editable editable', true)
46095 .style({
46096 position: 'absolute',
46097 'font-family': cStyle.fontFamily || 'Arial',
46098 'font-size': fontSize,
46099 color: options.fill || cStyle.fill || 'black',
46100 opacity: 1,
46101 'background-color': options.background || 'transparent',
46102 outline: '#ffffff33 1px solid',
46103 margin: [-fontSize / 8 + 1, 0, 0, -1].join('px ') + 'px',
46104 padding: '0',
46105 'box-sizing': 'border-box'
46106 })
46107 .attr({contenteditable: true})
46108 .text(initialText)
46109 .call(alignHTMLWith(context, container, options))
46110 .on('blur', function() {
46111 gd._editing = false;
46112 context.text(this.textContent)
46113 .style({opacity: 1});
46114 var svgClass = d3.select(this).attr('class');
46115 var mathjaxClass;
46116 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
46117 else mathjaxClass = '[class*=-math-group]';
46118 if(mathjaxClass) {
46119 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
46120 }
46121 var text = this.textContent;
46122 d3.select(this).transition().duration(0).remove();
46123 d3.select(document).on('mouseup', null);
46124 dispatch.edit.call(context, text);
46125 })
46126 .on('focus', function() {
46127 var editDiv = this;
46128 gd._editing = true;
46129 d3.select(document).on('mouseup', function() {
46130 if(d3.event.target === editDiv) return false;
46131 if(document.activeElement === div.node()) div.node().blur();
46132 });
46133 })
46134 .on('keyup', function() {
46135 if(d3.event.which === 27) {
46136 gd._editing = false;
46137 context.style({opacity: 1});
46138 d3.select(this)
46139 .style({opacity: 0})
46140 .on('blur', function() { return false; })
46141 .transition().remove();
46142 dispatch.cancel.call(context, this.textContent);
46143 } else {
46144 dispatch.input.call(context, this.textContent);
46145 d3.select(this).call(alignHTMLWith(context, container, options));
46146 }
46147 })
46148 .on('keydown', function() {
46149 if(d3.event.which === 13) this.blur();
46150 })
46151 .call(selectElementContents);
46152 }
46153
46154 if(options.immediate) handleClick();
46155 else handlerElement.on('click', handleClick);
46156
46157 return d3.rebind(context, dispatch, 'on');
46158};
46159
46160},{"../constants/alignment":152,"../constants/xmlns_namespaces":156,"../lib":177,"d3":13}],199:[function(_dereq_,module,exports){
46161/**
46162* Copyright 2012-2020, Plotly, Inc.
46163* All rights reserved.
46164*
46165* This source code is licensed under the MIT license found in the
46166* LICENSE file in the root directory of this source tree.
46167*/
46168
46169'use strict';
46170
46171var timerCache = {};
46172
46173/**
46174 * Throttle a callback. `callback` executes synchronously only if
46175 * more than `minInterval` milliseconds have already elapsed since the latest
46176 * call (if any). Otherwise we wait until `minInterval` is over and execute the
46177 * last callback received while waiting.
46178 * So the first and last events in a train are always executed (eventually)
46179 * but some of the events in the middle can be dropped.
46180 *
46181 * @param {string} id: an identifier to mark events to throttle together
46182 * @param {number} minInterval: minimum time, in milliseconds, between
46183 * invocations of `callback`
46184 * @param {function} callback: the function to throttle. `callback` itself
46185 * should be a purely synchronous function.
46186 */
46187exports.throttle = function throttle(id, minInterval, callback) {
46188 var cache = timerCache[id];
46189 var now = Date.now();
46190
46191 if(!cache) {
46192 /*
46193 * Throw out old items before making a new one, to prevent the cache
46194 * getting overgrown, for example from old plots that have been replaced.
46195 * 1 minute age is arbitrary.
46196 */
46197 for(var idi in timerCache) {
46198 if(timerCache[idi].ts < now - 60000) {
46199 delete timerCache[idi];
46200 }
46201 }
46202 cache = timerCache[id] = {ts: 0, timer: null};
46203 }
46204
46205 _clearTimeout(cache);
46206
46207 function exec() {
46208 callback();
46209 cache.ts = Date.now();
46210 if(cache.onDone) {
46211 cache.onDone();
46212 cache.onDone = null;
46213 }
46214 }
46215
46216 if(now > cache.ts + minInterval) {
46217 exec();
46218 return;
46219 }
46220
46221 cache.timer = setTimeout(function() {
46222 exec();
46223 cache.timer = null;
46224 }, minInterval);
46225};
46226
46227exports.done = function(id) {
46228 var cache = timerCache[id];
46229 if(!cache || !cache.timer) return Promise.resolve();
46230
46231 return new Promise(function(resolve) {
46232 var previousOnDone = cache.onDone;
46233 cache.onDone = function onDone() {
46234 if(previousOnDone) previousOnDone();
46235 resolve();
46236 cache.onDone = null;
46237 };
46238 });
46239};
46240
46241/**
46242 * Clear the throttle cache for one or all timers
46243 * @param {optional string} id:
46244 * if provided, clear just this timer
46245 * if omitted, clear all timers (mainly useful for testing)
46246 */
46247exports.clear = function(id) {
46248 if(id) {
46249 _clearTimeout(timerCache[id]);
46250 delete timerCache[id];
46251 } else {
46252 for(var idi in timerCache) exports.clear(idi);
46253 }
46254};
46255
46256function _clearTimeout(cache) {
46257 if(cache && cache.timer !== null) {
46258 clearTimeout(cache.timer);
46259 cache.timer = null;
46260 }
46261}
46262
46263},{}],200:[function(_dereq_,module,exports){
46264/**
46265* Copyright 2012-2020, Plotly, Inc.
46266* All rights reserved.
46267*
46268* This source code is licensed under the MIT license found in the
46269* LICENSE file in the root directory of this source tree.
46270*/
46271
46272'use strict';
46273
46274var isNumeric = _dereq_('fast-isnumeric');
46275
46276/**
46277 * convert a linear value into a logged value, folding negative numbers into
46278 * the given range
46279 */
46280module.exports = function toLogRange(val, range) {
46281 if(val > 0) return Math.log(val) / Math.LN10;
46282
46283 // move a negative value reference to a log axis - just put the
46284 // result at the lowest range value on the plot (or if the range also went negative,
46285 // one millionth of the top of the range)
46286 var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10;
46287 if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6;
46288 return newVal;
46289};
46290
46291},{"fast-isnumeric":15}],201:[function(_dereq_,module,exports){
46292/**
46293* Copyright 2012-2020, Plotly, Inc.
46294* All rights reserved.
46295*
46296* This source code is licensed under the MIT license found in the
46297* LICENSE file in the root directory of this source tree.
46298*/
46299
46300'use strict';
46301
46302var topojsonUtils = module.exports = {};
46303
46304var locationmodeToLayer = _dereq_('../plots/geo/constants').locationmodeToLayer;
46305var topojsonFeature = _dereq_('topojson-client').feature;
46306
46307topojsonUtils.getTopojsonName = function(geoLayout) {
46308 return [
46309 geoLayout.scope.replace(/ /g, '-'), '_',
46310 geoLayout.resolution.toString(), 'm'
46311 ].join('');
46312};
46313
46314topojsonUtils.getTopojsonPath = function(topojsonURL, topojsonName) {
46315 return topojsonURL + topojsonName + '.json';
46316};
46317
46318topojsonUtils.getTopojsonFeatures = function(trace, topojson) {
46319 var layer = locationmodeToLayer[trace.locationmode];
46320 var obj = topojson.objects[layer];
46321
46322 return topojsonFeature(topojson, obj).features;
46323};
46324
46325},{"../plots/geo/constants":252,"topojson-client":33}],202:[function(_dereq_,module,exports){
46326/**
46327* Copyright 2012-2020, Plotly, Inc.
46328* All rights reserved.
46329*
46330* This source code is licensed under the MIT license found in the
46331* LICENSE file in the root directory of this source tree.
46332*/
46333
46334'use strict';
46335
46336module.exports = {
46337 moduleType: 'locale',
46338 name: 'en-US',
46339 dictionary: {
46340 'Click to enter Colorscale title': 'Click to enter Colorscale title'
46341 },
46342 format: {
46343 date: '%m/%d/%Y'
46344 }
46345};
46346
46347},{}],203:[function(_dereq_,module,exports){
46348/**
46349* Copyright 2012-2020, Plotly, Inc.
46350* All rights reserved.
46351*
46352* This source code is licensed under the MIT license found in the
46353* LICENSE file in the root directory of this source tree.
46354*/
46355
46356'use strict';
46357
46358module.exports = {
46359 moduleType: 'locale',
46360 name: 'en',
46361 dictionary: {
46362 'Click to enter Colorscale title': 'Click to enter Colourscale title'
46363 },
46364 format: {
46365 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
46366 shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
46367 months: [
46368 'January', 'February', 'March', 'April', 'May', 'June',
46369 'July', 'August', 'September', 'October', 'November', 'December'
46370 ],
46371 shortMonths: [
46372 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
46373 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
46374 ],
46375 periods: ['AM', 'PM'],
46376 dateTime: '%a %b %e %X %Y',
46377 date: '%d/%m/%Y',
46378 time: '%H:%M:%S',
46379 decimal: '.',
46380 thousands: ',',
46381 grouping: [3],
46382 currency: ['$', ''],
46383 year: '%Y',
46384 month: '%b %Y',
46385 dayMonth: '%b %-d',
46386 dayMonthYear: '%b %-d, %Y'
46387 }
46388};
46389
46390},{}],204:[function(_dereq_,module,exports){
46391/**
46392* Copyright 2012-2020, Plotly, Inc.
46393* All rights reserved.
46394*
46395* This source code is licensed under the MIT license found in the
46396* LICENSE file in the root directory of this source tree.
46397*/
46398
46399
46400'use strict';
46401
46402var Registry = _dereq_('../registry');
46403
46404/*
46405 * containerArrayMatch: does this attribute string point into a
46406 * layout container array?
46407 *
46408 * @param {String} astr: an attribute string, like *annotations[2].text*
46409 *
46410 * @returns {Object | false} Returns false if `astr` doesn't match a container
46411 * array. If it does, returns:
46412 * {array: {String}, index: {Number}, property: {String}}
46413 * ie the attribute string for the array, the index within the array (or ''
46414 * if the whole array) and the property within that (or '' if the whole array
46415 * or the whole object)
46416 */
46417module.exports = function containerArrayMatch(astr) {
46418 var rootContainers = Registry.layoutArrayContainers;
46419 var regexpContainers = Registry.layoutArrayRegexes;
46420 var rootPart = astr.split('[')[0];
46421 var arrayStr;
46422 var match;
46423
46424 // look for regexp matches first, because they may be nested inside root matches
46425 // eg updatemenus[i].buttons is nested inside updatemenus
46426 for(var i = 0; i < regexpContainers.length; i++) {
46427 match = astr.match(regexpContainers[i]);
46428 if(match && match.index === 0) {
46429 arrayStr = match[0];
46430 break;
46431 }
46432 }
46433
46434 // now look for root matches
46435 if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)];
46436
46437 if(!arrayStr) return false;
46438
46439 var tail = astr.substr(arrayStr.length);
46440 if(!tail) return {array: arrayStr, index: '', property: ''};
46441
46442 match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/);
46443 if(!match) return false;
46444
46445 return {array: arrayStr, index: Number(match[1]), property: match[3] || ''};
46446};
46447
46448},{"../registry":272}],205:[function(_dereq_,module,exports){
46449/**
46450* Copyright 2012-2020, Plotly, Inc.
46451* All rights reserved.
46452*
46453* This source code is licensed under the MIT license found in the
46454* LICENSE file in the root directory of this source tree.
46455*/
46456
46457'use strict';
46458
46459var Lib = _dereq_('../lib');
46460var extendFlat = Lib.extendFlat;
46461var isPlainObject = Lib.isPlainObject;
46462
46463var traceOpts = {
46464 valType: 'flaglist',
46465 extras: ['none'],
46466 flags: ['calc', 'clearAxisTypes', 'plot', 'style', 'markerSize', 'colorbars'],
46467
46468};
46469
46470var layoutOpts = {
46471 valType: 'flaglist',
46472 extras: ['none'],
46473 flags: [
46474 'calc', 'plot', 'legend', 'ticks', 'axrange',
46475 'layoutstyle', 'modebar', 'camera', 'arraydraw', 'colorbars'
46476 ],
46477
46478};
46479
46480// flags for inside restyle/relayout include a few extras
46481// that shouldn't be used in attributes, to deal with certain
46482// combinations and conditionals efficiently
46483var traceEditTypeFlags = traceOpts.flags.slice()
46484 .concat(['fullReplot']);
46485
46486var layoutEditTypeFlags = layoutOpts.flags.slice()
46487 .concat('layoutReplot');
46488
46489module.exports = {
46490 traces: traceOpts,
46491 layout: layoutOpts,
46492 /*
46493 * default (all false) edit flags for restyle (traces)
46494 * creates a new object each call, so the caller can mutate freely
46495 */
46496 traceFlags: function() { return falseObj(traceEditTypeFlags); },
46497
46498 /*
46499 * default (all false) edit flags for relayout
46500 * creates a new object each call, so the caller can mutate freely
46501 */
46502 layoutFlags: function() { return falseObj(layoutEditTypeFlags); },
46503
46504 /*
46505 * update `flags` with the `editType` values found in `attr`
46506 */
46507 update: function(flags, attr) {
46508 var editType = attr.editType;
46509 if(editType && editType !== 'none') {
46510 var editTypeParts = editType.split('+');
46511 for(var i = 0; i < editTypeParts.length; i++) {
46512 flags[editTypeParts[i]] = true;
46513 }
46514 }
46515 },
46516
46517 overrideAll: overrideAll
46518};
46519
46520function falseObj(keys) {
46521 var out = {};
46522 for(var i = 0; i < keys.length; i++) out[keys[i]] = false;
46523 return out;
46524}
46525
46526/**
46527 * For attributes that are largely copied from elsewhere into a plot type that doesn't
46528 * support partial redraws - overrides the editType field of all attributes in the object
46529 *
46530 * @param {object} attrs: the attributes to override. Will not be mutated.
46531 * @param {string} editTypeOverride: the new editType to use
46532 * @param {'nested'|'from-root'} overrideContainers:
46533 * - 'nested' will override editType for nested containers but not the root.
46534 * - 'from-root' will also override editType of the root container.
46535 * Containers below the absolute top level (trace or layout root) DO need an
46536 * editType even if they are not `valObject`s themselves (eg `scatter.marker`)
46537 * to handle the case where you edit the whole container.
46538 *
46539 * @return {object} a new attributes object with `editType` modified as directed
46540 */
46541function overrideAll(attrs, editTypeOverride, overrideContainers) {
46542 var out = extendFlat({}, attrs);
46543 for(var key in out) {
46544 var attr = out[key];
46545 if(isPlainObject(attr)) {
46546 out[key] = overrideOne(attr, editTypeOverride, overrideContainers, key);
46547 }
46548 }
46549 if(overrideContainers === 'from-root') out.editType = editTypeOverride;
46550
46551 return out;
46552}
46553
46554function overrideOne(attr, editTypeOverride, overrideContainers, key) {
46555 if(attr.valType) {
46556 var out = extendFlat({}, attr);
46557 out.editType = editTypeOverride;
46558
46559 if(Array.isArray(attr.items)) {
46560 out.items = new Array(attr.items.length);
46561 for(var i = 0; i < attr.items.length; i++) {
46562 out.items[i] = overrideOne(attr.items[i], editTypeOverride, 'from-root');
46563 }
46564 }
46565 return out;
46566 } else {
46567 // don't provide an editType for the _deprecated container
46568 return overrideAll(attr, editTypeOverride,
46569 (key.charAt(0) === '_') ? 'nested' : 'from-root');
46570 }
46571}
46572
46573},{"../lib":177}],206:[function(_dereq_,module,exports){
46574/**
46575* Copyright 2012-2020, Plotly, Inc.
46576* All rights reserved.
46577*
46578* This source code is licensed under the MIT license found in the
46579* LICENSE file in the root directory of this source tree.
46580*/
46581
46582'use strict';
46583
46584var isNumeric = _dereq_('fast-isnumeric');
46585var m4FromQuat = _dereq_('gl-mat4/fromQuat');
46586
46587var Registry = _dereq_('../registry');
46588var Lib = _dereq_('../lib');
46589var Plots = _dereq_('../plots/plots');
46590var AxisIds = _dereq_('../plots/cartesian/axis_ids');
46591var Color = _dereq_('../components/color');
46592
46593var cleanId = AxisIds.cleanId;
46594var getFromTrace = AxisIds.getFromTrace;
46595var traceIs = Registry.traceIs;
46596
46597// clear the promise queue if one of them got rejected
46598exports.clearPromiseQueue = function(gd) {
46599 if(Array.isArray(gd._promises) && gd._promises.length > 0) {
46600 Lib.log('Clearing previous rejected promises from queue.');
46601 }
46602
46603 gd._promises = [];
46604};
46605
46606// make a few changes to the layout right away
46607// before it gets used for anything
46608// backward compatibility and cleanup of nonstandard options
46609exports.cleanLayout = function(layout) {
46610 var i, j;
46611
46612 if(!layout) layout = {};
46613
46614 // cannot have (x|y)axis1, numbering goes axis, axis2, axis3...
46615 if(layout.xaxis1) {
46616 if(!layout.xaxis) layout.xaxis = layout.xaxis1;
46617 delete layout.xaxis1;
46618 }
46619 if(layout.yaxis1) {
46620 if(!layout.yaxis) layout.yaxis = layout.yaxis1;
46621 delete layout.yaxis1;
46622 }
46623 if(layout.scene1) {
46624 if(!layout.scene) layout.scene = layout.scene1;
46625 delete layout.scene1;
46626 }
46627
46628 var axisAttrRegex = (Plots.subplotsRegistry.cartesian || {}).attrRegex;
46629 var polarAttrRegex = (Plots.subplotsRegistry.polar || {}).attrRegex;
46630 var ternaryAttrRegex = (Plots.subplotsRegistry.ternary || {}).attrRegex;
46631 var sceneAttrRegex = (Plots.subplotsRegistry.gl3d || {}).attrRegex;
46632
46633 var keys = Object.keys(layout);
46634 for(i = 0; i < keys.length; i++) {
46635 var key = keys[i];
46636
46637 if(axisAttrRegex && axisAttrRegex.test(key)) {
46638 // modifications to cartesian axes
46639
46640 var ax = layout[key];
46641 if(ax.anchor && ax.anchor !== 'free') {
46642 ax.anchor = cleanId(ax.anchor);
46643 }
46644 if(ax.overlaying) ax.overlaying = cleanId(ax.overlaying);
46645
46646 // old method of axis type - isdate and islog (before category existed)
46647 if(!ax.type) {
46648 if(ax.isdate) ax.type = 'date';
46649 else if(ax.islog) ax.type = 'log';
46650 else if(ax.isdate === false && ax.islog === false) ax.type = 'linear';
46651 }
46652 if(ax.autorange === 'withzero' || ax.autorange === 'tozero') {
46653 ax.autorange = true;
46654 ax.rangemode = 'tozero';
46655 }
46656 delete ax.islog;
46657 delete ax.isdate;
46658 delete ax.categories; // replaced by _categories
46659
46660 // prune empty domain arrays made before the new nestedProperty
46661 if(emptyContainer(ax, 'domain')) delete ax.domain;
46662
46663 // autotick -> tickmode
46664 if(ax.autotick !== undefined) {
46665 if(ax.tickmode === undefined) {
46666 ax.tickmode = ax.autotick ? 'auto' : 'linear';
46667 }
46668 delete ax.autotick;
46669 }
46670
46671 cleanTitle(ax);
46672 } else if(polarAttrRegex && polarAttrRegex.test(key)) {
46673 // modifications for polar
46674
46675 var polar = layout[key];
46676 cleanTitle(polar.radialaxis);
46677 } else if(ternaryAttrRegex && ternaryAttrRegex.test(key)) {
46678 // modifications for ternary
46679
46680 var ternary = layout[key];
46681 cleanTitle(ternary.aaxis);
46682 cleanTitle(ternary.baxis);
46683 cleanTitle(ternary.caxis);
46684 } else if(sceneAttrRegex && sceneAttrRegex.test(key)) {
46685 // modifications for 3D scenes
46686
46687 var scene = layout[key];
46688
46689 // clean old Camera coords
46690 var cameraposition = scene.cameraposition;
46691
46692 if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
46693 var rotation = cameraposition[0];
46694 var center = cameraposition[1];
46695 var radius = cameraposition[2];
46696 var mat = m4FromQuat([], rotation);
46697 var eye = [];
46698
46699 for(j = 0; j < 3; ++j) {
46700 eye[j] = center[j] + radius * mat[2 + 4 * j];
46701 }
46702
46703 scene.camera = {
46704 eye: {x: eye[0], y: eye[1], z: eye[2]},
46705 center: {x: center[0], y: center[1], z: center[2]},
46706 up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case
46707 };
46708
46709 delete scene.cameraposition;
46710 }
46711
46712 // clean axis titles
46713 cleanTitle(scene.xaxis);
46714 cleanTitle(scene.yaxis);
46715 cleanTitle(scene.zaxis);
46716 }
46717 }
46718
46719 var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
46720 for(i = 0; i < annotationsLen; i++) {
46721 var ann = layout.annotations[i];
46722
46723 if(!Lib.isPlainObject(ann)) continue;
46724
46725 if(ann.ref) {
46726 if(ann.ref === 'paper') {
46727 ann.xref = 'paper';
46728 ann.yref = 'paper';
46729 } else if(ann.ref === 'data') {
46730 ann.xref = 'x';
46731 ann.yref = 'y';
46732 }
46733 delete ann.ref;
46734 }
46735
46736 cleanAxRef(ann, 'xref');
46737 cleanAxRef(ann, 'yref');
46738 }
46739
46740 var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0;
46741 for(i = 0; i < shapesLen; i++) {
46742 var shape = layout.shapes[i];
46743
46744 if(!Lib.isPlainObject(shape)) continue;
46745
46746 cleanAxRef(shape, 'xref');
46747 cleanAxRef(shape, 'yref');
46748 }
46749
46750 var legend = layout.legend;
46751 if(legend) {
46752 // check for old-style legend positioning (x or y is +/- 100)
46753 if(legend.x > 3) {
46754 legend.x = 1.02;
46755 legend.xanchor = 'left';
46756 } else if(legend.x < -2) {
46757 legend.x = -0.02;
46758 legend.xanchor = 'right';
46759 }
46760
46761 if(legend.y > 3) {
46762 legend.y = 1.02;
46763 legend.yanchor = 'bottom';
46764 } else if(legend.y < -2) {
46765 legend.y = -0.02;
46766 legend.yanchor = 'top';
46767 }
46768 }
46769
46770 // clean plot title
46771 cleanTitle(layout);
46772
46773 /*
46774 * Moved from rotate -> orbit for dragmode
46775 */
46776 if(layout.dragmode === 'rotate') layout.dragmode = 'orbit';
46777
46778 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
46779 // supported, but new tinycolor does not because they're not valid css
46780 Color.clean(layout);
46781
46782 // clean the layout container in layout.template
46783 if(layout.template && layout.template.layout) {
46784 exports.cleanLayout(layout.template.layout);
46785 }
46786
46787 return layout;
46788};
46789
46790function cleanAxRef(container, attr) {
46791 var valIn = container[attr];
46792 var axLetter = attr.charAt(0);
46793 if(valIn && valIn !== 'paper') {
46794 container[attr] = cleanId(valIn, axLetter);
46795 }
46796}
46797
46798/**
46799 * Cleans up old title attribute structure (flat) in favor of the new one (nested).
46800 *
46801 * @param {Object} titleContainer - an object potentially including deprecated title attributes
46802 */
46803function cleanTitle(titleContainer) {
46804 if(titleContainer) {
46805 // title -> title.text
46806 // (although title used to be a string attribute,
46807 // numbers are accepted as well)
46808 if(typeof titleContainer.title === 'string' || typeof titleContainer.title === 'number') {
46809 titleContainer.title = {
46810 text: titleContainer.title
46811 };
46812 }
46813
46814 rewireAttr('titlefont', 'font');
46815 rewireAttr('titleposition', 'position');
46816 rewireAttr('titleside', 'side');
46817 rewireAttr('titleoffset', 'offset');
46818 }
46819
46820 function rewireAttr(oldAttrName, newAttrName) {
46821 var oldAttrSet = titleContainer[oldAttrName];
46822 var newAttrSet = titleContainer.title && titleContainer.title[newAttrName];
46823
46824 if(oldAttrSet && !newAttrSet) {
46825 // Ensure title object exists
46826 if(!titleContainer.title) {
46827 titleContainer.title = {};
46828 }
46829
46830 titleContainer.title[newAttrName] = titleContainer[oldAttrName];
46831 delete titleContainer[oldAttrName];
46832 }
46833 }
46834}
46835
46836/*
46837 * cleanData: Make a few changes to the data for backward compatibility
46838 * before it gets used for anything. Modifies the data traces users provide.
46839 *
46840 * Important: if you're going to add something here that modifies a data array,
46841 * update it in place so the new array === the old one.
46842 */
46843exports.cleanData = function(data) {
46844 for(var tracei = 0; tracei < data.length; tracei++) {
46845 var trace = data[tracei];
46846 var i;
46847
46848 // use xbins to bin data in x, and ybins to bin data in y
46849 if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) {
46850 trace.ybins = trace.xbins;
46851 delete trace.xbins;
46852 }
46853
46854 // error_y.opacity is obsolete - merge into color
46855 if(trace.error_y && 'opacity' in trace.error_y) {
46856 var dc = Color.defaults;
46857 var yeColor = trace.error_y.color || (traceIs(trace, 'bar') ?
46858 Color.defaultLine :
46859 dc[tracei % dc.length]);
46860 trace.error_y.color = Color.addOpacity(
46861 Color.rgb(yeColor),
46862 Color.opacity(yeColor) * trace.error_y.opacity);
46863 delete trace.error_y.opacity;
46864 }
46865
46866 // convert bardir to orientation, and put the data into
46867 // the axes it's eventually going to be used with
46868 if('bardir' in trace) {
46869 if(trace.bardir === 'h' && (traceIs(trace, 'bar') ||
46870 trace.type.substr(0, 9) === 'histogram')) {
46871 trace.orientation = 'h';
46872 exports.swapXYData(trace);
46873 }
46874 delete trace.bardir;
46875 }
46876
46877 // now we have only one 1D histogram type, and whether
46878 // it uses x or y data depends on trace.orientation
46879 if(trace.type === 'histogramy') exports.swapXYData(trace);
46880 if(trace.type === 'histogramx' || trace.type === 'histogramy') {
46881 trace.type = 'histogram';
46882 }
46883
46884 // scl->scale, reversescl->reversescale
46885 if('scl' in trace && !('colorscale' in trace)) {
46886 trace.colorscale = trace.scl;
46887 delete trace.scl;
46888 }
46889 if('reversescl' in trace && !('reversescale' in trace)) {
46890 trace.reversescale = trace.reversescl;
46891 delete trace.reversescl;
46892 }
46893
46894 // axis ids x1 -> x, y1-> y
46895 if(trace.xaxis) trace.xaxis = cleanId(trace.xaxis, 'x');
46896 if(trace.yaxis) trace.yaxis = cleanId(trace.yaxis, 'y');
46897
46898 // scene ids scene1 -> scene
46899 if(traceIs(trace, 'gl3d') && trace.scene) {
46900 trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
46901 }
46902
46903 if(!traceIs(trace, 'pie-like') && !traceIs(trace, 'bar-like')) {
46904 if(Array.isArray(trace.textposition)) {
46905 for(i = 0; i < trace.textposition.length; i++) {
46906 trace.textposition[i] = cleanTextPosition(trace.textposition[i]);
46907 }
46908 } else if(trace.textposition) {
46909 trace.textposition = cleanTextPosition(trace.textposition);
46910 }
46911 }
46912
46913 // fix typo in colorscale definition
46914 var _module = Registry.getModule(trace);
46915 if(_module && _module.colorbar) {
46916 var containerName = _module.colorbar.container;
46917 var container = containerName ? trace[containerName] : trace;
46918 if(container && container.colorscale) {
46919 if(container.colorscale === 'YIGnBu') container.colorscale = 'YlGnBu';
46920 if(container.colorscale === 'YIOrRd') container.colorscale = 'YlOrRd';
46921 }
46922 }
46923
46924 // fix typo in surface 'highlight*' definitions
46925 if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) {
46926 var dims = ['x', 'y', 'z'];
46927
46928 for(i = 0; i < dims.length; i++) {
46929 var opts = trace.contours[dims[i]];
46930
46931 if(!Lib.isPlainObject(opts)) continue;
46932
46933 if(opts.highlightColor) {
46934 opts.highlightcolor = opts.highlightColor;
46935 delete opts.highlightColor;
46936 }
46937
46938 if(opts.highlightWidth) {
46939 opts.highlightwidth = opts.highlightWidth;
46940 delete opts.highlightWidth;
46941 }
46942 }
46943 }
46944
46945 // fixes from converting finance from transforms to real trace types
46946 if(trace.type === 'candlestick' || trace.type === 'ohlc') {
46947 var increasingShowlegend = (trace.increasing || {}).showlegend !== false;
46948 var decreasingShowlegend = (trace.decreasing || {}).showlegend !== false;
46949 var increasingName = cleanFinanceDir(trace.increasing);
46950 var decreasingName = cleanFinanceDir(trace.decreasing);
46951
46952 // now figure out something smart to do with the separate direction
46953 // names we removed
46954 if((increasingName !== false) && (decreasingName !== false)) {
46955 // both sub-names existed: base name previously had no effect
46956 // so ignore it and try to find a shared part of the sub-names
46957
46958 var newName = commonPrefix(
46959 increasingName, decreasingName,
46960 increasingShowlegend, decreasingShowlegend
46961 );
46962 // if no common part, leave whatever name was (or wasn't) there
46963 if(newName) trace.name = newName;
46964 } else if((increasingName || decreasingName) && !trace.name) {
46965 // one sub-name existed but not the base name - just use the sub-name
46966 trace.name = increasingName || decreasingName;
46967 }
46968 }
46969
46970 // transforms backward compatibility fixes
46971 if(Array.isArray(trace.transforms)) {
46972 var transforms = trace.transforms;
46973
46974 for(i = 0; i < transforms.length; i++) {
46975 var transform = transforms[i];
46976
46977 if(!Lib.isPlainObject(transform)) continue;
46978
46979 switch(transform.type) {
46980 case 'filter':
46981 if(transform.filtersrc) {
46982 transform.target = transform.filtersrc;
46983 delete transform.filtersrc;
46984 }
46985
46986 if(transform.calendar) {
46987 if(!transform.valuecalendar) {
46988 transform.valuecalendar = transform.calendar;
46989 }
46990 delete transform.calendar;
46991 }
46992 break;
46993
46994 case 'groupby':
46995 // Name has changed from `style` to `styles`, so use `style` but prefer `styles`:
46996 transform.styles = transform.styles || transform.style;
46997
46998 if(transform.styles && !Array.isArray(transform.styles)) {
46999 var prevStyles = transform.styles;
47000 var styleKeys = Object.keys(prevStyles);
47001
47002 transform.styles = [];
47003 for(var j = 0; j < styleKeys.length; j++) {
47004 transform.styles.push({
47005 target: styleKeys[j],
47006 value: prevStyles[styleKeys[j]]
47007 });
47008 }
47009 }
47010 break;
47011 }
47012 }
47013 }
47014
47015 // prune empty containers made before the new nestedProperty
47016 if(emptyContainer(trace, 'line')) delete trace.line;
47017 if('marker' in trace) {
47018 if(emptyContainer(trace.marker, 'line')) delete trace.marker.line;
47019 if(emptyContainer(trace, 'marker')) delete trace.marker;
47020 }
47021
47022 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
47023 // supported, but new tinycolor does not because they're not valid css
47024 Color.clean(trace);
47025
47026 // remove obsolete autobin(x|y) attributes, but only if true
47027 // if false, this needs to happen in Histogram.calc because it
47028 // can be a one-time autobin so we need to know the results before
47029 // we can push them back into the trace.
47030 if(trace.autobinx) {
47031 delete trace.autobinx;
47032 delete trace.xbins;
47033 }
47034 if(trace.autobiny) {
47035 delete trace.autobiny;
47036 delete trace.ybins;
47037 }
47038
47039 cleanTitle(trace);
47040 if(trace.colorbar) cleanTitle(trace.colorbar);
47041 if(trace.marker && trace.marker.colorbar) cleanTitle(trace.marker.colorbar);
47042 if(trace.line && trace.line.colorbar) cleanTitle(trace.line.colorbar);
47043 if(trace.aaxis) cleanTitle(trace.aaxis);
47044 if(trace.baxis) cleanTitle(trace.baxis);
47045 }
47046};
47047
47048function cleanFinanceDir(dirContainer) {
47049 if(!Lib.isPlainObject(dirContainer)) return false;
47050
47051 var dirName = dirContainer.name;
47052
47053 delete dirContainer.name;
47054 delete dirContainer.showlegend;
47055
47056 return (typeof dirName === 'string' || typeof dirName === 'number') && String(dirName);
47057}
47058
47059function commonPrefix(name1, name2, show1, show2) {
47060 // if only one is shown in the legend, use that
47061 if(show1 && !show2) return name1;
47062 if(show2 && !show1) return name2;
47063
47064 // if both or neither are in the legend, check if one is blank (or whitespace)
47065 // and use the other one
47066 // note that hover labels can still use the name even if the legend doesn't
47067 if(!name1.trim()) return name2;
47068 if(!name2.trim()) return name1;
47069
47070 var minLen = Math.min(name1.length, name2.length);
47071 var i;
47072 for(i = 0; i < minLen; i++) {
47073 if(name1.charAt(i) !== name2.charAt(i)) break;
47074 }
47075
47076 var out = name1.substr(0, i);
47077 return out.trim();
47078}
47079
47080// textposition - support partial attributes (ie just 'top')
47081// and incorrect use of middle / center etc.
47082function cleanTextPosition(textposition) {
47083 var posY = 'middle';
47084 var posX = 'center';
47085
47086 if(typeof textposition === 'string') {
47087 if(textposition.indexOf('top') !== -1) posY = 'top';
47088 else if(textposition.indexOf('bottom') !== -1) posY = 'bottom';
47089
47090 if(textposition.indexOf('left') !== -1) posX = 'left';
47091 else if(textposition.indexOf('right') !== -1) posX = 'right';
47092 }
47093
47094 return posY + ' ' + posX;
47095}
47096
47097function emptyContainer(outer, innerStr) {
47098 return (innerStr in outer) &&
47099 (typeof outer[innerStr] === 'object') &&
47100 (Object.keys(outer[innerStr]).length === 0);
47101}
47102
47103
47104// swap all the data and data attributes associated with x and y
47105exports.swapXYData = function(trace) {
47106 var i;
47107 Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']);
47108 if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
47109 if(trace.transpose) delete trace.transpose;
47110 else trace.transpose = true;
47111 }
47112 if(trace.error_x && trace.error_y) {
47113 var errorY = trace.error_y;
47114 var copyYstyle = ('copy_ystyle' in errorY) ?
47115 errorY.copy_ystyle :
47116 !(errorY.color || errorY.thickness || errorY.width);
47117 Lib.swapAttrs(trace, ['error_?.copy_ystyle']);
47118 if(copyYstyle) {
47119 Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
47120 }
47121 }
47122 if(typeof trace.hoverinfo === 'string') {
47123 var hoverInfoParts = trace.hoverinfo.split('+');
47124 for(i = 0; i < hoverInfoParts.length; i++) {
47125 if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';
47126 else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x';
47127 }
47128 trace.hoverinfo = hoverInfoParts.join('+');
47129 }
47130};
47131
47132// coerce traceIndices input to array of trace indices
47133exports.coerceTraceIndices = function(gd, traceIndices) {
47134 if(isNumeric(traceIndices)) {
47135 return [traceIndices];
47136 } else if(!Array.isArray(traceIndices) || !traceIndices.length) {
47137 return gd.data.map(function(_, i) { return i; });
47138 } else if(Array.isArray(traceIndices)) {
47139 var traceIndicesOut = [];
47140 for(var i = 0; i < traceIndices.length; i++) {
47141 if(Lib.isIndex(traceIndices[i], gd.data.length)) {
47142 traceIndicesOut.push(traceIndices[i]);
47143 } else {
47144 Lib.warn('trace index (', traceIndices[i], ') is not a number or is out of bounds');
47145 }
47146 }
47147 return traceIndicesOut;
47148 }
47149
47150 return traceIndices;
47151};
47152
47153/**
47154 * Manages logic around array container item creation / deletion / update
47155 * that nested property alone can't handle.
47156 *
47157 * @param {Object} np
47158 * nested property of update attribute string about trace or layout object
47159 * @param {*} newVal
47160 * update value passed to restyle / relayout / update
47161 * @param {Object} undoit
47162 * undo hash (N.B. undoit may be mutated here).
47163 *
47164 */
47165exports.manageArrayContainers = function(np, newVal, undoit) {
47166 var obj = np.obj;
47167 var parts = np.parts;
47168 var pLength = parts.length;
47169 var pLast = parts[pLength - 1];
47170
47171 var pLastIsNumber = isNumeric(pLast);
47172
47173 if(pLastIsNumber && newVal === null) {
47174 // delete item
47175
47176 // Clear item in array container when new value is null
47177 var contPath = parts.slice(0, pLength - 1).join('.');
47178 var cont = Lib.nestedProperty(obj, contPath).get();
47179 cont.splice(pLast, 1);
47180
47181 // Note that nested property clears null / undefined at end of
47182 // array container, but not within them.
47183 } else if(pLastIsNumber && np.get() === undefined) {
47184 // create item
47185
47186 // When adding a new item, make sure undo command will remove it
47187 if(np.get() === undefined) undoit[np.astr] = null;
47188
47189 np.set(newVal);
47190 } else {
47191 // update item
47192
47193 // If the last part of attribute string isn't a number,
47194 // np.set is all we need.
47195 np.set(newVal);
47196 }
47197};
47198
47199/*
47200 * Match the part to strip off to turn an attribute into its parent
47201 * really it should be either '.some_characters' or '[number]'
47202 * but we're a little more permissive here and match either
47203 * '.not_brackets_or_dot' or '[not_brackets_or_dot]'
47204 */
47205var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/;
47206
47207function getParent(attr) {
47208 var tail = attr.search(ATTR_TAIL_RE);
47209 if(tail > 0) return attr.substr(0, tail);
47210}
47211
47212/*
47213 * hasParent: does an attribute object contain a parent of the given attribute?
47214 * for example, given 'images[2].x' do we also have 'images' or 'images[2]'?
47215 *
47216 * @param {Object} aobj
47217 * update object, whose keys are attribute strings and values are their new settings
47218 * @param {string} attr
47219 * the attribute string to test against
47220 * @returns {Boolean}
47221 * is a parent of attr present in aobj?
47222 */
47223exports.hasParent = function(aobj, attr) {
47224 var attrParent = getParent(attr);
47225 while(attrParent) {
47226 if(attrParent in aobj) return true;
47227 attrParent = getParent(attrParent);
47228 }
47229 return false;
47230};
47231
47232/**
47233 * Empty out types for all axes containing these traces so we auto-set them again
47234 *
47235 * @param {object} gd
47236 * @param {[integer]} traces: trace indices to search for axes to clear the types of
47237 * @param {object} layoutUpdate: any update being done concurrently to the layout,
47238 * which may supercede clearing the axis types
47239 */
47240var axLetters = ['x', 'y', 'z'];
47241exports.clearAxisTypes = function(gd, traces, layoutUpdate) {
47242 for(var i = 0; i < traces.length; i++) {
47243 var trace = gd._fullData[i];
47244 for(var j = 0; j < 3; j++) {
47245 var ax = getFromTrace(gd, trace, axLetters[j]);
47246
47247 // do not clear log type - that's never an auto result so must have been intentional
47248 if(ax && ax.type !== 'log') {
47249 var axAttr = ax._name;
47250 var sceneName = ax._id.substr(1);
47251 if(sceneName.substr(0, 5) === 'scene') {
47252 if(layoutUpdate[sceneName] !== undefined) continue;
47253 axAttr = sceneName + '.' + axAttr;
47254 }
47255 var typeAttr = axAttr + '.type';
47256
47257 if(layoutUpdate[axAttr] === undefined && layoutUpdate[typeAttr] === undefined) {
47258 Lib.nestedProperty(gd.layout, typeAttr).set(null);
47259 }
47260 }
47261 }
47262 }
47263};
47264
47265},{"../components/color":50,"../lib":177,"../plots/cartesian/axis_ids":225,"../plots/plots":263,"../registry":272,"fast-isnumeric":15,"gl-mat4/fromQuat":16}],207:[function(_dereq_,module,exports){
47266/**
47267* Copyright 2012-2020, Plotly, Inc.
47268* All rights reserved.
47269*
47270* This source code is licensed under the MIT license found in the
47271* LICENSE file in the root directory of this source tree.
47272*/
47273
47274'use strict';
47275
47276var main = _dereq_('./plot_api');
47277
47278exports.plot = main.plot;
47279exports.newPlot = main.newPlot;
47280exports.restyle = main.restyle;
47281exports.relayout = main.relayout;
47282exports.redraw = main.redraw;
47283exports.update = main.update;
47284exports._guiRestyle = main._guiRestyle;
47285exports._guiRelayout = main._guiRelayout;
47286exports._guiUpdate = main._guiUpdate;
47287exports._storeDirectGUIEdit = main._storeDirectGUIEdit;
47288exports.react = main.react;
47289exports.extendTraces = main.extendTraces;
47290exports.prependTraces = main.prependTraces;
47291exports.addTraces = main.addTraces;
47292exports.deleteTraces = main.deleteTraces;
47293exports.moveTraces = main.moveTraces;
47294exports.purge = main.purge;
47295exports.addFrames = main.addFrames;
47296exports.deleteFrames = main.deleteFrames;
47297exports.animate = main.animate;
47298exports.setPlotConfig = main.setPlotConfig;
47299
47300exports.toImage = _dereq_('./to_image');
47301exports.validate = _dereq_('./validate');
47302exports.downloadImage = _dereq_('../snapshot/download');
47303
47304var templateApi = _dereq_('./template_api');
47305exports.makeTemplate = templateApi.makeTemplate;
47306exports.validateTemplate = templateApi.validateTemplate;
47307
47308},{"../snapshot/download":274,"./plot_api":209,"./template_api":214,"./to_image":215,"./validate":216}],208:[function(_dereq_,module,exports){
47309/**
47310* Copyright 2012-2020, Plotly, Inc.
47311* All rights reserved.
47312*
47313* This source code is licensed under the MIT license found in the
47314* LICENSE file in the root directory of this source tree.
47315*/
47316
47317
47318'use strict';
47319
47320var isPlainObject = _dereq_('../lib/is_plain_object');
47321var noop = _dereq_('../lib/noop');
47322var Loggers = _dereq_('../lib/loggers');
47323var sorterAsc = _dereq_('../lib/search').sorterAsc;
47324var Registry = _dereq_('../registry');
47325
47326
47327exports.containerArrayMatch = _dereq_('./container_array_match');
47328
47329var isAddVal = exports.isAddVal = function isAddVal(val) {
47330 return val === 'add' || isPlainObject(val);
47331};
47332
47333var isRemoveVal = exports.isRemoveVal = function isRemoveVal(val) {
47334 return val === null || val === 'remove';
47335};
47336
47337/*
47338 * applyContainerArrayChanges: for managing arrays of layout components in relayout
47339 * handles them all with a consistent interface.
47340 *
47341 * Here are the supported actions -> relayout calls -> edits we get here
47342 * (as prepared in _relayout):
47343 *
47344 * add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}}
47345 * add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}}
47346 * delete an obj -> {'annotations[2]': 'remove'} -> {2: {'': 'remove'}}
47347 * -> {'annotations[2]': null} -> {2: {'': null}}
47348 * delete the whole array -> {'annotations': 'remove'} -> {'': {'': 'remove'}}
47349 * -> {'annotations': null} -> {'': {'': null}}
47350 * edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}}
47351 *
47352 * You can combine many edits to different objects. Objects are added and edited
47353 * in ascending order, then removed in descending order.
47354 * For example, starting with [a, b, c], if you want to:
47355 * - replace b with d:
47356 * {'annotations[1]': d, 'annotations[2]': null} (b is item 2 after adding d)
47357 * - add a new item d between a and b, and edit b:
47358 * {'annotations[1]': d, 'annotations[2].x': newX} (b is item 2 after adding d)
47359 * - delete b and edit c:
47360 * {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed)
47361 *
47362 * You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i`
47363 * You CANNOT combine replacing/deleting the whole array with anything else (for the same array).
47364 *
47365 * @param {HTMLDivElement} gd
47366 * the DOM element of the graph container div
47367 * @param {Lib.nestedProperty} componentType: the array we are editing
47368 * @param {Object} edits
47369 * the changes to make; keys are indices to edit, values are themselves objects:
47370 * {attr: newValue} of changes to make to that index (with add/remove behavior
47371 * in special values of the empty attr)
47372 * @param {Object} flags
47373 * the flags for which actions we're going to perform to display these (and
47374 * any other) changes. If we're already `recalc`ing, we don't need to redraw
47375 * individual items
47376 * @param {function} _nestedProperty
47377 * a (possibly modified for gui edits) nestedProperty constructor
47378 * The modified version takes a 3rd argument, for a prefix to the attribute
47379 * string necessary for storing GUI edits
47380 *
47381 * @returns {bool} `true` if it managed to complete drawing of the changes
47382 * `false` would mean the parent should replot.
47383 */
47384exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, edits, flags, _nestedProperty) {
47385 var componentType = np.astr;
47386 var supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults');
47387 var draw = Registry.getComponentMethod(componentType, 'draw');
47388 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
47389 var replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) || (draw === noop);
47390 var layout = gd.layout;
47391 var fullLayout = gd._fullLayout;
47392
47393 if(edits['']) {
47394 if(Object.keys(edits).length > 1) {
47395 Loggers.warn('Full array edits are incompatible with other edits',
47396 componentType);
47397 }
47398
47399 var fullVal = edits[''][''];
47400
47401 if(isRemoveVal(fullVal)) np.set(null);
47402 else if(Array.isArray(fullVal)) np.set(fullVal);
47403 else {
47404 Loggers.warn('Unrecognized full array edit value', componentType, fullVal);
47405 return true;
47406 }
47407
47408 if(replotLater) return false;
47409
47410 supplyComponentDefaults(layout, fullLayout);
47411 draw(gd);
47412 return true;
47413 }
47414
47415 var componentNums = Object.keys(edits).map(Number).sort(sorterAsc);
47416 var componentArrayIn = np.get();
47417 var componentArray = componentArrayIn || [];
47418 // componentArrayFull is used just to keep splices in line between
47419 // full and input arrays, so private keys can be copied over after
47420 // redoing supplyDefaults
47421 // TODO: this assumes componentArray is in gd.layout - which will not be
47422 // true after we extend this to restyle
47423 var componentArrayFull = _nestedProperty(fullLayout, componentType).get();
47424
47425 var deletes = [];
47426 var firstIndexChange = -1;
47427 var maxIndex = componentArray.length;
47428 var i;
47429 var j;
47430 var componentNum;
47431 var objEdits;
47432 var objKeys;
47433 var objVal;
47434 var adding, prefix;
47435
47436 // first make the add and edit changes
47437 for(i = 0; i < componentNums.length; i++) {
47438 componentNum = componentNums[i];
47439 objEdits = edits[componentNum];
47440 objKeys = Object.keys(objEdits);
47441 objVal = objEdits[''],
47442 adding = isAddVal(objVal);
47443
47444 if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) {
47445 Loggers.warn('index out of range', componentType, componentNum);
47446 continue;
47447 }
47448
47449 if(objVal !== undefined) {
47450 if(objKeys.length > 1) {
47451 Loggers.warn(
47452 'Insertion & removal are incompatible with edits to the same index.',
47453 componentType, componentNum);
47454 }
47455
47456 if(isRemoveVal(objVal)) {
47457 deletes.push(componentNum);
47458 } else if(adding) {
47459 if(objVal === 'add') objVal = {};
47460 componentArray.splice(componentNum, 0, objVal);
47461 if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {});
47462 } else {
47463 Loggers.warn('Unrecognized full object edit value',
47464 componentType, componentNum, objVal);
47465 }
47466
47467 if(firstIndexChange === -1) firstIndexChange = componentNum;
47468 } else {
47469 for(j = 0; j < objKeys.length; j++) {
47470 prefix = componentType + '[' + componentNum + '].';
47471 _nestedProperty(componentArray[componentNum], objKeys[j], prefix)
47472 .set(objEdits[objKeys[j]]);
47473 }
47474 }
47475 }
47476
47477 // now do deletes
47478 for(i = deletes.length - 1; i >= 0; i--) {
47479 componentArray.splice(deletes[i], 1);
47480 // TODO: this drops private keys that had been stored in componentArrayFull
47481 // does this have any ill effects?
47482 if(componentArrayFull) componentArrayFull.splice(deletes[i], 1);
47483 }
47484
47485 if(!componentArray.length) np.set(null);
47486 else if(!componentArrayIn) np.set(componentArray);
47487
47488 if(replotLater) return false;
47489
47490 supplyComponentDefaults(layout, fullLayout);
47491
47492 // finally draw all the components we need to
47493 // if we added or removed any, redraw all after it
47494 if(drawOne !== noop) {
47495 var indicesToDraw;
47496 if(firstIndexChange === -1) {
47497 // there's no re-indexing to do, so only redraw components that changed
47498 indicesToDraw = componentNums;
47499 } else {
47500 // in case the component array was shortened, we still need do call
47501 // drawOne on the latter items so they get properly removed
47502 maxIndex = Math.max(componentArray.length, maxIndex);
47503 indicesToDraw = [];
47504 for(i = 0; i < componentNums.length; i++) {
47505 componentNum = componentNums[i];
47506 if(componentNum >= firstIndexChange) break;
47507 indicesToDraw.push(componentNum);
47508 }
47509 for(i = firstIndexChange; i < maxIndex; i++) {
47510 indicesToDraw.push(i);
47511 }
47512 }
47513 for(i = 0; i < indicesToDraw.length; i++) {
47514 drawOne(gd, indicesToDraw[i]);
47515 }
47516 } else draw(gd);
47517
47518 return true;
47519};
47520
47521},{"../lib/is_plain_object":178,"../lib/loggers":181,"../lib/noop":186,"../lib/search":195,"../registry":272,"./container_array_match":204}],209:[function(_dereq_,module,exports){
47522/**
47523* Copyright 2012-2020, Plotly, Inc.
47524* All rights reserved.
47525*
47526* This source code is licensed under the MIT license found in the
47527* LICENSE file in the root directory of this source tree.
47528*/
47529
47530'use strict';
47531
47532var d3 = _dereq_('d3');
47533var isNumeric = _dereq_('fast-isnumeric');
47534var hasHover = _dereq_('has-hover');
47535
47536var Lib = _dereq_('../lib');
47537var nestedProperty = Lib.nestedProperty;
47538
47539var Events = _dereq_('../lib/events');
47540var Queue = _dereq_('../lib/queue');
47541
47542var Registry = _dereq_('../registry');
47543var PlotSchema = _dereq_('./plot_schema');
47544var Plots = _dereq_('../plots/plots');
47545var Polar = _dereq_('../plots/polar/legacy');
47546
47547var Axes = _dereq_('../plots/cartesian/axes');
47548var Drawing = _dereq_('../components/drawing');
47549var Color = _dereq_('../components/color');
47550var initInteractions = _dereq_('../plots/cartesian/graph_interact').initInteractions;
47551var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
47552var svgTextUtils = _dereq_('../lib/svg_text_utils');
47553var clearSelect = _dereq_('../plots/cartesian/select').clearSelect;
47554
47555var dfltConfig = _dereq_('./plot_config').dfltConfig;
47556var manageArrays = _dereq_('./manage_arrays');
47557var helpers = _dereq_('./helpers');
47558var subroutines = _dereq_('./subroutines');
47559var editTypes = _dereq_('./edit_types');
47560
47561var AX_NAME_PATTERN = _dereq_('../plots/cartesian/constants').AX_NAME_PATTERN;
47562
47563var numericNameWarningCount = 0;
47564var numericNameWarningCountLimit = 5;
47565
47566/**
47567 * Main plot-creation function
47568 *
47569 * @param {string id or DOM element} gd
47570 * the id or DOM element of the graph container div
47571 * @param {array of objects} data
47572 * array of traces, containing the data and display information for each trace
47573 * @param {object} layout
47574 * object describing the overall display of the plot,
47575 * all the stuff that doesn't pertain to any individual trace
47576 * @param {object} config
47577 * configuration options (see ./plot_config.js for more info)
47578 *
47579 * OR
47580 *
47581 * @param {string id or DOM element} gd
47582 * the id or DOM element of the graph container div
47583 * @param {object} figure
47584 * object containing `data`, `layout`, `config`, and `frames` members
47585 *
47586 */
47587function plot(gd, data, layout, config) {
47588 var frames;
47589
47590 gd = Lib.getGraphDiv(gd);
47591
47592 // Events.init is idempotent and bails early if gd has already been init'd
47593 Events.init(gd);
47594
47595 if(Lib.isPlainObject(data)) {
47596 var obj = data;
47597 data = obj.data;
47598 layout = obj.layout;
47599 config = obj.config;
47600 frames = obj.frames;
47601 }
47602
47603 var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]);
47604 if(okToPlot === false) return Promise.reject();
47605
47606 // if there's no data or layout, and this isn't yet a plotly plot
47607 // container, log a warning to help plotly.js users debug
47608 if(!data && !layout && !Lib.isPlotDiv(gd)) {
47609 Lib.warn('Calling Plotly.plot as if redrawing ' +
47610 'but this container doesn\'t yet have a plot.', gd);
47611 }
47612
47613 function addFrames() {
47614 if(frames) {
47615 return exports.addFrames(gd, frames);
47616 }
47617 }
47618
47619 // transfer configuration options to gd until we move over to
47620 // a more OO like model
47621 setPlotContext(gd, config);
47622
47623 if(!layout) layout = {};
47624
47625 // hook class for plots main container (in case of plotly.js
47626 // this won't be #embedded-graph or .js-tab-contents)
47627 d3.select(gd).classed('js-plotly-plot', true);
47628
47629 // off-screen getBoundingClientRect testing space,
47630 // in #js-plotly-tester (and stored as Drawing.tester)
47631 // so we can share cached text across tabs
47632 Drawing.makeTester();
47633
47634 // collect promises for any async actions during plotting
47635 // any part of the plotting code can push to gd._promises, then
47636 // before we move to the next step, we check that they're all
47637 // complete, and empty out the promise list again.
47638 if(!Array.isArray(gd._promises)) gd._promises = [];
47639
47640 var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
47641
47642 // if there is already data on the graph, append the new data
47643 // if you only want to redraw, pass a non-array for data
47644 if(Array.isArray(data)) {
47645 helpers.cleanData(data);
47646
47647 if(graphWasEmpty) gd.data = data;
47648 else gd.data.push.apply(gd.data, data);
47649
47650 // for routines outside graph_obj that want a clean tab
47651 // (rather than appending to an existing one) gd.empty
47652 // is used to determine whether to make a new tab
47653 gd.empty = false;
47654 }
47655
47656 if(!gd.layout || graphWasEmpty) {
47657 gd.layout = helpers.cleanLayout(layout);
47658 }
47659
47660 Plots.supplyDefaults(gd);
47661
47662 var fullLayout = gd._fullLayout;
47663 var hasCartesian = fullLayout._has('cartesian');
47664
47665 // Legacy polar plots
47666 if(!fullLayout._has('polar') && data && data[0] && data[0].r) {
47667 Lib.log('Legacy polar charts are deprecated!');
47668 return plotLegacyPolar(gd, data, layout);
47669 }
47670
47671 // so we don't try to re-call Plotly.plot from inside
47672 // legend and colorbar, if margins changed
47673 fullLayout._replotting = true;
47674
47675 // make or remake the framework if we need to
47676 if(graphWasEmpty || fullLayout._shouldCreateBgLayer) {
47677 makePlotFramework(gd);
47678
47679 if(fullLayout._shouldCreateBgLayer) {
47680 delete fullLayout._shouldCreateBgLayer;
47681 }
47682 }
47683
47684 // polar need a different framework
47685 if(gd.framework !== makePlotFramework) {
47686 gd.framework = makePlotFramework;
47687 makePlotFramework(gd);
47688 }
47689
47690 // clear gradient defs on each .plot call, because we know we'll loop through all traces
47691 Drawing.initGradients(gd);
47692
47693 // save initial show spikes once per graph
47694 if(graphWasEmpty) Axes.saveShowSpikeInitial(gd);
47695
47696 // prepare the data and find the autorange
47697
47698 // generate calcdata, if we need to
47699 // to force redoing calcdata, just delete it before calling Plotly.plot
47700 var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
47701 if(recalc) Plots.doCalcdata(gd);
47702
47703 // in case it has changed, attach fullData traces to calcdata
47704 for(var i = 0; i < gd.calcdata.length; i++) {
47705 gd.calcdata[i][0].trace = gd._fullData[i];
47706 }
47707
47708 // make the figure responsive
47709 if(gd._context.responsive) {
47710 if(!gd._responsiveChartHandler) {
47711 // Keep a reference to the resize handler to purge it down the road
47712 gd._responsiveChartHandler = function() { if(!Lib.isHidden(gd)) Plots.resize(gd); };
47713
47714 // Listen to window resize
47715 window.addEventListener('resize', gd._responsiveChartHandler);
47716 }
47717 } else {
47718 Lib.clearResponsive(gd);
47719 }
47720
47721 /*
47722 * start async-friendly code - now we're actually drawing things
47723 */
47724
47725 var oldMargins = Lib.extendFlat({}, fullLayout._size);
47726
47727 // draw framework first so that margin-pushing
47728 // components can position themselves correctly
47729 var drawFrameworkCalls = 0;
47730 function drawFramework() {
47731 var basePlotModules = fullLayout._basePlotModules;
47732
47733 for(var i = 0; i < basePlotModules.length; i++) {
47734 if(basePlotModules[i].drawFramework) {
47735 basePlotModules[i].drawFramework(gd);
47736 }
47737 }
47738
47739 if(!fullLayout._glcanvas && fullLayout._has('gl')) {
47740 fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
47741 key: 'contextLayer',
47742 context: true,
47743 pick: false
47744 }, {
47745 key: 'focusLayer',
47746 context: false,
47747 pick: false
47748 }, {
47749 key: 'pickLayer',
47750 context: false,
47751 pick: true
47752 }], function(d) { return d.key; });
47753
47754 fullLayout._glcanvas.enter().append('canvas')
47755 .attr('class', function(d) {
47756 return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
47757 })
47758 .style({
47759 position: 'absolute',
47760 top: 0,
47761 left: 0,
47762 overflow: 'visible',
47763 'pointer-events': 'none'
47764 });
47765 }
47766
47767 if(fullLayout._glcanvas) {
47768 fullLayout._glcanvas
47769 .attr('width', fullLayout.width)
47770 .attr('height', fullLayout.height);
47771
47772 var regl = fullLayout._glcanvas.data()[0].regl;
47773 if(regl) {
47774 // Unfortunately, this can happen when relayouting to large
47775 // width/height on some browsers.
47776 if(Math.floor(fullLayout.width) !== regl._gl.drawingBufferWidth ||
47777 Math.floor(fullLayout.height) !== regl._gl.drawingBufferHeight
47778 ) {
47779 var msg = 'WebGL context buffer and canvas dimensions do not match due to browser/WebGL bug.';
47780 if(drawFrameworkCalls) {
47781 Lib.error(msg);
47782 } else {
47783 Lib.log(msg + ' Clearing graph and plotting again.');
47784 Plots.cleanPlot([], {}, gd._fullData, fullLayout);
47785 Plots.supplyDefaults(gd);
47786 fullLayout = gd._fullLayout;
47787 Plots.doCalcdata(gd);
47788 drawFrameworkCalls++;
47789 return drawFramework();
47790 }
47791 }
47792 }
47793 }
47794
47795 if(fullLayout.modebar.orientation === 'h') {
47796 fullLayout._modebardiv
47797 .style('height', null)
47798 .style('width', '100%');
47799 } else {
47800 fullLayout._modebardiv
47801 .style('width', null)
47802 .style('height', fullLayout.height + 'px');
47803 }
47804
47805 return Plots.previousPromises(gd);
47806 }
47807
47808 // draw anything that can affect margins.
47809 function marginPushers() {
47810 // First reset the list of things that are allowed to change the margins
47811 // So any deleted traces or components will be wiped out of the
47812 // automargin calculation.
47813 // This means *every* margin pusher must be listed here, even if it
47814 // doesn't actually try to push the margins until later.
47815 Plots.clearAutoMarginIds(gd);
47816
47817 subroutines.drawMarginPushers(gd);
47818 Axes.allowAutoMargin(gd);
47819
47820 // TODO can this be moved elsewhere?
47821 if(fullLayout._has('pie')) {
47822 var fullData = gd._fullData;
47823 for(var i = 0; i < fullData.length; i++) {
47824 var trace = fullData[i];
47825 if(trace.type === 'pie' && trace.automargin) {
47826 Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin');
47827 }
47828 }
47829 }
47830
47831 Plots.doAutoMargin(gd);
47832 return Plots.previousPromises(gd);
47833 }
47834
47835 // in case the margins changed, draw margin pushers again
47836 function marginPushersAgain() {
47837 if(!Plots.didMarginChange(oldMargins, fullLayout._size)) return;
47838
47839 return Lib.syncOrAsync([
47840 marginPushers,
47841 subroutines.layoutStyles
47842 ], gd);
47843 }
47844
47845 function positionAndAutorange() {
47846 if(!recalc) {
47847 doAutoRangeAndConstraints();
47848 return;
47849 }
47850
47851 // TODO: autosize extra for text markers and images
47852 // see https://github.com/plotly/plotly.js/issues/1111
47853 return Lib.syncOrAsync([
47854 Registry.getComponentMethod('shapes', 'calcAutorange'),
47855 Registry.getComponentMethod('annotations', 'calcAutorange'),
47856 doAutoRangeAndConstraints
47857 ], gd);
47858 }
47859
47860 function doAutoRangeAndConstraints() {
47861 if(gd._transitioning) return;
47862
47863 subroutines.doAutoRangeAndConstraints(gd);
47864
47865 // store initial ranges *after* enforcing constraints, otherwise
47866 // we will never look like we're at the initial ranges
47867 if(graphWasEmpty) Axes.saveRangeInitial(gd);
47868
47869 // this one is different from shapes/annotations calcAutorange
47870 // the others incorporate those components into ax._extremes,
47871 // this one actually sets the ranges in rangesliders.
47872 Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd);
47873 }
47874
47875 // draw ticks, titles, and calculate axis scaling (._b, ._m)
47876 function drawAxes() {
47877 return Axes.draw(gd, graphWasEmpty ? '' : 'redraw');
47878 }
47879
47880 var seq = [
47881 Plots.previousPromises,
47882 addFrames,
47883 drawFramework,
47884 marginPushers,
47885 marginPushersAgain
47886 ];
47887
47888 if(hasCartesian) seq.push(positionAndAutorange);
47889
47890 seq.push(subroutines.layoutStyles);
47891 if(hasCartesian) seq.push(drawAxes);
47892
47893 seq.push(
47894 subroutines.drawData,
47895 subroutines.finalDraw,
47896 initInteractions,
47897 Plots.addLinks,
47898 Plots.rehover,
47899 Plots.redrag,
47900 // TODO: doAutoMargin is only needed here for axis automargin, which
47901 // happens outside of marginPushers where all the other automargins are
47902 // calculated. Would be much better to separate margin calculations from
47903 // component drawing - see https://github.com/plotly/plotly.js/issues/2704
47904 Plots.doAutoMargin,
47905 Plots.previousPromises
47906 );
47907
47908 // even if everything we did was synchronous, return a promise
47909 // so that the caller doesn't care which route we took
47910 var plotDone = Lib.syncOrAsync(seq, gd);
47911 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
47912
47913 return plotDone.then(function() {
47914 emitAfterPlot(gd);
47915 return gd;
47916 });
47917}
47918
47919function emitAfterPlot(gd) {
47920 var fullLayout = gd._fullLayout;
47921
47922 if(fullLayout._redrawFromAutoMarginCount) {
47923 fullLayout._redrawFromAutoMarginCount--;
47924 } else {
47925 gd.emit('plotly_afterplot');
47926 }
47927}
47928
47929function setPlotConfig(obj) {
47930 return Lib.extendFlat(dfltConfig, obj);
47931}
47932
47933function setBackground(gd, bgColor) {
47934 try {
47935 gd._fullLayout._paper.style('background', bgColor);
47936 } catch(e) {
47937 Lib.error(e);
47938 }
47939}
47940
47941function opaqueSetBackground(gd, bgColor) {
47942 var blend = Color.combine(bgColor, 'white');
47943 setBackground(gd, blend);
47944}
47945
47946function setPlotContext(gd, config) {
47947 if(!gd._context) {
47948 gd._context = Lib.extendDeep({}, dfltConfig);
47949
47950 // stash <base> href, used to make robust clipPath URLs
47951 var base = d3.select('base');
47952 gd._context._baseUrl = base.size() && base.attr('href') ?
47953 window.location.href.split('#')[0] :
47954 '';
47955 }
47956
47957 var context = gd._context;
47958
47959 var i, keys, key;
47960
47961 if(config) {
47962 keys = Object.keys(config);
47963 for(i = 0; i < keys.length; i++) {
47964 key = keys[i];
47965 if(key === 'editable' || key === 'edits') continue;
47966 if(key in context) {
47967 if(key === 'setBackground' && config[key] === 'opaque') {
47968 context[key] = opaqueSetBackground;
47969 } else {
47970 context[key] = config[key];
47971 }
47972 }
47973 }
47974
47975 // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility
47976 if(config.plot3dPixelRatio && !context.plotGlPixelRatio) {
47977 context.plotGlPixelRatio = context.plot3dPixelRatio;
47978 }
47979
47980 // now deal with editable and edits - first editable overrides
47981 // everything, then edits refines
47982 var editable = config.editable;
47983 if(editable !== undefined) {
47984 // we're not going to *use* context.editable, we're only going to
47985 // use context.edits... but keep it for the record
47986 context.editable = editable;
47987
47988 keys = Object.keys(context.edits);
47989 for(i = 0; i < keys.length; i++) {
47990 context.edits[keys[i]] = editable;
47991 }
47992 }
47993 if(config.edits) {
47994 keys = Object.keys(config.edits);
47995 for(i = 0; i < keys.length; i++) {
47996 key = keys[i];
47997 if(key in context.edits) {
47998 context.edits[key] = config.edits[key];
47999 }
48000 }
48001 }
48002
48003 // not part of the user-facing config options
48004 context._exportedPlot = config._exportedPlot;
48005 }
48006
48007 // staticPlot forces a bunch of others:
48008 if(context.staticPlot) {
48009 context.editable = false;
48010 context.edits = {};
48011 context.autosizable = false;
48012 context.scrollZoom = false;
48013 context.doubleClick = false;
48014 context.showTips = false;
48015 context.showLink = false;
48016 context.displayModeBar = false;
48017 }
48018
48019 // make sure hover-only devices have mode bar visible
48020 if(context.displayModeBar === 'hover' && !hasHover) {
48021 context.displayModeBar = true;
48022 }
48023
48024 // default and fallback for setBackground
48025 if(context.setBackground === 'transparent' || typeof context.setBackground !== 'function') {
48026 context.setBackground = setBackground;
48027 }
48028
48029 // Check if gd has a specified widht/height to begin with
48030 context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
48031 context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;
48032
48033 // fill context._scrollZoom helper to help manage scrollZoom flaglist
48034 var szIn = context.scrollZoom;
48035 var szOut = context._scrollZoom = {};
48036 if(szIn === true) {
48037 szOut.cartesian = 1;
48038 szOut.gl3d = 1;
48039 szOut.geo = 1;
48040 szOut.mapbox = 1;
48041 } else if(typeof szIn === 'string') {
48042 var parts = szIn.split('+');
48043 for(i = 0; i < parts.length; i++) {
48044 szOut[parts[i]] = 1;
48045 }
48046 } else if(szIn !== false) {
48047 szOut.gl3d = 1;
48048 szOut.geo = 1;
48049 szOut.mapbox = 1;
48050 }
48051}
48052
48053function plotLegacyPolar(gd, data, layout) {
48054 // build or reuse the container skeleton
48055 var plotContainer = d3.select(gd).selectAll('.plot-container')
48056 .data([0]);
48057 plotContainer.enter()
48058 .insert('div', ':first-child')
48059 .classed('plot-container plotly', true);
48060 var paperDiv = plotContainer.selectAll('.svg-container')
48061 .data([0]);
48062 paperDiv.enter().append('div')
48063 .classed('svg-container', true)
48064 .style('position', 'relative');
48065
48066 // empty it everytime for now
48067 paperDiv.html('');
48068
48069 // fulfill gd requirements
48070 if(data) gd.data = data;
48071 if(layout) gd.layout = layout;
48072 Polar.manager.fillLayout(gd);
48073
48074 // resize canvas
48075 paperDiv.style({
48076 width: gd._fullLayout.width + 'px',
48077 height: gd._fullLayout.height + 'px'
48078 });
48079
48080 // instantiate framework
48081 gd.framework = Polar.manager.framework(gd);
48082
48083 // plot
48084 gd.framework({data: gd.data, layout: gd.layout}, paperDiv.node());
48085
48086 // set undo point
48087 gd.framework.setUndoPoint();
48088
48089 // get the resulting svg for extending it
48090 var polarPlotSVG = gd.framework.svg();
48091
48092 // editable title
48093 var opacity = 1;
48094 var txt = gd._fullLayout.title ? gd._fullLayout.title.text : '';
48095 if(txt === '' || !txt) opacity = 0;
48096
48097 var titleLayout = function() {
48098 this.call(svgTextUtils.convertToTspans, gd);
48099 // TODO: html/mathjax
48100 // TODO: center title
48101 };
48102
48103 var title = polarPlotSVG.select('.title-group text')
48104 .call(titleLayout);
48105
48106 if(gd._context.edits.titleText) {
48107 var placeholderText = Lib._(gd, 'Click to enter Plot title');
48108 if(!txt || txt === placeholderText) {
48109 opacity = 0.2;
48110 // placeholder is not going through convertToTspans
48111 // so needs explicit data-unformatted
48112 title.attr({'data-unformatted': placeholderText})
48113 .text(placeholderText)
48114 .style({opacity: opacity})
48115 .on('mouseover.opacity', function() {
48116 d3.select(this).transition().duration(100)
48117 .style('opacity', 1);
48118 })
48119 .on('mouseout.opacity', function() {
48120 d3.select(this).transition().duration(1000)
48121 .style('opacity', 0);
48122 });
48123 }
48124
48125 var setContenteditable = function() {
48126 this.call(svgTextUtils.makeEditable, {gd: gd})
48127 .on('edit', function(text) {
48128 gd.framework({layout: {title: {text: text}}});
48129 this.text(text)
48130 .call(titleLayout);
48131 this.call(setContenteditable);
48132 })
48133 .on('cancel', function() {
48134 var txt = this.attr('data-unformatted');
48135 this.text(txt).call(titleLayout);
48136 });
48137 };
48138 title.call(setContenteditable);
48139 }
48140
48141 gd._context.setBackground(gd, gd._fullLayout.paper_bgcolor);
48142 Plots.addLinks(gd);
48143
48144 return Promise.resolve();
48145}
48146
48147// convenience function to force a full redraw, mostly for use by plotly.js
48148function redraw(gd) {
48149 gd = Lib.getGraphDiv(gd);
48150
48151 if(!Lib.isPlotDiv(gd)) {
48152 throw new Error('This element is not a Plotly plot: ' + gd);
48153 }
48154
48155 helpers.cleanData(gd.data);
48156 helpers.cleanLayout(gd.layout);
48157
48158 gd.calcdata = undefined;
48159 return exports.plot(gd).then(function() {
48160 gd.emit('plotly_redraw');
48161 return gd;
48162 });
48163}
48164
48165/**
48166 * Convenience function to make idempotent plot option obvious to users.
48167 *
48168 * @param gd
48169 * @param {Object[]} data
48170 * @param {Object} layout
48171 * @param {Object} config
48172 */
48173function newPlot(gd, data, layout, config) {
48174 gd = Lib.getGraphDiv(gd);
48175
48176 // remove gl contexts
48177 Plots.cleanPlot([], {}, gd._fullData || [], gd._fullLayout || {});
48178
48179 Plots.purge(gd);
48180 return exports.plot(gd, data, layout, config);
48181}
48182
48183/**
48184 * Wrap negative indicies to their positive counterparts.
48185 *
48186 * @param {Number[]} indices An array of indices
48187 * @param {Number} maxIndex The maximum index allowable (arr.length - 1)
48188 */
48189function positivifyIndices(indices, maxIndex) {
48190 var parentLength = maxIndex + 1;
48191 var positiveIndices = [];
48192 var i;
48193 var index;
48194
48195 for(i = 0; i < indices.length; i++) {
48196 index = indices[i];
48197 if(index < 0) {
48198 positiveIndices.push(parentLength + index);
48199 } else {
48200 positiveIndices.push(index);
48201 }
48202 }
48203 return positiveIndices;
48204}
48205
48206/**
48207 * Ensures that an index array for manipulating gd.data is valid.
48208 *
48209 * Intended for use with addTraces, deleteTraces, and moveTraces.
48210 *
48211 * @param gd
48212 * @param indices
48213 * @param arrayName
48214 */
48215function assertIndexArray(gd, indices, arrayName) {
48216 var i,
48217 index;
48218
48219 for(i = 0; i < indices.length; i++) {
48220 index = indices[i];
48221
48222 // validate that indices are indeed integers
48223 if(index !== parseInt(index, 10)) {
48224 throw new Error('all values in ' + arrayName + ' must be integers');
48225 }
48226
48227 // check that all indices are in bounds for given gd.data array length
48228 if(index >= gd.data.length || index < -gd.data.length) {
48229 throw new Error(arrayName + ' must be valid indices for gd.data.');
48230 }
48231
48232 // check that indices aren't repeated
48233 if(indices.indexOf(index, i + 1) > -1 ||
48234 index >= 0 && indices.indexOf(-gd.data.length + index) > -1 ||
48235 index < 0 && indices.indexOf(gd.data.length + index) > -1) {
48236 throw new Error('each index in ' + arrayName + ' must be unique.');
48237 }
48238 }
48239}
48240
48241/**
48242 * Private function used by Plotly.moveTraces to check input args
48243 *
48244 * @param gd
48245 * @param currentIndices
48246 * @param newIndices
48247 */
48248function checkMoveTracesArgs(gd, currentIndices, newIndices) {
48249 // check that gd has attribute 'data' and 'data' is array
48250 if(!Array.isArray(gd.data)) {
48251 throw new Error('gd.data must be an array.');
48252 }
48253
48254 // validate currentIndices array
48255 if(typeof currentIndices === 'undefined') {
48256 throw new Error('currentIndices is a required argument.');
48257 } else if(!Array.isArray(currentIndices)) {
48258 currentIndices = [currentIndices];
48259 }
48260 assertIndexArray(gd, currentIndices, 'currentIndices');
48261
48262 // validate newIndices array if it exists
48263 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
48264 newIndices = [newIndices];
48265 }
48266 if(typeof newIndices !== 'undefined') {
48267 assertIndexArray(gd, newIndices, 'newIndices');
48268 }
48269
48270 // check currentIndices and newIndices are the same length if newIdices exists
48271 if(typeof newIndices !== 'undefined' && currentIndices.length !== newIndices.length) {
48272 throw new Error('current and new indices must be of equal length.');
48273 }
48274}
48275/**
48276 * A private function to reduce the type checking clutter in addTraces.
48277 *
48278 * @param gd
48279 * @param traces
48280 * @param newIndices
48281 */
48282function checkAddTracesArgs(gd, traces, newIndices) {
48283 var i, value;
48284
48285 // check that gd has attribute 'data' and 'data' is array
48286 if(!Array.isArray(gd.data)) {
48287 throw new Error('gd.data must be an array.');
48288 }
48289
48290 // make sure traces exists
48291 if(typeof traces === 'undefined') {
48292 throw new Error('traces must be defined.');
48293 }
48294
48295 // make sure traces is an array
48296 if(!Array.isArray(traces)) {
48297 traces = [traces];
48298 }
48299
48300 // make sure each value in traces is an object
48301 for(i = 0; i < traces.length; i++) {
48302 value = traces[i];
48303 if(typeof value !== 'object' || (Array.isArray(value) || value === null)) {
48304 throw new Error('all values in traces array must be non-array objects');
48305 }
48306 }
48307
48308 // make sure we have an index for each trace
48309 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
48310 newIndices = [newIndices];
48311 }
48312 if(typeof newIndices !== 'undefined' && newIndices.length !== traces.length) {
48313 throw new Error(
48314 'if indices is specified, traces.length must equal indices.length'
48315 );
48316 }
48317}
48318
48319/**
48320 * A private function to reduce the type checking clutter in spliceTraces.
48321 * Get all update Properties from gd.data. Validate inputs and outputs.
48322 * Used by prependTrace and extendTraces
48323 *
48324 * @param gd
48325 * @param update
48326 * @param indices
48327 * @param maxPoints
48328 */
48329function assertExtendTracesArgs(gd, update, indices, maxPoints) {
48330 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
48331
48332 if(!Array.isArray(gd.data)) {
48333 throw new Error('gd.data must be an array');
48334 }
48335 if(!Lib.isPlainObject(update)) {
48336 throw new Error('update must be a key:value object');
48337 }
48338
48339 if(typeof indices === 'undefined') {
48340 throw new Error('indices must be an integer or array of integers');
48341 }
48342
48343 assertIndexArray(gd, indices, 'indices');
48344
48345 for(var key in update) {
48346 /*
48347 * Verify that the attribute to be updated contains as many trace updates
48348 * as indices. Failure must result in throw and no-op
48349 */
48350 if(!Array.isArray(update[key]) || update[key].length !== indices.length) {
48351 throw new Error('attribute ' + key + ' must be an array of length equal to indices array length');
48352 }
48353
48354 /*
48355 * if maxPoints is an object it must match keys and array lengths of 'update' 1:1
48356 */
48357 if(maxPointsIsObject &&
48358 (!(key in maxPoints) || !Array.isArray(maxPoints[key]) ||
48359 maxPoints[key].length !== update[key].length)) {
48360 throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' +
48361 'corrispondence with the keys and number of traces in the update object');
48362 }
48363 }
48364}
48365
48366/**
48367 * A private function to reduce the type checking clutter in spliceTraces.
48368 *
48369 * @param {Object|HTMLDivElement} gd
48370 * @param {Object} update
48371 * @param {Number[]} indices
48372 * @param {Number||Object} maxPoints
48373 * @return {Object[]}
48374 */
48375function getExtendProperties(gd, update, indices, maxPoints) {
48376 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
48377 var updateProps = [];
48378 var trace, target, prop, insert, maxp;
48379
48380 // allow scalar index to represent a single trace position
48381 if(!Array.isArray(indices)) indices = [indices];
48382
48383 // negative indices are wrapped around to their positive value. Equivalent to python indexing.
48384 indices = positivifyIndices(indices, gd.data.length - 1);
48385
48386 // loop through all update keys and traces and harvest validated data.
48387 for(var key in update) {
48388 for(var j = 0; j < indices.length; j++) {
48389 /*
48390 * Choose the trace indexed by the indices map argument and get the prop setter-getter
48391 * instance that references the key and value for this particular trace.
48392 */
48393 trace = gd.data[indices[j]];
48394 prop = nestedProperty(trace, key);
48395
48396 /*
48397 * Target is the existing gd.data.trace.dataArray value like "x" or "marker.size"
48398 * Target must exist as an Array to allow the extend operation to be performed.
48399 */
48400 target = prop.get();
48401 insert = update[key][j];
48402
48403 if(!Lib.isArrayOrTypedArray(insert)) {
48404 throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
48405 }
48406 if(!Lib.isArrayOrTypedArray(target)) {
48407 throw new Error('cannot extend missing or non-array attribute: ' + key);
48408 }
48409 if(target.constructor !== insert.constructor) {
48410 throw new Error('cannot extend array with an array of a different type: ' + key);
48411 }
48412
48413 /*
48414 * maxPoints may be an object map or a scalar. If object select the key:value, else
48415 * Use the scalar maxPoints for all key and trace combinations.
48416 */
48417 maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints;
48418
48419 // could have chosen null here, -1 just tells us to not take a window
48420 if(!isNumeric(maxp)) maxp = -1;
48421
48422 /*
48423 * Wrap the nestedProperty in an object containing required data
48424 * for lengthening and windowing this particular trace - key combination.
48425 * Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function.
48426 */
48427 updateProps.push({
48428 prop: prop,
48429 target: target,
48430 insert: insert,
48431 maxp: Math.floor(maxp)
48432 });
48433 }
48434 }
48435
48436 // all target and insertion data now validated
48437 return updateProps;
48438}
48439
48440/**
48441 * A private function to key Extend and Prepend traces DRY
48442 *
48443 * @param {Object|HTMLDivElement} gd
48444 * @param {Object} update
48445 * @param {Number[]} indices
48446 * @param {Number||Object} maxPoints
48447 * @param {Function} updateArray
48448 * @return {Object}
48449 */
48450function spliceTraces(gd, update, indices, maxPoints, updateArray) {
48451 assertExtendTracesArgs(gd, update, indices, maxPoints);
48452
48453 var updateProps = getExtendProperties(gd, update, indices, maxPoints);
48454 var undoUpdate = {};
48455 var undoPoints = {};
48456
48457 for(var i = 0; i < updateProps.length; i++) {
48458 var prop = updateProps[i].prop;
48459 var maxp = updateProps[i].maxp;
48460
48461 // return new array and remainder
48462 var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp);
48463 prop.set(out[0]);
48464
48465 // build the inverse update object for the undo operation
48466 if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
48467 undoUpdate[prop.astr].push(out[1]);
48468
48469 // build the matching maxPoints undo object containing original trace lengths
48470 if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
48471 undoPoints[prop.astr].push(updateProps[i].target.length);
48472 }
48473
48474 return {update: undoUpdate, maxPoints: undoPoints};
48475}
48476
48477function concatTypedArray(arr0, arr1) {
48478 var arr2 = new arr0.constructor(arr0.length + arr1.length);
48479 arr2.set(arr0);
48480 arr2.set(arr1, arr0.length);
48481 return arr2;
48482}
48483
48484/**
48485 * extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
48486 *
48487 * Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend
48488 * inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints
48489 * from the head, whereas Extend truncates the head of the array, counting backward maxPoints
48490 * from the tail.
48491 *
48492 * If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no
48493 * truncation / windowing will be performed. If its zero, well the whole trace is truncated.
48494 *
48495 * @param {Object|HTMLDivElement} gd The graph div
48496 * @param {Object} update The key:array map of target attributes to extend
48497 * @param {Number|Number[]} indices The locations of traces to be extended
48498 * @param {Number|Object} [maxPoints] Number of points for trace window after lengthening.
48499 *
48500 */
48501function extendTraces(gd, update, indices, maxPoints) {
48502 gd = Lib.getGraphDiv(gd);
48503
48504 function updateArray(target, insert, maxp) {
48505 var newArray, remainder;
48506
48507 if(Lib.isTypedArray(target)) {
48508 if(maxp < 0) {
48509 var none = new target.constructor(0);
48510 var both = concatTypedArray(target, insert);
48511
48512 if(maxp < 0) {
48513 newArray = both;
48514 remainder = none;
48515 } else {
48516 newArray = none;
48517 remainder = both;
48518 }
48519 } else {
48520 newArray = new target.constructor(maxp);
48521 remainder = new target.constructor(target.length + insert.length - maxp);
48522
48523 if(maxp === insert.length) {
48524 newArray.set(insert);
48525 remainder.set(target);
48526 } else if(maxp < insert.length) {
48527 var numberOfItemsFromInsert = insert.length - maxp;
48528
48529 newArray.set(insert.subarray(numberOfItemsFromInsert));
48530 remainder.set(target);
48531 remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length);
48532 } else {
48533 var numberOfItemsFromTarget = maxp - insert.length;
48534 var targetBegin = target.length - numberOfItemsFromTarget;
48535
48536 newArray.set(target.subarray(targetBegin));
48537 newArray.set(insert, numberOfItemsFromTarget);
48538 remainder.set(target.subarray(0, targetBegin));
48539 }
48540 }
48541 } else {
48542 newArray = target.concat(insert);
48543 remainder = (maxp >= 0 && maxp < newArray.length) ?
48544 newArray.splice(0, newArray.length - maxp) :
48545 [];
48546 }
48547
48548 return [newArray, remainder];
48549 }
48550
48551 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
48552 var promise = exports.redraw(gd);
48553 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
48554 Queue.add(gd, exports.prependTraces, undoArgs, extendTraces, arguments);
48555
48556 return promise;
48557}
48558
48559function prependTraces(gd, update, indices, maxPoints) {
48560 gd = Lib.getGraphDiv(gd);
48561
48562 function updateArray(target, insert, maxp) {
48563 var newArray, remainder;
48564
48565 if(Lib.isTypedArray(target)) {
48566 if(maxp <= 0) {
48567 var none = new target.constructor(0);
48568 var both = concatTypedArray(insert, target);
48569
48570 if(maxp < 0) {
48571 newArray = both;
48572 remainder = none;
48573 } else {
48574 newArray = none;
48575 remainder = both;
48576 }
48577 } else {
48578 newArray = new target.constructor(maxp);
48579 remainder = new target.constructor(target.length + insert.length - maxp);
48580
48581 if(maxp === insert.length) {
48582 newArray.set(insert);
48583 remainder.set(target);
48584 } else if(maxp < insert.length) {
48585 var numberOfItemsFromInsert = insert.length - maxp;
48586
48587 newArray.set(insert.subarray(0, numberOfItemsFromInsert));
48588 remainder.set(insert.subarray(numberOfItemsFromInsert));
48589 remainder.set(target, numberOfItemsFromInsert);
48590 } else {
48591 var numberOfItemsFromTarget = maxp - insert.length;
48592
48593 newArray.set(insert);
48594 newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length);
48595 remainder.set(target.subarray(numberOfItemsFromTarget));
48596 }
48597 }
48598 } else {
48599 newArray = insert.concat(target);
48600 remainder = (maxp >= 0 && maxp < newArray.length) ?
48601 newArray.splice(maxp, newArray.length) :
48602 [];
48603 }
48604
48605 return [newArray, remainder];
48606 }
48607
48608 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
48609 var promise = exports.redraw(gd);
48610 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
48611 Queue.add(gd, exports.extendTraces, undoArgs, prependTraces, arguments);
48612
48613 return promise;
48614}
48615
48616/**
48617 * Add data traces to an existing graph div.
48618 *
48619 * @param {Object|HTMLDivElement} gd The graph div
48620 * @param {Object[]} gd.data The array of traces we're adding to
48621 * @param {Object[]|Object} traces The object or array of objects to add
48622 * @param {Number[]|Number} [newIndices=[gd.data.length]] Locations to add traces
48623 *
48624 */
48625function addTraces(gd, traces, newIndices) {
48626 gd = Lib.getGraphDiv(gd);
48627
48628 var currentIndices = [];
48629 var undoFunc = exports.deleteTraces;
48630 var redoFunc = addTraces;
48631 var undoArgs = [gd, currentIndices];
48632 var redoArgs = [gd, traces]; // no newIndices here
48633 var i;
48634 var promise;
48635
48636 // all validation is done elsewhere to remove clutter here
48637 checkAddTracesArgs(gd, traces, newIndices);
48638
48639 // make sure traces is an array
48640 if(!Array.isArray(traces)) {
48641 traces = [traces];
48642 }
48643
48644 // make sure traces do not repeat existing ones
48645 traces = traces.map(function(trace) {
48646 return Lib.extendFlat({}, trace);
48647 });
48648
48649 helpers.cleanData(traces);
48650
48651 // add the traces to gd.data (no redrawing yet!)
48652 for(i = 0; i < traces.length; i++) {
48653 gd.data.push(traces[i]);
48654 }
48655
48656 // to continue, we need to call moveTraces which requires currentIndices
48657 for(i = 0; i < traces.length; i++) {
48658 currentIndices.push(-traces.length + i);
48659 }
48660
48661 // if the user didn't define newIndices, they just want the traces appended
48662 // i.e., we can simply redraw and be done
48663 if(typeof newIndices === 'undefined') {
48664 promise = exports.redraw(gd);
48665 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
48666 return promise;
48667 }
48668
48669 // make sure indices is property defined
48670 if(!Array.isArray(newIndices)) {
48671 newIndices = [newIndices];
48672 }
48673
48674 try {
48675 // this is redundant, but necessary to not catch later possible errors!
48676 checkMoveTracesArgs(gd, currentIndices, newIndices);
48677 } catch(error) {
48678 // something went wrong, reset gd to be safe and rethrow error
48679 gd.data.splice(gd.data.length - traces.length, traces.length);
48680 throw error;
48681 }
48682
48683 // if we're here, the user has defined specific places to place the new traces
48684 // this requires some extra work that moveTraces will do
48685 Queue.startSequence(gd);
48686 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
48687 promise = exports.moveTraces(gd, currentIndices, newIndices);
48688 Queue.stopSequence(gd);
48689 return promise;
48690}
48691
48692/**
48693 * Delete traces at `indices` from gd.data array.
48694 *
48695 * @param {Object|HTMLDivElement} gd The graph div
48696 * @param {Object[]} gd.data The array of traces we're removing from
48697 * @param {Number|Number[]} indices The indices
48698 */
48699function deleteTraces(gd, indices) {
48700 gd = Lib.getGraphDiv(gd);
48701
48702 var traces = [];
48703 var undoFunc = exports.addTraces;
48704 var redoFunc = deleteTraces;
48705 var undoArgs = [gd, traces, indices];
48706 var redoArgs = [gd, indices];
48707 var i;
48708 var deletedTrace;
48709
48710 // make sure indices are defined
48711 if(typeof indices === 'undefined') {
48712 throw new Error('indices must be an integer or array of integers.');
48713 } else if(!Array.isArray(indices)) {
48714 indices = [indices];
48715 }
48716 assertIndexArray(gd, indices, 'indices');
48717
48718 // convert negative indices to positive indices
48719 indices = positivifyIndices(indices, gd.data.length - 1);
48720
48721 // we want descending here so that splicing later doesn't affect indexing
48722 indices.sort(Lib.sorterDes);
48723 for(i = 0; i < indices.length; i += 1) {
48724 deletedTrace = gd.data.splice(indices[i], 1)[0];
48725 traces.push(deletedTrace);
48726 }
48727
48728 var promise = exports.redraw(gd);
48729 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
48730
48731 return promise;
48732}
48733
48734/**
48735 * Move traces at currentIndices array to locations in newIndices array.
48736 *
48737 * If newIndices is omitted, currentIndices will be moved to the end. E.g.,
48738 * these are equivalent:
48739 *
48740 * Plotly.moveTraces(gd, [1, 2, 3], [-3, -2, -1])
48741 * Plotly.moveTraces(gd, [1, 2, 3])
48742 *
48743 * @param {Object|HTMLDivElement} gd The graph div
48744 * @param {Object[]} gd.data The array of traces we're removing from
48745 * @param {Number|Number[]} currentIndices The locations of traces to be moved
48746 * @param {Number|Number[]} [newIndices] The locations to move traces to
48747 *
48748 * Example calls:
48749 *
48750 * // move trace i to location x
48751 * Plotly.moveTraces(gd, i, x)
48752 *
48753 * // move trace i to end of array
48754 * Plotly.moveTraces(gd, i)
48755 *
48756 * // move traces i, j, k to end of array (i != j != k)
48757 * Plotly.moveTraces(gd, [i, j, k])
48758 *
48759 * // move traces [i, j, k] to [x, y, z] (i != j != k) (x != y != z)
48760 * Plotly.moveTraces(gd, [i, j, k], [x, y, z])
48761 *
48762 * // reorder all traces (assume there are 5--a, b, c, d, e)
48763 * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end'
48764 */
48765function moveTraces(gd, currentIndices, newIndices) {
48766 gd = Lib.getGraphDiv(gd);
48767
48768 var newData = [];
48769 var movingTraceMap = [];
48770 var undoFunc = moveTraces;
48771 var redoFunc = moveTraces;
48772 var undoArgs = [gd, newIndices, currentIndices];
48773 var redoArgs = [gd, currentIndices, newIndices];
48774 var i;
48775
48776 // to reduce complexity here, check args elsewhere
48777 // this throws errors where appropriate
48778 checkMoveTracesArgs(gd, currentIndices, newIndices);
48779
48780 // make sure currentIndices is an array
48781 currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices];
48782
48783 // if undefined, define newIndices to point to the end of gd.data array
48784 if(typeof newIndices === 'undefined') {
48785 newIndices = [];
48786 for(i = 0; i < currentIndices.length; i++) {
48787 newIndices.push(-currentIndices.length + i);
48788 }
48789 }
48790
48791 // make sure newIndices is an array if it's user-defined
48792 newIndices = Array.isArray(newIndices) ? newIndices : [newIndices];
48793
48794 // convert negative indices to positive indices (they're the same length)
48795 currentIndices = positivifyIndices(currentIndices, gd.data.length - 1);
48796 newIndices = positivifyIndices(newIndices, gd.data.length - 1);
48797
48798 // at this point, we've coerced the index arrays into predictable forms
48799
48800 // get the traces that aren't being moved around
48801 for(i = 0; i < gd.data.length; i++) {
48802 // if index isn't in currentIndices, include it in ignored!
48803 if(currentIndices.indexOf(i) === -1) {
48804 newData.push(gd.data[i]);
48805 }
48806 }
48807
48808 // get a mapping of indices to moving traces
48809 for(i = 0; i < currentIndices.length; i++) {
48810 movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]});
48811 }
48812
48813 // reorder this mapping by newIndex, ascending
48814 movingTraceMap.sort(function(a, b) {
48815 return a.newIndex - b.newIndex;
48816 });
48817
48818 // now, add the moving traces back in, in order!
48819 for(i = 0; i < movingTraceMap.length; i += 1) {
48820 newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace);
48821 }
48822
48823 gd.data = newData;
48824
48825 var promise = exports.redraw(gd);
48826 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
48827
48828 return promise;
48829}
48830
48831/**
48832 * restyle: update trace attributes of an existing plot
48833 *
48834 * Can be called two ways.
48835 *
48836 * Signature 1:
48837 * @param {String | HTMLDivElement} gd
48838 * the id or DOM element of the graph container div
48839 * @param {String} astr
48840 * attribute string (like `'marker.symbol'`) to update
48841 * @param {*} val
48842 * value to give this attribute
48843 * @param {Number[] | Number} [traces]
48844 * integer or array of integers for the traces to alter (all if omitted)
48845 *
48846 * Signature 2:
48847 * @param {String | HTMLDivElement} gd
48848 * (as in signature 1)
48849 * @param {Object} aobj
48850 * attribute object `{astr1: val1, astr2: val2 ...}`
48851 * allows setting multiple attributes simultaneously
48852 * @param {Number[] | Number} [traces]
48853 * (as in signature 1)
48854 *
48855 * `val` (or `val1`, `val2` ... in the object form) can be an array,
48856 * to apply different values to each trace.
48857 *
48858 * If the array is too short, it will wrap around (useful for
48859 * style files that want to specify cyclical default values).
48860 */
48861function restyle(gd, astr, val, _traces) {
48862 gd = Lib.getGraphDiv(gd);
48863 helpers.clearPromiseQueue(gd);
48864
48865 var aobj = {};
48866 if(typeof astr === 'string') aobj[astr] = val;
48867 else if(Lib.isPlainObject(astr)) {
48868 // the 3-arg form
48869 aobj = Lib.extendFlat({}, astr);
48870 if(_traces === undefined) _traces = val;
48871 } else {
48872 Lib.warn('Restyle fail.', astr, val, _traces);
48873 return Promise.reject();
48874 }
48875
48876 if(Object.keys(aobj).length) gd.changed = true;
48877
48878 var traces = helpers.coerceTraceIndices(gd, _traces);
48879
48880 var specs = _restyle(gd, aobj, traces);
48881 var flags = specs.flags;
48882
48883 // clear calcdata and/or axis types if required so they get regenerated
48884 if(flags.calc) gd.calcdata = undefined;
48885 if(flags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, {});
48886
48887 // fill in redraw sequence
48888 var seq = [];
48889
48890 if(flags.fullReplot) {
48891 seq.push(exports.plot);
48892 } else {
48893 seq.push(Plots.previousPromises);
48894
48895 // maybe only call Plots.supplyDataDefaults in the splom case,
48896 // to skip over long and slow axes defaults
48897 Plots.supplyDefaults(gd);
48898
48899 if(flags.markerSize) {
48900 Plots.doCalcdata(gd);
48901 addAxRangeSequence(seq);
48902
48903 // TODO
48904 // if all axes have autorange:false, then
48905 // proceed to subroutines.doTraceStyle(),
48906 // otherwise we must go through addAxRangeSequence,
48907 // which in general must redraws 'all' axes
48908 }
48909
48910 if(flags.style) seq.push(subroutines.doTraceStyle);
48911 if(flags.colorbars) seq.push(subroutines.doColorBars);
48912
48913 seq.push(emitAfterPlot);
48914 }
48915
48916 seq.push(Plots.rehover, Plots.redrag);
48917
48918 Queue.add(gd,
48919 restyle, [gd, specs.undoit, specs.traces],
48920 restyle, [gd, specs.redoit, specs.traces]
48921 );
48922
48923 var plotDone = Lib.syncOrAsync(seq, gd);
48924 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
48925
48926 return plotDone.then(function() {
48927 gd.emit('plotly_restyle', specs.eventData);
48928 return gd;
48929 });
48930}
48931
48932// for undo: undefined initial vals must be turned into nulls
48933// so that we unset rather than ignore them
48934function undefinedToNull(val) {
48935 if(val === undefined) return null;
48936 return val;
48937}
48938
48939/**
48940 * Factory function to wrap nestedProperty with GUI edits if necessary
48941 * with GUI edits we add an optional prefix to the nestedProperty constructor
48942 * to prepend to the attribute string in the preGUI store.
48943 */
48944function makeNP(preGUI, guiEditFlag) {
48945 if(!guiEditFlag) return nestedProperty;
48946
48947 return function(container, attr, prefix) {
48948 var np = nestedProperty(container, attr);
48949 var npSet = np.set;
48950 np.set = function(val) {
48951 var fullAttr = (prefix || '') + attr;
48952 storeCurrent(fullAttr, np.get(), val, preGUI);
48953 npSet(val);
48954 };
48955 return np;
48956 };
48957}
48958
48959function storeCurrent(attr, val, newVal, preGUI) {
48960 if(Array.isArray(val) || Array.isArray(newVal)) {
48961 var arrayVal = Array.isArray(val) ? val : [];
48962 var arrayNew = Array.isArray(newVal) ? newVal : [];
48963 var maxLen = Math.max(arrayVal.length, arrayNew.length);
48964 for(var i = 0; i < maxLen; i++) {
48965 storeCurrent(attr + '[' + i + ']', arrayVal[i], arrayNew[i], preGUI);
48966 }
48967 } else if(Lib.isPlainObject(val) || Lib.isPlainObject(newVal)) {
48968 var objVal = Lib.isPlainObject(val) ? val : {};
48969 var objNew = Lib.isPlainObject(newVal) ? newVal : {};
48970 var objBoth = Lib.extendFlat({}, objVal, objNew);
48971 for(var key in objBoth) {
48972 storeCurrent(attr + '.' + key, objVal[key], objNew[key], preGUI);
48973 }
48974 } else if(preGUI[attr] === undefined) {
48975 preGUI[attr] = undefinedToNull(val);
48976 }
48977}
48978
48979/**
48980 * storeDirectGUIEdit: for routines that skip restyle/relayout and mock it
48981 * by emitting a plotly_restyle or plotly_relayout event, this routine
48982 * keeps track of the initial state in _preGUI for use by uirevision
48983 * Does *not* apply these changes to data/layout - that's the responsibility
48984 * of the calling routine.
48985 *
48986 * @param {object} container: the input attributes container (eg `layout` or a `trace`)
48987 * @param {object} preGUI: where original values should be stored, either
48988 * `layout._preGUI` or `layout._tracePreGUI[uid]`
48989 * @param {object} edits: the {attr: val} object as normally passed to `relayout` etc
48990 */
48991function _storeDirectGUIEdit(container, preGUI, edits) {
48992 for(var attr in edits) {
48993 var np = nestedProperty(container, attr);
48994 storeCurrent(attr, np.get(), edits[attr], preGUI);
48995 }
48996}
48997
48998function _restyle(gd, aobj, traces) {
48999 var fullLayout = gd._fullLayout;
49000 var fullData = gd._fullData;
49001 var data = gd.data;
49002 var guiEditFlag = fullLayout._guiEditing;
49003 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
49004 var eventData = Lib.extendDeepAll({}, aobj);
49005 var i;
49006
49007 cleanDeprecatedAttributeKeys(aobj);
49008
49009 // initialize flags
49010 var flags = editTypes.traceFlags();
49011
49012 // copies of the change (and previous values of anything affected)
49013 // for the undo / redo queue
49014 var redoit = {};
49015 var undoit = {};
49016 var axlist;
49017
49018 // make a new empty vals array for undoit
49019 function a0() { return traces.map(function() { return undefined; }); }
49020
49021 // for autoranging multiple axes
49022 function addToAxlist(axid) {
49023 var axName = Axes.id2name(axid);
49024 if(axlist.indexOf(axName) === -1) axlist.push(axName);
49025 }
49026
49027 function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
49028
49029 function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
49030
49031 function getFullTrace(traceIndex) {
49032 // usually fullData maps 1:1 onto data, but with groupby transforms
49033 // the fullData index can be greater. Take the *first* matching trace.
49034 for(var j = traceIndex; j < fullData.length; j++) {
49035 if(fullData[j]._input === data[traceIndex]) return fullData[j];
49036 }
49037 // should never get here - and if we *do* it should cause an error
49038 // later on undefined fullTrace is passed to nestedProperty.
49039 }
49040
49041 // for attrs that interact (like scales & autoscales), save the
49042 // old vals before making the change
49043 // val=undefined will not set a value, just record what the value was.
49044 // val=null will delete the attribute
49045 // attr can be an array to set several at once (all to the same val)
49046 function doextra(attr, val, i) {
49047 if(Array.isArray(attr)) {
49048 attr.forEach(function(a) { doextra(a, val, i); });
49049 return;
49050 }
49051 // quit if explicitly setting this elsewhere
49052 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
49053
49054 var extraparam;
49055 if(attr.substr(0, 6) === 'LAYOUT') {
49056 extraparam = layoutNP(gd.layout, attr.replace('LAYOUT', ''));
49057 } else {
49058 var tracei = traces[i];
49059 var preGUI = fullLayout._tracePreGUI[getFullTrace(tracei)._fullInput.uid];
49060 extraparam = makeNP(preGUI, guiEditFlag)(data[tracei], attr);
49061 }
49062
49063 if(!(attr in undoit)) {
49064 undoit[attr] = a0();
49065 }
49066 if(undoit[attr][i] === undefined) {
49067 undoit[attr][i] = undefinedToNull(extraparam.get());
49068 }
49069 if(val !== undefined) {
49070 extraparam.set(val);
49071 }
49072 }
49073
49074 function allBins(binAttr) {
49075 return function(j) {
49076 return fullData[j][binAttr];
49077 };
49078 }
49079
49080 function arrayBins(binAttr) {
49081 return function(vij, j) {
49082 return vij === false ? fullData[traces[j]][binAttr] : null;
49083 };
49084 }
49085
49086 // now make the changes to gd.data (and occasionally gd.layout)
49087 // and figure out what kind of graphics update we need to do
49088 for(var ai in aobj) {
49089 if(helpers.hasParent(aobj, ai)) {
49090 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
49091 }
49092
49093 var vi = aobj[ai];
49094 var cont;
49095 var contFull;
49096 var param;
49097 var oldVal;
49098 var newVal;
49099 var valObject;
49100
49101 // Backward compatibility shim for turning histogram autobin on,
49102 // or freezing previous autobinned values.
49103 // Replace obsolete `autobin(x|y): true` with `(x|y)bins: null`
49104 // and `autobin(x|y): false` with the `(x|y)bins` in `fullData`
49105 if(ai === 'autobinx' || ai === 'autobiny') {
49106 ai = ai.charAt(ai.length - 1) + 'bins';
49107 if(Array.isArray(vi)) vi = vi.map(arrayBins(ai));
49108 else if(vi === false) vi = traces.map(allBins(ai));
49109 else vi = null;
49110 }
49111
49112 redoit[ai] = vi;
49113
49114 if(ai.substr(0, 6) === 'LAYOUT') {
49115 param = layoutNP(gd.layout, ai.replace('LAYOUT', ''));
49116 undoit[ai] = [undefinedToNull(param.get())];
49117 // since we're allowing val to be an array, allow it here too,
49118 // even though that's meaningless
49119 param.set(Array.isArray(vi) ? vi[0] : vi);
49120 // ironically, the layout attrs in restyle only require replot,
49121 // not relayout
49122 flags.calc = true;
49123 continue;
49124 }
49125
49126 // set attribute in gd.data
49127 undoit[ai] = a0();
49128 for(i = 0; i < traces.length; i++) {
49129 cont = data[traces[i]];
49130 contFull = getFullTrace(traces[i]);
49131 var preGUI = fullLayout._tracePreGUI[contFull._fullInput.uid];
49132 param = makeNP(preGUI, guiEditFlag)(cont, ai);
49133 oldVal = param.get();
49134 newVal = Array.isArray(vi) ? vi[i % vi.length] : vi;
49135
49136 if(newVal === undefined) continue;
49137
49138 var finalPart = param.parts[param.parts.length - 1];
49139 var prefix = ai.substr(0, ai.length - finalPart.length - 1);
49140 var prefixDot = prefix ? prefix + '.' : '';
49141 var innerContFull = prefix ?
49142 nestedProperty(contFull, prefix).get() : contFull;
49143
49144 valObject = PlotSchema.getTraceValObject(contFull, param.parts);
49145
49146 if(valObject && valObject.impliedEdits && newVal !== null) {
49147 for(var impliedKey in valObject.impliedEdits) {
49148 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey], i);
49149 }
49150 } else if((finalPart === 'thicknessmode' || finalPart === 'lenmode') &&
49151 oldVal !== newVal &&
49152 (newVal === 'fraction' || newVal === 'pixels') &&
49153 innerContFull
49154 ) {
49155 // changing colorbar size modes,
49156 // make the resulting size not change
49157 // note that colorbar fractional sizing is based on the
49158 // original plot size, before anything (like a colorbar)
49159 // increases the margins
49160
49161 var gs = fullLayout._size;
49162 var orient = innerContFull.orient;
49163 var topOrBottom = (orient === 'top') || (orient === 'bottom');
49164 if(finalPart === 'thicknessmode') {
49165 var thicknorm = topOrBottom ? gs.h : gs.w;
49166 doextra(prefixDot + 'thickness', innerContFull.thickness *
49167 (newVal === 'fraction' ? 1 / thicknorm : thicknorm), i);
49168 } else {
49169 var lennorm = topOrBottom ? gs.w : gs.h;
49170 doextra(prefixDot + 'len', innerContFull.len *
49171 (newVal === 'fraction' ? 1 / lennorm : lennorm), i);
49172 }
49173 } else if(ai === 'type' && (
49174 (newVal === 'pie') !== (oldVal === 'pie') ||
49175 (newVal === 'funnelarea') !== (oldVal === 'funnelarea')
49176 )) {
49177 var labelsTo = 'x';
49178 var valuesTo = 'y';
49179 if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
49180 labelsTo = 'y';
49181 valuesTo = 'x';
49182 }
49183 Lib.swapAttrs(cont, ['?', '?src'], 'labels', labelsTo);
49184 Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo);
49185 Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo);
49186
49187 if(oldVal === 'pie' || oldVal === 'funnelarea') {
49188 nestedProperty(cont, 'marker.color')
49189 .set(nestedProperty(cont, 'marker.colors').get());
49190
49191 // super kludgy - but if all pies are gone we won't remove them otherwise
49192 fullLayout._pielayer.selectAll('g.trace').remove();
49193 } else if(Registry.traceIs(cont, 'cartesian')) {
49194 nestedProperty(cont, 'marker.colors')
49195 .set(nestedProperty(cont, 'marker.color').get());
49196 }
49197 }
49198
49199 undoit[ai][i] = undefinedToNull(oldVal);
49200 // set the new value - if val is an array, it's one el per trace
49201 // first check for attributes that get more complex alterations
49202 var swapAttrs = [
49203 'swapxy', 'swapxyaxes', 'orientation', 'orientationaxes'
49204 ];
49205 if(swapAttrs.indexOf(ai) !== -1) {
49206 // setting an orientation: make sure it's changing
49207 // before we swap everything else
49208 if(ai === 'orientation') {
49209 param.set(newVal);
49210 // obnoxious that we need this level of coupling... but in order to
49211 // properly handle setting orientation to `null` we need to mimic
49212 // the logic inside Bars.supplyDefaults for default orientation
49213 var defaultOrientation = (cont.x && !cont.y) ? 'h' : 'v';
49214 if((param.get() || defaultOrientation) === contFull.orientation) {
49215 continue;
49216 }
49217 } else if(ai === 'orientationaxes') {
49218 // orientationaxes has no value,
49219 // it flips everything and the axes
49220
49221 cont.orientation =
49222 {v: 'h', h: 'v'}[contFull.orientation];
49223 }
49224 helpers.swapXYData(cont);
49225 flags.calc = flags.clearAxisTypes = true;
49226 } else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) {
49227 // TODO: use manageArrays.applyContainerArrayChanges here too
49228 helpers.manageArrayContainers(param, newVal, undoit);
49229 flags.calc = true;
49230 } else {
49231 if(valObject) {
49232 // must redo calcdata when restyling array values of arrayOk attributes
49233 // ... but no need to this for regl-based traces
49234 if(valObject.arrayOk &&
49235 !Registry.traceIs(contFull, 'regl') &&
49236 (Lib.isArrayOrTypedArray(newVal) || Lib.isArrayOrTypedArray(oldVal))
49237 ) {
49238 flags.calc = true;
49239 } else editTypes.update(flags, valObject);
49240 } else {
49241 /*
49242 * if we couldn't find valObject, assume a full recalc.
49243 * This can happen if you're changing type and making
49244 * some other edits too, so the modules we're
49245 * looking at don't have these attributes in them.
49246 */
49247 flags.calc = true;
49248 }
49249
49250 // all the other ones, just modify that one attribute
49251 param.set(newVal);
49252 }
49253 }
49254
49255 // swap the data attributes of the relevant x and y axes?
49256 if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) {
49257 Axes.swap(gd, traces);
49258 }
49259
49260 // swap hovermode if set to "compare x/y data"
49261 if(ai === 'orientationaxes') {
49262 var hovermode = nestedProperty(gd.layout, 'hovermode');
49263 if(hovermode.get() === 'x') {
49264 hovermode.set('y');
49265 } else if(hovermode.get() === 'y') {
49266 hovermode.set('x');
49267 } else if(hovermode.get() === 'x unified') {
49268 hovermode.set('y unified');
49269 } else if(hovermode.get() === 'y unified') {
49270 hovermode.set('x unified');
49271 }
49272 }
49273
49274 // Major enough changes deserve autoscale and
49275 // non-reversed axes so people don't get confused
49276 //
49277 // Note: autobin (or its new analog bin clearing) is not included here
49278 // since we're not pushing bins back to gd.data, so if we have bin
49279 // info it was explicitly provided by the user.
49280 if(['orientation', 'type'].indexOf(ai) !== -1) {
49281 axlist = [];
49282 for(i = 0; i < traces.length; i++) {
49283 var trace = data[traces[i]];
49284
49285 if(Registry.traceIs(trace, 'cartesian')) {
49286 addToAxlist(trace.xaxis || 'x');
49287 addToAxlist(trace.yaxis || 'y');
49288 }
49289 }
49290
49291 doextra(axlist.map(autorangeAttr), true, 0);
49292 doextra(axlist.map(rangeAttr), [0, 1], 0);
49293 }
49294 }
49295
49296 if(flags.calc || flags.plot) {
49297 flags.fullReplot = true;
49298 }
49299
49300 return {
49301 flags: flags,
49302 undoit: undoit,
49303 redoit: redoit,
49304 traces: traces,
49305 eventData: Lib.extendDeepNoArrays([], [eventData, traces])
49306 };
49307}
49308
49309/**
49310 * Converts deprecated attribute keys to
49311 * the current API to ensure backwards compatibility.
49312 *
49313 * This is needed for the update mechanism to determine which
49314 * subroutines to run based on the actual attribute
49315 * definitions (that don't include the deprecated ones).
49316 *
49317 * E.g. Maps {'xaxis.title': 'A chart'} to {'xaxis.title.text': 'A chart'}
49318 * and {titlefont: {...}} to {'title.font': {...}}.
49319 *
49320 * @param aobj
49321 */
49322function cleanDeprecatedAttributeKeys(aobj) {
49323 var oldAxisTitleRegex = Lib.counterRegex('axis', '\.title', false, false);
49324 var colorbarRegex = /colorbar\.title$/;
49325 var keys = Object.keys(aobj);
49326 var i, key, value;
49327
49328 for(i = 0; i < keys.length; i++) {
49329 key = keys[i];
49330 value = aobj[key];
49331
49332 if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) &&
49333 (typeof value === 'string' || typeof value === 'number')) {
49334 replace(key, key.replace('title', 'title.text'));
49335 } else if(key.indexOf('titlefont') > -1) {
49336 replace(key, key.replace('titlefont', 'title.font'));
49337 } else if(key.indexOf('titleposition') > -1) {
49338 replace(key, key.replace('titleposition', 'title.position'));
49339 } else if(key.indexOf('titleside') > -1) {
49340 replace(key, key.replace('titleside', 'title.side'));
49341 } else if(key.indexOf('titleoffset') > -1) {
49342 replace(key, key.replace('titleoffset', 'title.offset'));
49343 }
49344 }
49345
49346 function replace(oldAttrStr, newAttrStr) {
49347 aobj[newAttrStr] = aobj[oldAttrStr];
49348 delete aobj[oldAttrStr];
49349 }
49350}
49351
49352/**
49353 * relayout: update layout attributes of an existing plot
49354 *
49355 * Can be called two ways:
49356 *
49357 * Signature 1:
49358 * @param {String | HTMLDivElement} gd
49359 * the id or dom element of the graph container div
49360 * @param {String} astr
49361 * attribute string (like `'xaxis.range[0]'`) to update
49362 * @param {*} val
49363 * value to give this attribute
49364 *
49365 * Signature 2:
49366 * @param {String | HTMLDivElement} gd
49367 * (as in signature 1)
49368 * @param {Object} aobj
49369 * attribute object `{astr1: val1, astr2: val2 ...}`
49370 * allows setting multiple attributes simultaneously
49371 */
49372function relayout(gd, astr, val) {
49373 gd = Lib.getGraphDiv(gd);
49374 helpers.clearPromiseQueue(gd);
49375
49376 if(gd.framework && gd.framework.isPolar) {
49377 return Promise.resolve(gd);
49378 }
49379
49380 var aobj = {};
49381 if(typeof astr === 'string') {
49382 aobj[astr] = val;
49383 } else if(Lib.isPlainObject(astr)) {
49384 aobj = Lib.extendFlat({}, astr);
49385 } else {
49386 Lib.warn('Relayout fail.', astr, val);
49387 return Promise.reject();
49388 }
49389
49390 if(Object.keys(aobj).length) gd.changed = true;
49391
49392 var specs = _relayout(gd, aobj);
49393 var flags = specs.flags;
49394
49395 // clear calcdata if required
49396 if(flags.calc) gd.calcdata = undefined;
49397
49398 // fill in redraw sequence
49399
49400 // even if we don't have anything left in aobj,
49401 // something may have happened within relayout that we
49402 // need to wait for
49403 var seq = [Plots.previousPromises];
49404
49405 if(flags.layoutReplot) {
49406 seq.push(subroutines.layoutReplot);
49407 } else if(Object.keys(aobj).length) {
49408 axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd);
49409
49410 if(flags.legend) seq.push(subroutines.doLegend);
49411 if(flags.layoutstyle) seq.push(subroutines.layoutStyles);
49412 if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered);
49413 if(flags.ticks) seq.push(subroutines.doTicksRelayout);
49414 if(flags.modebar) seq.push(subroutines.doModeBar);
49415 if(flags.camera) seq.push(subroutines.doCamera);
49416 if(flags.colorbars) seq.push(subroutines.doColorBars);
49417
49418 seq.push(emitAfterPlot);
49419 }
49420
49421 seq.push(Plots.rehover, Plots.redrag);
49422
49423 Queue.add(gd,
49424 relayout, [gd, specs.undoit],
49425 relayout, [gd, specs.redoit]
49426 );
49427
49428 var plotDone = Lib.syncOrAsync(seq, gd);
49429 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
49430
49431 return plotDone.then(function() {
49432 gd.emit('plotly_relayout', specs.eventData);
49433 return gd;
49434 });
49435}
49436
49437// Optimization mostly for large splom traces where
49438// Plots.supplyDefaults can take > 100ms
49439function axRangeSupplyDefaultsByPass(gd, flags, specs) {
49440 var fullLayout = gd._fullLayout;
49441
49442 if(!flags.axrange) return false;
49443
49444 for(var k in flags) {
49445 if(k !== 'axrange' && flags[k]) return false;
49446 }
49447
49448 for(var axId in specs.rangesAltered) {
49449 var axName = Axes.id2name(axId);
49450 var axIn = gd.layout[axName];
49451 var axOut = fullLayout[axName];
49452 axOut.autorange = axIn.autorange;
49453 axOut.range = axIn.range.slice();
49454 axOut.cleanRange();
49455
49456 if(axOut._matchGroup) {
49457 for(var axId2 in axOut._matchGroup) {
49458 if(axId2 !== axId) {
49459 var ax2 = fullLayout[Axes.id2name(axId2)];
49460 ax2.autorange = axOut.autorange;
49461 ax2.range = axOut.range.slice();
49462 ax2._input.range = axOut.range.slice();
49463 }
49464 }
49465 }
49466 }
49467
49468 return true;
49469}
49470
49471function addAxRangeSequence(seq, rangesAltered) {
49472 // N.B. leave as sequence of subroutines (for now) instead of
49473 // subroutine of its own so that finalDraw always gets
49474 // executed after drawData
49475 var drawAxes = rangesAltered ?
49476 function(gd) {
49477 var axIds = [];
49478 var skipTitle = true;
49479
49480 for(var id in rangesAltered) {
49481 var ax = Axes.getFromId(gd, id);
49482 axIds.push(id);
49483
49484 if(ax._matchGroup) {
49485 for(var id2 in ax._matchGroup) {
49486 if(!rangesAltered[id2]) {
49487 axIds.push(id2);
49488 }
49489 }
49490 }
49491
49492 if(ax.automargin) skipTitle = false;
49493 }
49494
49495 return Axes.draw(gd, axIds, {skipTitle: skipTitle});
49496 } :
49497 function(gd) {
49498 return Axes.draw(gd, 'redraw');
49499 };
49500
49501 seq.push(
49502 clearSelect,
49503 subroutines.doAutoRangeAndConstraints,
49504 drawAxes,
49505 subroutines.drawData,
49506 subroutines.finalDraw
49507 );
49508}
49509
49510var AX_RANGE_RE = /^[xyz]axis[0-9]*\.range(\[[0|1]\])?$/;
49511var AX_AUTORANGE_RE = /^[xyz]axis[0-9]*\.autorange$/;
49512var AX_DOMAIN_RE = /^[xyz]axis[0-9]*\.domain(\[[0|1]\])?$/;
49513
49514function _relayout(gd, aobj) {
49515 var layout = gd.layout;
49516 var fullLayout = gd._fullLayout;
49517 var guiEditFlag = fullLayout._guiEditing;
49518 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
49519 var keys = Object.keys(aobj);
49520 var axes = Axes.list(gd);
49521 var eventData = Lib.extendDeepAll({}, aobj);
49522 var arrayEdits = {};
49523
49524 var arrayStr, i, j;
49525
49526 cleanDeprecatedAttributeKeys(aobj);
49527 keys = Object.keys(aobj);
49528
49529 // look for 'allaxes', split out into all axes
49530 // in case of 3D the axis are nested within a scene which is held in _id
49531 for(i = 0; i < keys.length; i++) {
49532 if(keys[i].indexOf('allaxes') === 0) {
49533 for(j = 0; j < axes.length; j++) {
49534 var scene = axes[j]._id.substr(1);
49535 var axisAttr = (scene.indexOf('scene') !== -1) ? (scene + '.') : '';
49536 var newkey = keys[i].replace('allaxes', axisAttr + axes[j]._name);
49537
49538 if(!aobj[newkey]) aobj[newkey] = aobj[keys[i]];
49539 }
49540
49541 delete aobj[keys[i]];
49542 }
49543 }
49544
49545 // initialize flags
49546 var flags = editTypes.layoutFlags();
49547
49548 // copies of the change (and previous values of anything affected)
49549 // for the undo / redo queue
49550 var redoit = {};
49551 var undoit = {};
49552
49553 // for attrs that interact (like scales & autoscales), save the
49554 // old vals before making the change
49555 // val=undefined will not set a value, just record what the value was.
49556 // attr can be an array to set several at once (all to the same val)
49557 function doextra(attr, val) {
49558 if(Array.isArray(attr)) {
49559 attr.forEach(function(a) { doextra(a, val); });
49560 return;
49561 }
49562
49563 // if we have another value for this attribute (explicitly or
49564 // via a parent) do not override with this auto-generated extra
49565 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
49566
49567 var p = layoutNP(layout, attr);
49568 if(!(attr in undoit)) {
49569 undoit[attr] = undefinedToNull(p.get());
49570 }
49571 if(val !== undefined) p.set(val);
49572 }
49573
49574 // for constraint enforcement: keep track of all axes (as {id: name})
49575 // we're editing the (auto)range of, so we can tell the others constrained
49576 // to scale with them that it's OK for them to shrink
49577 var rangesAltered = {};
49578 var axId;
49579
49580 function recordAlteredAxis(pleafPlus) {
49581 var axId = Axes.name2id(pleafPlus.split('.')[0]);
49582 rangesAltered[axId] = 1;
49583 return axId;
49584 }
49585
49586 // alter gd.layout
49587 for(var ai in aobj) {
49588 if(helpers.hasParent(aobj, ai)) {
49589 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
49590 }
49591
49592 var p = layoutNP(layout, ai);
49593 var vi = aobj[ai];
49594 var plen = p.parts.length;
49595 // p.parts may end with an index integer if the property is an array
49596 var pend = plen - 1;
49597 while(pend > 0 && typeof p.parts[pend] !== 'string') pend--;
49598 // last property in chain (leaf node)
49599 var pleaf = p.parts[pend];
49600 // leaf plus immediate parent
49601 var pleafPlus = p.parts[pend - 1] + '.' + pleaf;
49602 // trunk nodes (everything except the leaf)
49603 var ptrunk = p.parts.slice(0, pend).join('.');
49604 var parentIn = nestedProperty(gd.layout, ptrunk).get();
49605 var parentFull = nestedProperty(fullLayout, ptrunk).get();
49606 var vOld = p.get();
49607
49608 if(vi === undefined) continue;
49609
49610 redoit[ai] = vi;
49611
49612 // axis reverse is special - it is its own inverse
49613 // op and has no flag.
49614 undoit[ai] = (pleaf === 'reverse') ? vi : undefinedToNull(vOld);
49615
49616 var valObject = PlotSchema.getLayoutValObject(fullLayout, p.parts);
49617
49618 if(valObject && valObject.impliedEdits && vi !== null) {
49619 for(var impliedKey in valObject.impliedEdits) {
49620 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey]);
49621 }
49622 }
49623
49624 // Setting width or height to null must reset the graph's width / height
49625 // back to its initial value as computed during the first pass in Plots.plotAutoSize.
49626 //
49627 // To do so, we must manually set them back here using the _initialAutoSize cache.
49628 // can't use impliedEdits for this because behavior depends on vi
49629 if(['width', 'height'].indexOf(ai) !== -1) {
49630 if(vi) {
49631 doextra('autosize', null);
49632 // currently we don't support autosize one dim only - so
49633 // explicitly set the other one. Note that doextra will
49634 // ignore this if the same relayout call also provides oppositeAttr
49635 var oppositeAttr = ai === 'height' ? 'width' : 'height';
49636 doextra(oppositeAttr, fullLayout[oppositeAttr]);
49637 } else {
49638 fullLayout[ai] = gd._initialAutoSize[ai];
49639 }
49640 } else if(ai === 'autosize') {
49641 // depends on vi here too, so again can't use impliedEdits
49642 doextra('width', vi ? null : fullLayout.width);
49643 doextra('height', vi ? null : fullLayout.height);
49644 } else if(pleafPlus.match(AX_RANGE_RE)) {
49645 // check autorange vs range
49646
49647 recordAlteredAxis(pleafPlus);
49648 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
49649 } else if(pleafPlus.match(AX_AUTORANGE_RE)) {
49650 recordAlteredAxis(pleafPlus);
49651 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
49652 var axFull = nestedProperty(fullLayout, ptrunk).get();
49653 if(axFull._inputDomain) {
49654 // if we're autoranging and this axis has a constrained domain,
49655 // reset it so we don't get locked into a shrunken size
49656 axFull._input.domain = axFull._inputDomain.slice();
49657 }
49658 } else if(pleafPlus.match(AX_DOMAIN_RE)) {
49659 nestedProperty(fullLayout, ptrunk + '._inputDomain').set(null);
49660 }
49661
49662 // toggling axis type between log and linear: we need to convert
49663 // positions for components that are still using linearized values,
49664 // not data values like newer components.
49665 // previously we did this for log <-> not-log, but now only do it
49666 // for log <-> linear
49667 if(pleaf === 'type') {
49668 var ax = parentIn;
49669 var toLog = parentFull.type === 'linear' && vi === 'log';
49670 var fromLog = parentFull.type === 'log' && vi === 'linear';
49671
49672 if(toLog || fromLog) {
49673 if(!ax || !ax.range) {
49674 // 2D never gets here, but 3D does
49675 // I don't think this is needed, but left here in case there
49676 // are edge cases I'm not thinking of.
49677 doextra(ptrunk + '.autorange', true);
49678 } else if(!parentFull.autorange) {
49679 // toggling log without autorange: need to also recalculate ranges
49680 // because log axes use linearized values for range endpoints
49681 var r0 = ax.range[0];
49682 var r1 = ax.range[1];
49683 if(toLog) {
49684 // if both limits are negative, autorange
49685 if(r0 <= 0 && r1 <= 0) {
49686 doextra(ptrunk + '.autorange', true);
49687 }
49688 // if one is negative, set it 6 orders below the other.
49689 if(r0 <= 0) r0 = r1 / 1e6;
49690 else if(r1 <= 0) r1 = r0 / 1e6;
49691 // now set the range values as appropriate
49692 doextra(ptrunk + '.range[0]', Math.log(r0) / Math.LN10);
49693 doextra(ptrunk + '.range[1]', Math.log(r1) / Math.LN10);
49694 } else {
49695 doextra(ptrunk + '.range[0]', Math.pow(10, r0));
49696 doextra(ptrunk + '.range[1]', Math.pow(10, r1));
49697 }
49698 } else if(toLog) {
49699 // just make sure the range is positive and in the right
49700 // order, it'll get recalculated later
49701 ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1];
49702 }
49703
49704 // clear polar view initial stash for radial range so that
49705 // value get recomputed in correct units
49706 if(Array.isArray(fullLayout._subplots.polar) &&
49707 fullLayout._subplots.polar.length &&
49708 fullLayout[p.parts[0]] &&
49709 p.parts[1] === 'radialaxis'
49710 ) {
49711 delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range'];
49712 }
49713
49714 // Annotations and images also need to convert to/from linearized coords
49715 // Shapes do not need this :)
49716 Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra);
49717 Registry.getComponentMethod('images', 'convertCoords')(gd, parentFull, vi, doextra);
49718 } else {
49719 // any other type changes: the range from the previous type
49720 // will not make sense, so autorange it.
49721 doextra(ptrunk + '.autorange', true);
49722 doextra(ptrunk + '.range', null);
49723 }
49724 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
49725 } else if(pleaf.match(AX_NAME_PATTERN)) {
49726 var fullProp = nestedProperty(fullLayout, ai).get();
49727 var newType = (vi || {}).type;
49728
49729 // This can potentially cause strange behavior if the autotype is not
49730 // numeric (linear, because we don't auto-log) but the previous type
49731 // was log. That's a very strange edge case though
49732 if(!newType || newType === '-') newType = 'linear';
49733 Registry.getComponentMethod('annotations', 'convertCoords')(gd, fullProp, newType, doextra);
49734 Registry.getComponentMethod('images', 'convertCoords')(gd, fullProp, newType, doextra);
49735 }
49736
49737 // alter gd.layout
49738
49739 // collect array component edits for execution all together
49740 // so we can ensure consistent behavior adding/removing items
49741 // and order-independence for add/remove/edit all together in
49742 // one relayout call
49743 var containerArrayMatch = manageArrays.containerArrayMatch(ai);
49744 if(containerArrayMatch) {
49745 arrayStr = containerArrayMatch.array;
49746 i = containerArrayMatch.index;
49747 var propStr = containerArrayMatch.property;
49748 var updateValObject = valObject || {editType: 'calc'};
49749
49750 if(i !== '' && propStr === '') {
49751 // special handling of undoit if we're adding or removing an element
49752 // ie 'annotations[2]' which can be {...} (add) or null,
49753 // does not work when replacing the entire array
49754 if(manageArrays.isAddVal(vi)) {
49755 undoit[ai] = null;
49756 } else if(manageArrays.isRemoveVal(vi)) {
49757 undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i];
49758 } else {
49759 Lib.warn('unrecognized full object value', aobj);
49760 }
49761 }
49762 editTypes.update(flags, updateValObject);
49763
49764 // prepare the edits object we'll send to applyContainerArrayChanges
49765 if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};
49766 var objEdits = arrayEdits[arrayStr][i];
49767 if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {};
49768 objEdits[propStr] = vi;
49769
49770 delete aobj[ai];
49771 } else if(pleaf === 'reverse') {
49772 // handle axis reversal explicitly, as there's no 'reverse' attribute
49773
49774 if(parentIn.range) parentIn.range.reverse();
49775 else {
49776 doextra(ptrunk + '.autorange', true);
49777 parentIn.range = [1, 0];
49778 }
49779
49780 if(parentFull.autorange) flags.calc = true;
49781 else flags.plot = true;
49782 } else {
49783 if((fullLayout._has('scatter-like') && fullLayout._has('regl')) &&
49784 (ai === 'dragmode' &&
49785 (vi === 'lasso' || vi === 'select') &&
49786 !(vOld === 'lasso' || vOld === 'select'))
49787 ) {
49788 flags.plot = true;
49789 } else if(fullLayout._has('gl2d')) {
49790 flags.plot = true;
49791 } else if(valObject) editTypes.update(flags, valObject);
49792 else flags.calc = true;
49793
49794 p.set(vi);
49795 }
49796 }
49797
49798 // now we've collected component edits - execute them all together
49799 for(arrayStr in arrayEdits) {
49800 var finished = manageArrays.applyContainerArrayChanges(gd,
49801 layoutNP(layout, arrayStr), arrayEdits[arrayStr], flags, layoutNP);
49802 if(!finished) flags.plot = true;
49803 }
49804
49805 // figure out if we need to recalculate axis constraints
49806 var constraints = fullLayout._axisConstraintGroups || [];
49807 for(axId in rangesAltered) {
49808 for(i = 0; i < constraints.length; i++) {
49809 var group = constraints[i];
49810 if(group[axId]) {
49811 // Always recalc if we're changing constrained ranges.
49812 // Otherwise it's possible to violate the constraints by
49813 // specifying arbitrary ranges for all axes in the group.
49814 // this way some ranges may expand beyond what's specified,
49815 // as they do at first draw, to satisfy the constraints.
49816 flags.calc = true;
49817 for(var groupAxId in group) {
49818 if(!rangesAltered[groupAxId]) {
49819 Axes.getFromId(gd, groupAxId)._constraintShrinkable = true;
49820 }
49821 }
49822 }
49823 }
49824 }
49825
49826 // If the autosize changed or height or width was explicitly specified,
49827 // this triggers a redraw
49828 // TODO: do we really need special aobj.height/width handling here?
49829 // couldn't editType do this?
49830 if(updateAutosize(gd) || aobj.height || aobj.width) flags.plot = true;
49831
49832 if(flags.plot || flags.calc) {
49833 flags.layoutReplot = true;
49834 }
49835
49836 // now all attribute mods are done, as are
49837 // redo and undo so we can save them
49838
49839 return {
49840 flags: flags,
49841 rangesAltered: rangesAltered,
49842 undoit: undoit,
49843 redoit: redoit,
49844 eventData: eventData
49845 };
49846}
49847
49848/*
49849 * updateAutosize: we made a change, does it change the autosize result?
49850 * puts the new size into fullLayout
49851 * returns true if either height or width changed
49852 */
49853function updateAutosize(gd) {
49854 var fullLayout = gd._fullLayout;
49855 var oldWidth = fullLayout.width;
49856 var oldHeight = fullLayout.height;
49857
49858 // calculate autosizing
49859 if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout);
49860
49861 return (fullLayout.width !== oldWidth) || (fullLayout.height !== oldHeight);
49862}
49863
49864/**
49865 * update: update trace and layout attributes of an existing plot
49866 *
49867 * @param {String | HTMLDivElement} gd
49868 * the id or DOM element of the graph container div
49869 * @param {Object} traceUpdate
49870 * attribute object `{astr1: val1, astr2: val2 ...}`
49871 * corresponding to updates in the plot's traces
49872 * @param {Object} layoutUpdate
49873 * attribute object `{astr1: val1, astr2: val2 ...}`
49874 * corresponding to updates in the plot's layout
49875 * @param {Number[] | Number} [traces]
49876 * integer or array of integers for the traces to alter (all if omitted)
49877 *
49878 */
49879function update(gd, traceUpdate, layoutUpdate, _traces) {
49880 gd = Lib.getGraphDiv(gd);
49881 helpers.clearPromiseQueue(gd);
49882
49883 if(gd.framework && gd.framework.isPolar) {
49884 return Promise.resolve(gd);
49885 }
49886
49887 if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {};
49888 if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {};
49889
49890 if(Object.keys(traceUpdate).length) gd.changed = true;
49891 if(Object.keys(layoutUpdate).length) gd.changed = true;
49892
49893 var traces = helpers.coerceTraceIndices(gd, _traces);
49894
49895 var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces);
49896 var restyleFlags = restyleSpecs.flags;
49897
49898 var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate));
49899 var relayoutFlags = relayoutSpecs.flags;
49900
49901 // clear calcdata and/or axis types if required
49902 if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
49903 if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate);
49904
49905 // fill in redraw sequence
49906 var seq = [];
49907
49908 if(relayoutFlags.layoutReplot) {
49909 // N.B. works fine when both
49910 // relayoutFlags.layoutReplot and restyleFlags.fullReplot are true
49911 seq.push(subroutines.layoutReplot);
49912 } else if(restyleFlags.fullReplot) {
49913 seq.push(exports.plot);
49914 } else {
49915 seq.push(Plots.previousPromises);
49916 axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd);
49917
49918 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
49919 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
49920 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
49921 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
49922 if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered);
49923 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
49924 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
49925 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
49926
49927 seq.push(emitAfterPlot);
49928 }
49929
49930 seq.push(Plots.rehover, Plots.redrag);
49931
49932 Queue.add(gd,
49933 update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
49934 update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]
49935 );
49936
49937 var plotDone = Lib.syncOrAsync(seq, gd);
49938 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
49939
49940 return plotDone.then(function() {
49941 gd.emit('plotly_update', {
49942 data: restyleSpecs.eventData,
49943 layout: relayoutSpecs.eventData
49944 });
49945
49946 return gd;
49947 });
49948}
49949
49950/*
49951 * internal-use-only restyle/relayout/update variants that record the initial
49952 * values in (fullLayout|fullTrace)._preGUI so changes can be persisted across
49953 * Plotly.react data updates, dependent on uirevision attributes
49954 */
49955function guiEdit(func) {
49956 return function wrappedEdit(gd) {
49957 gd._fullLayout._guiEditing = true;
49958 var p = func.apply(null, arguments);
49959 gd._fullLayout._guiEditing = false;
49960 return p;
49961 };
49962}
49963
49964// For connecting edited layout attributes to uirevision attrs
49965// If no `attr` we use `match[1] + '.uirevision'`
49966// Ordered by most common edits first, to minimize our search time
49967var layoutUIControlPatterns = [
49968 {pattern: /^hiddenlabels/, attr: 'legend.uirevision'},
49969 {pattern: /^((x|y)axis\d*)\.((auto)?range|title\.text)/},
49970
49971 // showspikes and modes include those nested inside scenes
49972 {pattern: /axis\d*\.showspikes$/, attr: 'modebar.uirevision'},
49973 {pattern: /(hover|drag)mode$/, attr: 'modebar.uirevision'},
49974
49975 {pattern: /^(scene\d*)\.camera/},
49976 {pattern: /^(geo\d*)\.(projection|center|fitbounds)/},
49977 {pattern: /^(ternary\d*\.[abc]axis)\.(min|title\.text)$/},
49978 {pattern: /^(polar\d*\.radialaxis)\.((auto)?range|angle|title\.text)/},
49979 {pattern: /^(polar\d*\.angularaxis)\.rotation/},
49980 {pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/},
49981
49982 {pattern: /^legend\.(x|y)$/, attr: 'editrevision'},
49983 {pattern: /^(shapes|annotations)/, attr: 'editrevision'},
49984 {pattern: /^title\.text$/, attr: 'editrevision'}
49985];
49986
49987// same for trace attributes: if `attr` is given it's in layout,
49988// or with no `attr` we use `trace.uirevision`
49989var traceUIControlPatterns = [
49990 {pattern: /^selectedpoints$/, attr: 'selectionrevision'},
49991 // "visible" includes trace.transforms[i].styles[j].value.visible
49992 {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'},
49993 {pattern: /^dimensions\[\d+\]\.constraintrange/},
49994 {pattern: /^node\.(x|y|groups)/}, // for Sankey nodes
49995 {pattern: /^level$/}, // for Sunburst & Treemap traces
49996
49997 // below this you must be in editable: true mode
49998 // TODO: I still put name and title with `trace.uirevision`
49999 // reasonable or should these be `editrevision`?
50000 // Also applies to axis titles up in the layout section
50001
50002 // "name" also includes transform.styles
50003 {pattern: /(^|value\.)name$/},
50004 // including nested colorbar attributes (ie marker.colorbar)
50005 {pattern: /colorbar\.title\.text$/},
50006 {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'}
50007];
50008
50009function findUIPattern(key, patternSpecs) {
50010 for(var i = 0; i < patternSpecs.length; i++) {
50011 var spec = patternSpecs[i];
50012 var match = key.match(spec.pattern);
50013 if(match) {
50014 return {head: match[1], attr: spec.attr};
50015 }
50016 }
50017}
50018
50019// We're finding the new uirevision before supplyDefaults, so do the
50020// inheritance manually. Note that only `undefined` inherits - other
50021// falsy values are returned.
50022function getNewRev(revAttr, container) {
50023 var newRev = nestedProperty(container, revAttr).get();
50024 if(newRev !== undefined) return newRev;
50025
50026 var parts = revAttr.split('.');
50027 parts.pop();
50028 while(parts.length > 1) {
50029 parts.pop();
50030 newRev = nestedProperty(container, parts.join('.') + '.uirevision').get();
50031 if(newRev !== undefined) return newRev;
50032 }
50033
50034 return container.uirevision;
50035}
50036
50037function getFullTraceIndexFromUid(uid, fullData) {
50038 for(var i = 0; i < fullData.length; i++) {
50039 if(fullData[i]._fullInput.uid === uid) return i;
50040 }
50041 return -1;
50042}
50043
50044function getTraceIndexFromUid(uid, data, tracei) {
50045 for(var i = 0; i < data.length; i++) {
50046 if(data[i].uid === uid) return i;
50047 }
50048 // fall back on trace order, but only if user didn't provide a uid for that trace
50049 return (!data[tracei] || data[tracei].uid) ? -1 : tracei;
50050}
50051
50052function valsMatch(v1, v2) {
50053 var v1IsObj = Lib.isPlainObject(v1);
50054 var v1IsArray = Array.isArray(v1);
50055 if(v1IsObj || v1IsArray) {
50056 return (
50057 (v1IsObj && Lib.isPlainObject(v2)) ||
50058 (v1IsArray && Array.isArray(v2))
50059 ) && JSON.stringify(v1) === JSON.stringify(v2);
50060 }
50061 return v1 === v2;
50062}
50063
50064function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
50065 var layoutPreGUI = oldFullLayout._preGUI;
50066 var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
50067 var bothInheritAutorange = [];
50068 var newRangeAccepted = {};
50069 for(key in layoutPreGUI) {
50070 match = findUIPattern(key, layoutUIControlPatterns);
50071 if(match) {
50072 revAttr = match.attr || (match.head + '.uirevision');
50073 oldRev = nestedProperty(oldFullLayout, revAttr).get();
50074 newRev = oldRev && getNewRev(revAttr, layout);
50075 if(newRev && (newRev === oldRev)) {
50076 preGUIVal = layoutPreGUI[key];
50077 if(preGUIVal === null) preGUIVal = undefined;
50078 newNP = nestedProperty(layout, key);
50079 newVal = newNP.get();
50080 if(valsMatch(newVal, preGUIVal)) {
50081 if(newVal === undefined && key.substr(key.length - 9) === 'autorange') {
50082 bothInheritAutorange.push(key.substr(0, key.length - 10));
50083 }
50084 newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
50085 continue;
50086 }
50087 }
50088 } else {
50089 Lib.warn('unrecognized GUI edit: ' + key);
50090 }
50091 // if we got this far, the new value was accepted as the new starting
50092 // point (either because it changed or revision changed)
50093 // so remove it from _preGUI for next time.
50094 delete layoutPreGUI[key];
50095
50096 if(key.substr(key.length - 8, 6) === 'range[') {
50097 newRangeAccepted[key.substr(0, key.length - 9)] = 1;
50098 }
50099 }
50100
50101 // Special logic for `autorange`, since it interacts with `range`:
50102 // If the new figure's matching `range` was kept, and `autorange`
50103 // wasn't supplied explicitly in either the original or the new figure,
50104 // we shouldn't alter that - but we may just have done that, so fix it.
50105 for(var i = 0; i < bothInheritAutorange.length; i++) {
50106 var axAttr = bothInheritAutorange[i];
50107 if(newRangeAccepted[axAttr]) {
50108 var newAx = nestedProperty(layout, axAttr).get();
50109 if(newAx) delete newAx.autorange;
50110 }
50111 }
50112
50113 // Now traces - try to match them up by uid (in case we added/deleted in
50114 // the middle), then fall back on index.
50115 var allTracePreGUI = oldFullLayout._tracePreGUI;
50116 for(var uid in allTracePreGUI) {
50117 var tracePreGUI = allTracePreGUI[uid];
50118 var newTrace = null;
50119 var fullInput;
50120 for(key in tracePreGUI) {
50121 // wait until we know we have preGUI values to look for traces
50122 // but if we don't find both, stop looking at this uid
50123 if(!newTrace) {
50124 var fulli = getFullTraceIndexFromUid(uid, oldFullData);
50125 if(fulli < 0) {
50126 // Somehow we didn't even have this trace in oldFullData...
50127 // I guess this could happen with `deleteTraces` or something
50128 delete allTracePreGUI[uid];
50129 break;
50130 }
50131 var fullTrace = oldFullData[fulli];
50132 fullInput = fullTrace._fullInput;
50133
50134 var newTracei = getTraceIndexFromUid(uid, data, fullInput.index);
50135 if(newTracei < 0) {
50136 // No match in new data
50137 delete allTracePreGUI[uid];
50138 break;
50139 }
50140 newTrace = data[newTracei];
50141 }
50142
50143 match = findUIPattern(key, traceUIControlPatterns);
50144 if(match) {
50145 if(match.attr) {
50146 oldRev = nestedProperty(oldFullLayout, match.attr).get();
50147 newRev = oldRev && getNewRev(match.attr, layout);
50148 } else {
50149 oldRev = fullInput.uirevision;
50150 // inheritance for trace.uirevision is simple, just layout.uirevision
50151 newRev = newTrace.uirevision;
50152 if(newRev === undefined) newRev = layout.uirevision;
50153 }
50154
50155 if(newRev && newRev === oldRev) {
50156 preGUIVal = tracePreGUI[key];
50157 if(preGUIVal === null) preGUIVal = undefined;
50158 newNP = nestedProperty(newTrace, key);
50159 newVal = newNP.get();
50160 if(valsMatch(newVal, preGUIVal)) {
50161 newNP.set(undefinedToNull(nestedProperty(fullInput, key).get()));
50162 continue;
50163 }
50164 }
50165 } else {
50166 Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid);
50167 }
50168 delete tracePreGUI[key];
50169 }
50170 }
50171}
50172
50173/**
50174 * Plotly.react:
50175 * A plot/update method that takes the full plot state (same API as plot/newPlot)
50176 * and diffs to determine the minimal update pathway
50177 *
50178 * @param {string id or DOM element} gd
50179 * the id or DOM element of the graph container div
50180 * @param {array of objects} data
50181 * array of traces, containing the data and display information for each trace
50182 * @param {object} layout
50183 * object describing the overall display of the plot,
50184 * all the stuff that doesn't pertain to any individual trace
50185 * @param {object} config
50186 * configuration options (see ./plot_config.js for more info)
50187 *
50188 * OR
50189 *
50190 * @param {string id or DOM element} gd
50191 * the id or DOM element of the graph container div
50192 * @param {object} figure
50193 * object containing `data`, `layout`, `config`, and `frames` members
50194 *
50195 */
50196function react(gd, data, layout, config) {
50197 var frames, plotDone;
50198
50199 function addFrames() { return exports.addFrames(gd, frames); }
50200
50201 gd = Lib.getGraphDiv(gd);
50202 helpers.clearPromiseQueue(gd);
50203
50204 var oldFullData = gd._fullData;
50205 var oldFullLayout = gd._fullLayout;
50206
50207 // you can use this as the initial draw as well as to update
50208 if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) {
50209 plotDone = exports.newPlot(gd, data, layout, config);
50210 } else {
50211 if(Lib.isPlainObject(data)) {
50212 var obj = data;
50213 data = obj.data;
50214 layout = obj.layout;
50215 config = obj.config;
50216 frames = obj.frames;
50217 }
50218
50219 var configChanged = false;
50220 // assume that if there's a config at all, we're reacting to it too,
50221 // and completely replace the previous config
50222 if(config) {
50223 var oldConfig = Lib.extendDeep({}, gd._context);
50224 gd._context = undefined;
50225 setPlotContext(gd, config);
50226 configChanged = diffConfig(oldConfig, gd._context);
50227 }
50228
50229 gd.data = data || [];
50230 helpers.cleanData(gd.data);
50231 gd.layout = layout || {};
50232 helpers.cleanLayout(gd.layout);
50233
50234 applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout);
50235
50236 // "true" skips updating calcdata and remapping arrays from calcTransforms,
50237 // which supplyDefaults usually does at the end, but we may need to NOT do
50238 // if the diff (which we haven't determined yet) says we'll recalc
50239 Plots.supplyDefaults(gd, {skipUpdateCalc: true});
50240
50241 var newFullData = gd._fullData;
50242 var newFullLayout = gd._fullLayout;
50243 var immutable = newFullLayout.datarevision === undefined;
50244 var transition = newFullLayout.transition;
50245
50246 var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition);
50247 var newDataRevision = relayoutFlags.newDataRevision;
50248 var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision);
50249
50250 // TODO: how to translate this part of relayout to Plotly.react?
50251 // // Setting width or height to null must reset the graph's width / height
50252 // // back to its initial value as computed during the first pass in Plots.plotAutoSize.
50253 // //
50254 // // To do so, we must manually set them back here using the _initialAutoSize cache.
50255 // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) {
50256 // fullLayout[ai] = gd._initialAutoSize[ai];
50257 // }
50258
50259 if(updateAutosize(gd)) relayoutFlags.layoutReplot = true;
50260
50261 // clear calcdata if required
50262 if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
50263 // otherwise do the calcdata updates and calcTransform array remaps that we skipped earlier
50264 else Plots.supplyDefaultsUpdateCalc(gd.calcdata, newFullData);
50265
50266 // Note: what restyle/relayout use impliedEdits and clearAxisTypes for
50267 // must be handled by the user when using Plotly.react.
50268
50269 // fill in redraw sequence
50270 var seq = [];
50271
50272 if(frames) {
50273 gd._transitionData = {};
50274 Plots.createTransitionData(gd);
50275 seq.push(addFrames);
50276 }
50277
50278 // Transition pathway,
50279 // only used when 'transition' is set by user and
50280 // when at least one animatable attribute has changed,
50281 // N.B. config changed aren't animatable
50282 if(newFullLayout.transition && !configChanged && (restyleFlags.anim || relayoutFlags.anim)) {
50283 Plots.doCalcdata(gd);
50284 subroutines.doAutoRangeAndConstraints(gd);
50285
50286 seq.push(function() {
50287 return Plots.transitionFromReact(gd, restyleFlags, relayoutFlags, oldFullLayout);
50288 });
50289 } else if(restyleFlags.fullReplot || relayoutFlags.layoutReplot || configChanged) {
50290 gd._fullLayout._skipDefaults = true;
50291 seq.push(exports.plot);
50292 } else {
50293 for(var componentType in relayoutFlags.arrays) {
50294 var indices = relayoutFlags.arrays[componentType];
50295 if(indices.length) {
50296 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
50297 if(drawOne !== Lib.noop) {
50298 for(var i = 0; i < indices.length; i++) {
50299 drawOne(gd, indices[i]);
50300 }
50301 } else {
50302 var draw = Registry.getComponentMethod(componentType, 'draw');
50303 if(draw === Lib.noop) {
50304 throw new Error('cannot draw components: ' + componentType);
50305 }
50306 draw(gd);
50307 }
50308 }
50309 }
50310
50311 seq.push(Plots.previousPromises);
50312 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
50313 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
50314 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
50315 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
50316 if(relayoutFlags.axrange) addAxRangeSequence(seq);
50317 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
50318 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
50319 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
50320 seq.push(emitAfterPlot);
50321 }
50322
50323 seq.push(Plots.rehover, Plots.redrag);
50324
50325 plotDone = Lib.syncOrAsync(seq, gd);
50326 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
50327 }
50328
50329 return plotDone.then(function() {
50330 gd.emit('plotly_react', {
50331 data: data,
50332 layout: layout
50333 });
50334
50335 return gd;
50336 });
50337}
50338
50339function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) {
50340 var sameTraceLength = oldFullData.length === newFullData.length;
50341
50342 if(!transition && !sameTraceLength) {
50343 return {
50344 fullReplot: true,
50345 calc: true
50346 };
50347 }
50348
50349 var flags = editTypes.traceFlags();
50350 flags.arrays = {};
50351 flags.nChanges = 0;
50352 flags.nChangesAnim = 0;
50353
50354 var i, trace;
50355
50356 function getTraceValObject(parts) {
50357 var out = PlotSchema.getTraceValObject(trace, parts);
50358 if(!trace._module.animatable && out.anim) {
50359 out.anim = false;
50360 }
50361 return out;
50362 }
50363
50364 var diffOpts = {
50365 getValObject: getTraceValObject,
50366 flags: flags,
50367 immutable: immutable,
50368 transition: transition,
50369 newDataRevision: newDataRevision,
50370 gd: gd
50371 };
50372
50373 var seenUIDs = {};
50374
50375 for(i = 0; i < oldFullData.length; i++) {
50376 if(newFullData[i]) {
50377 trace = newFullData[i]._fullInput;
50378 if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
50379 if(seenUIDs[trace.uid]) continue;
50380 seenUIDs[trace.uid] = 1;
50381
50382 getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
50383 }
50384 }
50385
50386 if(flags.calc || flags.plot) {
50387 flags.fullReplot = true;
50388 }
50389
50390 if(transition && flags.nChanges && flags.nChangesAnim) {
50391 flags.anim = (flags.nChanges === flags.nChangesAnim) && sameTraceLength ? 'all' : 'some';
50392 }
50393
50394 return flags;
50395}
50396
50397function diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition) {
50398 var flags = editTypes.layoutFlags();
50399 flags.arrays = {};
50400 flags.rangesAltered = {};
50401 flags.nChanges = 0;
50402 flags.nChangesAnim = 0;
50403
50404 function getLayoutValObject(parts) {
50405 return PlotSchema.getLayoutValObject(newFullLayout, parts);
50406 }
50407
50408 var diffOpts = {
50409 getValObject: getLayoutValObject,
50410 flags: flags,
50411 immutable: immutable,
50412 transition: transition,
50413 gd: gd
50414 };
50415
50416 getDiffFlags(oldFullLayout, newFullLayout, [], diffOpts);
50417
50418 if(flags.plot || flags.calc) {
50419 flags.layoutReplot = true;
50420 }
50421
50422 if(transition && flags.nChanges && flags.nChangesAnim) {
50423 flags.anim = flags.nChanges === flags.nChangesAnim ? 'all' : 'some';
50424 }
50425
50426 return flags;
50427}
50428
50429function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
50430 var valObject, key, astr;
50431
50432 var getValObject = opts.getValObject;
50433 var flags = opts.flags;
50434 var immutable = opts.immutable;
50435 var inArray = opts.inArray;
50436 var arrayIndex = opts.arrayIndex;
50437
50438 function changed() {
50439 var editType = valObject.editType;
50440 if(inArray && editType.indexOf('arraydraw') !== -1) {
50441 Lib.pushUnique(flags.arrays[inArray], arrayIndex);
50442 return;
50443 }
50444 editTypes.update(flags, valObject);
50445
50446 if(editType !== 'none') {
50447 flags.nChanges++;
50448 }
50449
50450 // track animatable changes
50451 if(opts.transition && valObject.anim) {
50452 flags.nChangesAnim++;
50453 }
50454
50455 // track cartesian axes with altered ranges
50456 if(AX_RANGE_RE.test(astr) || AX_AUTORANGE_RE.test(astr)) {
50457 flags.rangesAltered[outerparts[0]] = 1;
50458 }
50459
50460 // clear _inputDomain on cartesian axes with altered domains
50461 if(AX_DOMAIN_RE.test(astr)) {
50462 nestedProperty(newContainer, '_inputDomain').set(null);
50463 }
50464
50465 // track datarevision changes
50466 if(key === 'datarevision') {
50467 flags.newDataRevision = 1;
50468 }
50469 }
50470
50471 function valObjectCanBeDataArray(valObject) {
50472 return valObject.valType === 'data_array' || valObject.arrayOk;
50473 }
50474
50475 for(key in oldContainer) {
50476 // short-circuit based on previous calls or previous keys that already maximized the pathway
50477 if(flags.calc && !opts.transition) return;
50478
50479 var oldVal = oldContainer[key];
50480 var newVal = newContainer[key];
50481 var parts = outerparts.concat(key);
50482 astr = parts.join('.');
50483
50484 if(key.charAt(0) === '_' || typeof oldVal === 'function' || oldVal === newVal) continue;
50485
50486 // FIXME: ax.tick0 and dtick get filled in during plotting (except for geo subplots),
50487 // and unlike other auto values they don't make it back into the input,
50488 // so newContainer won't have them.
50489 if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
50490 var tickMode = newContainer.tickmode;
50491 if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
50492 }
50493 // FIXME: Similarly for axis ranges for 3D
50494 // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
50495 if(key === 'range' && newContainer.autorange) continue;
50496 if((key === 'zmin' || key === 'zmax') && newContainer.type === 'contourcarpet') continue;
50497
50498 valObject = getValObject(parts);
50499
50500 // in case type changed, we may not even *have* a valObject.
50501 if(!valObject) continue;
50502
50503 if(valObject._compareAsJSON && JSON.stringify(oldVal) === JSON.stringify(newVal)) continue;
50504
50505 var valType = valObject.valType;
50506 var i;
50507
50508 var canBeDataArray = valObjectCanBeDataArray(valObject);
50509 var wasArray = Array.isArray(oldVal);
50510 var nowArray = Array.isArray(newVal);
50511
50512 // hack for traces that modify the data in supplyDefaults, like
50513 // converting 1D to 2D arrays, which will always create new objects
50514 if(wasArray && nowArray) {
50515 var inputKey = '_input_' + key;
50516 var oldValIn = oldContainer[inputKey];
50517 var newValIn = newContainer[inputKey];
50518 if(Array.isArray(oldValIn) && oldValIn === newValIn) continue;
50519 }
50520
50521 if(newVal === undefined) {
50522 if(canBeDataArray && wasArray) flags.calc = true;
50523 else changed();
50524 } else if(valObject._isLinkedToArray) {
50525 var arrayEditIndices = [];
50526 var extraIndices = false;
50527 if(!inArray) flags.arrays[key] = arrayEditIndices;
50528
50529 var minLen = Math.min(oldVal.length, newVal.length);
50530 var maxLen = Math.max(oldVal.length, newVal.length);
50531 if(minLen !== maxLen) {
50532 if(valObject.editType === 'arraydraw') {
50533 extraIndices = true;
50534 } else {
50535 changed();
50536 continue;
50537 }
50538 }
50539
50540 for(i = 0; i < minLen; i++) {
50541 getDiffFlags(oldVal[i], newVal[i], parts.concat(i),
50542 // add array indices, but not if we're already in an array
50543 Lib.extendFlat({inArray: key, arrayIndex: i}, opts));
50544 }
50545
50546 // put this at the end so that we know our collected array indices are sorted
50547 // but the check for length changes happens up front so we can short-circuit
50548 // diffing if appropriate
50549 if(extraIndices) {
50550 for(i = minLen; i < maxLen; i++) {
50551 arrayEditIndices.push(i);
50552 }
50553 }
50554 } else if(!valType && Lib.isPlainObject(oldVal)) {
50555 getDiffFlags(oldVal, newVal, parts, opts);
50556 } else if(canBeDataArray) {
50557 if(wasArray && nowArray) {
50558 // don't try to diff two data arrays. If immutable we know the data changed,
50559 // if not, assume it didn't and let `layout.datarevision` tell us if it did
50560 if(immutable) {
50561 flags.calc = true;
50562 }
50563
50564 // look for animatable attributes when the data changed
50565 if(immutable || opts.newDataRevision) {
50566 changed();
50567 }
50568 } else if(wasArray !== nowArray) {
50569 flags.calc = true;
50570 } else changed();
50571 } else if(wasArray && nowArray) {
50572 // info array, colorscale, 'any' - these are short, just stringify.
50573 // I don't *think* that covers up any real differences post-validation, does it?
50574 // otherwise we need to dive in 1 (info_array) or 2 (colorscale) levels and compare
50575 // all elements.
50576 if(oldVal.length !== newVal.length || String(oldVal) !== String(newVal)) {
50577 changed();
50578 }
50579 } else {
50580 changed();
50581 }
50582 }
50583
50584 for(key in newContainer) {
50585 if(!(key in oldContainer || key.charAt(0) === '_' || typeof newContainer[key] === 'function')) {
50586 valObject = getValObject(outerparts.concat(key));
50587
50588 if(valObjectCanBeDataArray(valObject) && Array.isArray(newContainer[key])) {
50589 flags.calc = true;
50590 return;
50591 } else changed();
50592 }
50593 }
50594}
50595
50596/*
50597 * simple diff for config - for now, just treat all changes as equivalent
50598 */
50599function diffConfig(oldConfig, newConfig) {
50600 var key;
50601
50602 for(key in oldConfig) {
50603 if(key.charAt(0) === '_') continue;
50604 var oldVal = oldConfig[key];
50605 var newVal = newConfig[key];
50606 if(oldVal !== newVal) {
50607 if(Lib.isPlainObject(oldVal) && Lib.isPlainObject(newVal)) {
50608 if(diffConfig(oldVal, newVal)) {
50609 return true;
50610 }
50611 } else if(Array.isArray(oldVal) && Array.isArray(newVal)) {
50612 if(oldVal.length !== newVal.length) {
50613 return true;
50614 }
50615 for(var i = 0; i < oldVal.length; i++) {
50616 if(oldVal[i] !== newVal[i]) {
50617 if(Lib.isPlainObject(oldVal[i]) && Lib.isPlainObject(newVal[i])) {
50618 if(diffConfig(oldVal[i], newVal[i])) {
50619 return true;
50620 }
50621 } else {
50622 return true;
50623 }
50624 }
50625 }
50626 } else {
50627 return true;
50628 }
50629 }
50630 }
50631}
50632
50633/**
50634 * Animate to a frame, sequence of frame, frame group, or frame definition
50635 *
50636 * @param {string id or DOM element} gd
50637 * the id or DOM element of the graph container div
50638 *
50639 * @param {string or object or array of strings or array of objects} frameOrGroupNameOrFrameList
50640 * a single frame, array of frames, or group to which to animate. The intent is
50641 * inferred by the type of the input. Valid inputs are:
50642 *
50643 * - string, e.g. 'groupname': animate all frames of a given `group` in the order
50644 * in which they are defined via `Plotly.addFrames`.
50645 *
50646 * - array of strings, e.g. ['frame1', frame2']: a list of frames by name to which
50647 * to animate in sequence
50648 *
50649 * - object: {data: ...}: a frame definition to which to animate. The frame is not
50650 * and does not need to be added via `Plotly.addFrames`. It may contain any of
50651 * the properties of a frame, including `data`, `layout`, and `traces`. The
50652 * frame is used as provided and does not use the `baseframe` property.
50653 *
50654 * - array of objects, e.g. [{data: ...}, {data: ...}]: a list of frame objects,
50655 * each following the same rules as a single `object`.
50656 *
50657 * @param {object} animationOpts
50658 * configuration for the animation
50659 */
50660function animate(gd, frameOrGroupNameOrFrameList, animationOpts) {
50661 gd = Lib.getGraphDiv(gd);
50662
50663 if(!Lib.isPlotDiv(gd)) {
50664 throw new Error(
50665 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
50666 'to create a plot before animating it. For more details, see ' +
50667 'https://plotly.com/javascript/animations/'
50668 );
50669 }
50670
50671 var trans = gd._transitionData;
50672
50673 // This is the queue of frames that will be animated as soon as possible. They
50674 // are popped immediately upon the *start* of a transition:
50675 if(!trans._frameQueue) {
50676 trans._frameQueue = [];
50677 }
50678
50679 animationOpts = Plots.supplyAnimationDefaults(animationOpts);
50680 var transitionOpts = animationOpts.transition;
50681 var frameOpts = animationOpts.frame;
50682
50683 // Since frames are popped immediately, an empty queue only means all frames have
50684 // *started* to transition, not that the animation is complete. To solve that,
50685 // track a separate counter that increments at the same time as frames are added
50686 // to the queue, but decrements only when the transition is complete.
50687 if(trans._frameWaitingCnt === undefined) {
50688 trans._frameWaitingCnt = 0;
50689 }
50690
50691 function getTransitionOpts(i) {
50692 if(Array.isArray(transitionOpts)) {
50693 if(i >= transitionOpts.length) {
50694 return transitionOpts[0];
50695 } else {
50696 return transitionOpts[i];
50697 }
50698 } else {
50699 return transitionOpts;
50700 }
50701 }
50702
50703 function getFrameOpts(i) {
50704 if(Array.isArray(frameOpts)) {
50705 if(i >= frameOpts.length) {
50706 return frameOpts[0];
50707 } else {
50708 return frameOpts[i];
50709 }
50710 } else {
50711 return frameOpts;
50712 }
50713 }
50714
50715 // Execute a callback after the wrapper function has been called n times.
50716 // This is used to defer the resolution until a transition has resovled *and*
50717 // the frame has completed. If it's not done this way, then we get a race
50718 // condition in which the animation might resolve before a transition is complete
50719 // or vice versa.
50720 function callbackOnNthTime(cb, n) {
50721 var cnt = 0;
50722 return function() {
50723 if(cb && ++cnt === n) {
50724 return cb();
50725 }
50726 };
50727 }
50728
50729 return new Promise(function(resolve, reject) {
50730 function discardExistingFrames() {
50731 if(trans._frameQueue.length === 0) {
50732 return;
50733 }
50734
50735 while(trans._frameQueue.length) {
50736 var next = trans._frameQueue.pop();
50737 if(next.onInterrupt) {
50738 next.onInterrupt();
50739 }
50740 }
50741
50742 gd.emit('plotly_animationinterrupted', []);
50743 }
50744
50745 function queueFrames(frameList) {
50746 if(frameList.length === 0) return;
50747
50748 for(var i = 0; i < frameList.length; i++) {
50749 var computedFrame;
50750
50751 if(frameList[i].type === 'byname') {
50752 // If it's a named frame, compute it:
50753 computedFrame = Plots.computeFrame(gd, frameList[i].name);
50754 } else {
50755 // Otherwise we must have been given a simple object, so treat
50756 // the input itself as the computed frame.
50757 computedFrame = frameList[i].data;
50758 }
50759
50760 var frameOpts = getFrameOpts(i);
50761 var transitionOpts = getTransitionOpts(i);
50762
50763 // It doesn't make much sense for the transition duration to be greater than
50764 // the frame duration, so limit it:
50765 transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration);
50766
50767 var nextFrame = {
50768 frame: computedFrame,
50769 name: frameList[i].name,
50770 frameOpts: frameOpts,
50771 transitionOpts: transitionOpts,
50772 };
50773 if(i === frameList.length - 1) {
50774 // The last frame in this .animate call stores the promise resolve
50775 // and reject callbacks. This is how we ensure that the animation
50776 // loop (which may exist as a result of a *different* .animate call)
50777 // still resolves or rejecdts this .animate call's promise. once it's
50778 // complete.
50779 nextFrame.onComplete = callbackOnNthTime(resolve, 2);
50780 nextFrame.onInterrupt = reject;
50781 }
50782
50783 trans._frameQueue.push(nextFrame);
50784 }
50785
50786 // Set it as never having transitioned to a frame. This will cause the animation
50787 // loop to immediately transition to the next frame (which, for immediate mode,
50788 // is the first frame in the list since all others would have been discarded
50789 // below)
50790 if(animationOpts.mode === 'immediate') {
50791 trans._lastFrameAt = -Infinity;
50792 }
50793
50794 // Only it's not already running, start a RAF loop. This could be avoided in the
50795 // case that there's only one frame, but it significantly complicated the logic
50796 // and only sped things up by about 5% or so for a lorenz attractor simulation.
50797 // It would be a fine thing to implement, but the benefit of that optimization
50798 // doesn't seem worth the extra complexity.
50799 if(!trans._animationRaf) {
50800 beginAnimationLoop();
50801 }
50802 }
50803
50804 function stopAnimationLoop() {
50805 gd.emit('plotly_animated');
50806
50807 // Be sure to unset also since it's how we know whether a loop is already running:
50808 window.cancelAnimationFrame(trans._animationRaf);
50809 trans._animationRaf = null;
50810 }
50811
50812 function nextFrame() {
50813 if(trans._currentFrame && trans._currentFrame.onComplete) {
50814 // Execute the callback and unset it to ensure it doesn't
50815 // accidentally get called twice
50816 trans._currentFrame.onComplete();
50817 }
50818
50819 var newFrame = trans._currentFrame = trans._frameQueue.shift();
50820
50821 if(newFrame) {
50822 // Since it's sometimes necessary to do deep digging into frame data,
50823 // we'll consider it not 100% impossible for nulls or numbers to sneak through,
50824 // so check when casting the name, just to be absolutely certain:
50825 var stringName = newFrame.name ? newFrame.name.toString() : null;
50826 gd._fullLayout._currentFrame = stringName;
50827
50828 trans._lastFrameAt = Date.now();
50829 trans._timeToNext = newFrame.frameOpts.duration;
50830
50831 // This is simply called and it's left to .transition to decide how to manage
50832 // interrupting current transitions. That means we don't need to worry about
50833 // how it resolves or what happens after this:
50834 Plots.transition(gd,
50835 newFrame.frame.data,
50836 newFrame.frame.layout,
50837 helpers.coerceTraceIndices(gd, newFrame.frame.traces),
50838 newFrame.frameOpts,
50839 newFrame.transitionOpts
50840 ).then(function() {
50841 if(newFrame.onComplete) {
50842 newFrame.onComplete();
50843 }
50844 });
50845
50846 gd.emit('plotly_animatingframe', {
50847 name: stringName,
50848 frame: newFrame.frame,
50849 animation: {
50850 frame: newFrame.frameOpts,
50851 transition: newFrame.transitionOpts,
50852 }
50853 });
50854 } else {
50855 // If there are no more frames, then stop the RAF loop:
50856 stopAnimationLoop();
50857 }
50858 }
50859
50860 function beginAnimationLoop() {
50861 gd.emit('plotly_animating');
50862
50863 // If no timer is running, then set last frame = long ago so that the next
50864 // frame is immediately transitioned:
50865 trans._lastFrameAt = -Infinity;
50866 trans._timeToNext = 0;
50867 trans._runningTransitions = 0;
50868 trans._currentFrame = null;
50869
50870 var doFrame = function() {
50871 // This *must* be requested before nextFrame since nextFrame may decide
50872 // to cancel it if there's nothing more to animated:
50873 trans._animationRaf = window.requestAnimationFrame(doFrame);
50874
50875 // Check if we're ready for a new frame:
50876 if(Date.now() - trans._lastFrameAt > trans._timeToNext) {
50877 nextFrame();
50878 }
50879 };
50880
50881 doFrame();
50882 }
50883
50884 // This is an animate-local counter that helps match up option input list
50885 // items with the particular frame.
50886 var configCounter = 0;
50887 function setTransitionConfig(frame) {
50888 if(Array.isArray(transitionOpts)) {
50889 if(configCounter >= transitionOpts.length) {
50890 frame.transitionOpts = transitionOpts[configCounter];
50891 } else {
50892 frame.transitionOpts = transitionOpts[0];
50893 }
50894 } else {
50895 frame.transitionOpts = transitionOpts;
50896 }
50897 configCounter++;
50898 return frame;
50899 }
50900
50901 // Disambiguate what's sort of frames have been received
50902 var i, frame;
50903 var frameList = [];
50904 var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null;
50905 var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList);
50906 var isSingleFrame = !allFrames && !isFrameArray && Lib.isPlainObject(frameOrGroupNameOrFrameList);
50907
50908 if(isSingleFrame) {
50909 // In this case, a simple object has been passed to animate.
50910 frameList.push({
50911 type: 'object',
50912 data: setTransitionConfig(Lib.extendFlat({}, frameOrGroupNameOrFrameList))
50913 });
50914 } else if(allFrames || ['string', 'number'].indexOf(typeof frameOrGroupNameOrFrameList) !== -1) {
50915 // In this case, null or undefined has been passed so that we want to
50916 // animate *all* currently defined frames
50917 for(i = 0; i < trans._frames.length; i++) {
50918 frame = trans._frames[i];
50919
50920 if(!frame) continue;
50921
50922 if(allFrames || String(frame.group) === String(frameOrGroupNameOrFrameList)) {
50923 frameList.push({
50924 type: 'byname',
50925 name: String(frame.name),
50926 data: setTransitionConfig({name: frame.name})
50927 });
50928 }
50929 }
50930 } else if(isFrameArray) {
50931 for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) {
50932 var frameOrName = frameOrGroupNameOrFrameList[i];
50933 if(['number', 'string'].indexOf(typeof frameOrName) !== -1) {
50934 frameOrName = String(frameOrName);
50935 // In this case, there's an array and this frame is a string name:
50936 frameList.push({
50937 type: 'byname',
50938 name: frameOrName,
50939 data: setTransitionConfig({name: frameOrName})
50940 });
50941 } else if(Lib.isPlainObject(frameOrName)) {
50942 frameList.push({
50943 type: 'object',
50944 data: setTransitionConfig(Lib.extendFlat({}, frameOrName))
50945 });
50946 }
50947 }
50948 }
50949
50950 // Verify that all of these frames actually exist; return and reject if not:
50951 for(i = 0; i < frameList.length; i++) {
50952 frame = frameList[i];
50953 if(frame.type === 'byname' && !trans._frameHash[frame.data.name]) {
50954 Lib.warn('animate failure: frame not found: "' + frame.data.name + '"');
50955 reject();
50956 return;
50957 }
50958 }
50959
50960 // If the mode is either next or immediate, then all currently queued frames must
50961 // be dumped and the corresponding .animate promises rejected.
50962 if(['next', 'immediate'].indexOf(animationOpts.mode) !== -1) {
50963 discardExistingFrames();
50964 }
50965
50966 if(animationOpts.direction === 'reverse') {
50967 frameList.reverse();
50968 }
50969
50970 var currentFrame = gd._fullLayout._currentFrame;
50971 if(currentFrame && animationOpts.fromcurrent) {
50972 var idx = -1;
50973 for(i = 0; i < frameList.length; i++) {
50974 frame = frameList[i];
50975 if(frame.type === 'byname' && frame.name === currentFrame) {
50976 idx = i;
50977 break;
50978 }
50979 }
50980
50981 if(idx > 0 && idx < frameList.length - 1) {
50982 var filteredFrameList = [];
50983 for(i = 0; i < frameList.length; i++) {
50984 frame = frameList[i];
50985 if(frameList[i].type !== 'byname' || i > idx) {
50986 filteredFrameList.push(frame);
50987 }
50988 }
50989 frameList = filteredFrameList;
50990 }
50991 }
50992
50993 if(frameList.length > 0) {
50994 queueFrames(frameList);
50995 } else {
50996 // This is the case where there were simply no frames. It's a little strange
50997 // since there's not much to do:
50998 gd.emit('plotly_animated');
50999 resolve();
51000 }
51001 });
51002}
51003
51004/**
51005 * Register new frames
51006 *
51007 * @param {string id or DOM element} gd
51008 * the id or DOM element of the graph container div
51009 *
51010 * @param {array of objects} frameList
51011 * list of frame definitions, in which each object includes any of:
51012 * - name: {string} name of frame to add
51013 * - data: {array of objects} trace data
51014 * - layout {object} layout definition
51015 * - traces {array} trace indices
51016 * - baseframe {string} name of frame from which this frame gets defaults
51017 *
51018 * @param {array of integers} indices
51019 * an array of integer indices matching the respective frames in `frameList`. If not
51020 * provided, an index will be provided in serial order. If already used, the frame
51021 * will be overwritten.
51022 */
51023function addFrames(gd, frameList, indices) {
51024 gd = Lib.getGraphDiv(gd);
51025
51026 if(frameList === null || frameList === undefined) {
51027 return Promise.resolve();
51028 }
51029
51030 if(!Lib.isPlotDiv(gd)) {
51031 throw new Error(
51032 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
51033 'to create a plot before adding frames. For more details, see ' +
51034 'https://plotly.com/javascript/animations/'
51035 );
51036 }
51037
51038 var i, frame, j, idx;
51039 var _frames = gd._transitionData._frames;
51040 var _frameHash = gd._transitionData._frameHash;
51041
51042
51043 if(!Array.isArray(frameList)) {
51044 throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList);
51045 }
51046
51047 // Create a sorted list of insertions since we run into lots of problems if these
51048 // aren't in ascending order of index:
51049 //
51050 // Strictly for sorting. Make sure this is guaranteed to never collide with any
51051 // already-exisisting indices:
51052 var bigIndex = _frames.length + frameList.length * 2;
51053
51054 var insertions = [];
51055 var _frameHashLocal = {};
51056 for(i = frameList.length - 1; i >= 0; i--) {
51057 if(!Lib.isPlainObject(frameList[i])) continue;
51058
51059 // The entire logic for checking for this type of name collision can be removed once we migrate to ES6 and
51060 // use a Map instead of an Object instance, as Map keys aren't converted to strings.
51061 var lookupName = frameList[i].name;
51062 var name = (_frameHash[lookupName] || _frameHashLocal[lookupName] || {}).name;
51063 var newName = frameList[i].name;
51064 var collisionPresent = _frameHash[name] || _frameHashLocal[name];
51065
51066 if(name && newName && typeof newName === 'number' && collisionPresent && numericNameWarningCount < numericNameWarningCountLimit) {
51067 numericNameWarningCount++;
51068
51069 Lib.warn('addFrames: overwriting frame "' + (_frameHash[name] || _frameHashLocal[name]).name +
51070 '" with a frame whose name of type "number" also equates to "' +
51071 name + '". This is valid but may potentially lead to unexpected ' +
51072 'behavior since all plotly.js frame names are stored internally ' +
51073 'as strings.');
51074
51075 if(numericNameWarningCount === numericNameWarningCountLimit) {
51076 Lib.warn('addFrames: This API call has yielded too many of these warnings. ' +
51077 'For the rest of this call, further warnings about numeric frame ' +
51078 'names will be suppressed.');
51079 }
51080 }
51081
51082 _frameHashLocal[lookupName] = {name: lookupName};
51083
51084 insertions.push({
51085 frame: Plots.supplyFrameDefaults(frameList[i]),
51086 index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
51087 });
51088 }
51089
51090 // Sort this, taking note that undefined insertions end up at the end:
51091 insertions.sort(function(a, b) {
51092 if(a.index > b.index) return -1;
51093 if(a.index < b.index) return 1;
51094 return 0;
51095 });
51096
51097 var ops = [];
51098 var revops = [];
51099 var frameCount = _frames.length;
51100
51101 for(i = insertions.length - 1; i >= 0; i--) {
51102 frame = insertions[i].frame;
51103
51104 if(typeof frame.name === 'number') {
51105 Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' +
51106 'implicitly cast to strings');
51107 }
51108
51109 if(!frame.name) {
51110 // Repeatedly assign a default name, incrementing the counter each time until
51111 // we get a name that's not in the hashed lookup table:
51112 while(_frameHash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
51113 }
51114
51115 if(_frameHash[frame.name]) {
51116 // If frame is present, overwrite its definition:
51117 for(j = 0; j < _frames.length; j++) {
51118 if((_frames[j] || {}).name === frame.name) break;
51119 }
51120 ops.push({type: 'replace', index: j, value: frame});
51121 revops.unshift({type: 'replace', index: j, value: _frames[j]});
51122 } else {
51123 // Otherwise insert it at the end of the list:
51124 idx = Math.max(0, Math.min(insertions[i].index, frameCount));
51125
51126 ops.push({type: 'insert', index: idx, value: frame});
51127 revops.unshift({type: 'delete', index: idx});
51128 frameCount++;
51129 }
51130 }
51131
51132 var undoFunc = Plots.modifyFrames;
51133 var redoFunc = Plots.modifyFrames;
51134 var undoArgs = [gd, revops];
51135 var redoArgs = [gd, ops];
51136
51137 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
51138
51139 return Plots.modifyFrames(gd, ops);
51140}
51141
51142/**
51143 * Delete frame
51144 *
51145 * @param {string id or DOM element} gd
51146 * the id or DOM element of the graph container div
51147 *
51148 * @param {array of integers} frameList
51149 * list of integer indices of frames to be deleted
51150 */
51151function deleteFrames(gd, frameList) {
51152 gd = Lib.getGraphDiv(gd);
51153
51154 if(!Lib.isPlotDiv(gd)) {
51155 throw new Error('This element is not a Plotly plot: ' + gd);
51156 }
51157
51158 var i, idx;
51159 var _frames = gd._transitionData._frames;
51160 var ops = [];
51161 var revops = [];
51162
51163 if(!frameList) {
51164 frameList = [];
51165 for(i = 0; i < _frames.length; i++) {
51166 frameList.push(i);
51167 }
51168 }
51169
51170 frameList = frameList.slice();
51171 frameList.sort();
51172
51173 for(i = frameList.length - 1; i >= 0; i--) {
51174 idx = frameList[i];
51175 ops.push({type: 'delete', index: idx});
51176 revops.unshift({type: 'insert', index: idx, value: _frames[idx]});
51177 }
51178
51179 var undoFunc = Plots.modifyFrames;
51180 var redoFunc = Plots.modifyFrames;
51181 var undoArgs = [gd, revops];
51182 var redoArgs = [gd, ops];
51183
51184 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
51185
51186 return Plots.modifyFrames(gd, ops);
51187}
51188
51189/**
51190 * Purge a graph container div back to its initial pre-Plotly.plot state
51191 *
51192 * @param {string id or DOM element} gd
51193 * the id or DOM element of the graph container div
51194 */
51195function purge(gd) {
51196 gd = Lib.getGraphDiv(gd);
51197
51198 var fullLayout = gd._fullLayout || {};
51199 var fullData = gd._fullData || [];
51200
51201 // remove gl contexts
51202 Plots.cleanPlot([], {}, fullData, fullLayout);
51203
51204 // purge properties
51205 Plots.purge(gd);
51206
51207 // purge event emitter methods
51208 Events.purge(gd);
51209
51210 // remove plot container
51211 if(fullLayout._container) fullLayout._container.remove();
51212
51213 // in contrast to Plotly.Plots.purge which does NOT clear _context!
51214 delete gd._context;
51215
51216 return gd;
51217}
51218
51219// -------------------------------------------------------
51220// makePlotFramework: Create the plot container and axes
51221// -------------------------------------------------------
51222function makePlotFramework(gd) {
51223 var gd3 = d3.select(gd);
51224 var fullLayout = gd._fullLayout;
51225
51226 // Plot container
51227 fullLayout._container = gd3.selectAll('.plot-container').data([0]);
51228 fullLayout._container.enter().insert('div', ':first-child')
51229 .classed('plot-container', true)
51230 .classed('plotly', true);
51231
51232 // Make the svg container
51233 fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
51234 fullLayout._paperdiv.enter().append('div')
51235 .classed('svg-container', true)
51236 .style('position', 'relative');
51237
51238 // Make the graph containers
51239 // start fresh each time we get here, so we know the order comes out
51240 // right, rather than enter/exit which can muck up the order
51241 // TODO: sort out all the ordering so we don't have to
51242 // explicitly delete anything
51243 // FIXME: parcoords reuses this object, not the best pattern
51244 fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
51245 .data([{}]);
51246
51247 fullLayout._glcontainer.enter().append('div')
51248 .classed('gl-container', true);
51249
51250 fullLayout._paperdiv.selectAll('.main-svg').remove();
51251 fullLayout._paperdiv.select('.modebar-container').remove();
51252
51253 fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')
51254 .classed('main-svg', true);
51255
51256 fullLayout._toppaper = fullLayout._paperdiv.append('svg')
51257 .classed('main-svg', true);
51258
51259 fullLayout._modebardiv = fullLayout._paperdiv.append('div');
51260
51261 fullLayout._hoverpaper = fullLayout._paperdiv.append('svg')
51262 .classed('main-svg', true);
51263
51264 if(!fullLayout._uid) {
51265 var otherUids = {};
51266 d3.selectAll('defs').each(function() {
51267 if(this.id) otherUids[this.id.split('-')[1]] = 1;
51268 });
51269 fullLayout._uid = Lib.randstr(otherUids);
51270 }
51271
51272 fullLayout._paperdiv.selectAll('.main-svg')
51273 .attr(xmlnsNamespaces.svgAttrs);
51274
51275 fullLayout._defs = fullLayout._paper.append('defs')
51276 .attr('id', 'defs-' + fullLayout._uid);
51277
51278 fullLayout._clips = fullLayout._defs.append('g')
51279 .classed('clips', true);
51280
51281 fullLayout._topdefs = fullLayout._toppaper.append('defs')
51282 .attr('id', 'topdefs-' + fullLayout._uid);
51283
51284 fullLayout._topclips = fullLayout._topdefs.append('g')
51285 .classed('clips', true);
51286
51287 fullLayout._bgLayer = fullLayout._paper.append('g')
51288 .classed('bglayer', true);
51289
51290 fullLayout._draggers = fullLayout._paper.append('g')
51291 .classed('draglayer', true);
51292
51293 // lower shape/image layer - note that this is behind
51294 // all subplots data/grids but above the backgrounds
51295 // except inset subplots, whose backgrounds are drawn
51296 // inside their own group so that they appear above
51297 // the data for the main subplot
51298 // lower shapes and images which are fully referenced to
51299 // a subplot still get drawn within the subplot's group
51300 // so they will work correctly on insets
51301 var layerBelow = fullLayout._paper.append('g')
51302 .classed('layer-below', true);
51303 fullLayout._imageLowerLayer = layerBelow.append('g')
51304 .classed('imagelayer', true);
51305 fullLayout._shapeLowerLayer = layerBelow.append('g')
51306 .classed('shapelayer', true);
51307
51308 // single cartesian layer for the whole plot
51309 fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true);
51310
51311 // single polar layer for the whole plot
51312 fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true);
51313
51314 // single ternary layer for the whole plot
51315 fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true);
51316
51317 // single geo layer for the whole plot
51318 fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
51319
51320 // single funnelarea layer for the whole plot
51321 fullLayout._funnelarealayer = fullLayout._paper.append('g').classed('funnelarealayer', true);
51322
51323 // single pie layer for the whole plot
51324 fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
51325
51326 // single treemap layer for the whole plot
51327 fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true);
51328
51329 // single sunburst layer for the whole plot
51330 fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true);
51331
51332 // single indicator layer for the whole plot
51333 fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true);
51334
51335 // fill in image server scrape-svg
51336 fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true);
51337
51338 // lastly upper shapes, info (legend, annotations) and hover layers go on top
51339 // these are in a different svg element normally, but get collapsed into a single
51340 // svg when exporting (after inserting 3D)
51341 // upper shapes/images are only those drawn above the whole plot, including subplots
51342 var layerAbove = fullLayout._toppaper.append('g')
51343 .classed('layer-above', true);
51344 fullLayout._imageUpperLayer = layerAbove.append('g')
51345 .classed('imagelayer', true);
51346 fullLayout._shapeUpperLayer = layerAbove.append('g')
51347 .classed('shapelayer', true);
51348
51349 fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
51350 fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true);
51351 fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
51352 fullLayout._hoverlayer = fullLayout._hoverpaper.append('g').classed('hoverlayer', true);
51353
51354 // Make the modebar container
51355 fullLayout._modebardiv
51356 .classed('modebar-container', true)
51357 .style('position', 'absolute')
51358 .style('top', '0px')
51359 .style('right', '0px');
51360
51361 gd.emit('plotly_framework');
51362}
51363
51364exports.animate = animate;
51365exports.addFrames = addFrames;
51366exports.deleteFrames = deleteFrames;
51367
51368exports.addTraces = addTraces;
51369exports.deleteTraces = deleteTraces;
51370exports.extendTraces = extendTraces;
51371exports.moveTraces = moveTraces;
51372exports.prependTraces = prependTraces;
51373
51374exports.newPlot = newPlot;
51375exports.plot = plot;
51376exports.purge = purge;
51377
51378exports.react = react;
51379exports.redraw = redraw;
51380exports.relayout = relayout;
51381exports.restyle = restyle;
51382
51383exports.setPlotConfig = setPlotConfig;
51384
51385exports.update = update;
51386
51387exports._guiRelayout = guiEdit(relayout);
51388exports._guiRestyle = guiEdit(restyle);
51389exports._guiUpdate = guiEdit(update);
51390
51391exports._storeDirectGUIEdit = _storeDirectGUIEdit;
51392
51393},{"../components/color":50,"../components/drawing":72,"../constants/xmlns_namespaces":156,"../lib":177,"../lib/events":169,"../lib/queue":191,"../lib/svg_text_utils":198,"../plots/cartesian/axes":222,"../plots/cartesian/constants":228,"../plots/cartesian/graph_interact":231,"../plots/cartesian/select":241,"../plots/plots":263,"../plots/polar/legacy":266,"../registry":272,"./edit_types":205,"./helpers":206,"./manage_arrays":208,"./plot_config":210,"./plot_schema":211,"./subroutines":213,"d3":13,"fast-isnumeric":15,"has-hover":17}],210:[function(_dereq_,module,exports){
51394/**
51395* Copyright 2012-2020, Plotly, Inc.
51396* All rights reserved.
51397*
51398* This source code is licensed under the MIT license found in the
51399* LICENSE file in the root directory of this source tree.
51400*/
51401
51402'use strict';
51403
51404/**
51405 * This will be transferred over to gd and overridden by
51406 * config args to Plotly.plot.
51407 *
51408 * The defaults are the appropriate settings for plotly.js,
51409 * so we get the right experience without any config argument.
51410 *
51411 * N.B. the config options are not coerced using Lib.coerce so keys
51412 * like `valType` and `values` are only set for documentation purposes
51413 * at the moment.
51414 */
51415
51416var configAttributes = {
51417 staticPlot: {
51418 valType: 'boolean',
51419 dflt: false,
51420
51421 },
51422
51423 plotlyServerURL: {
51424 valType: 'string',
51425 dflt: '',
51426
51427 },
51428
51429 editable: {
51430 valType: 'boolean',
51431 dflt: false,
51432
51433 },
51434 edits: {
51435 annotationPosition: {
51436 valType: 'boolean',
51437 dflt: false,
51438
51439 },
51440 annotationTail: {
51441 valType: 'boolean',
51442 dflt: false,
51443
51444 },
51445 annotationText: {
51446 valType: 'boolean',
51447 dflt: false,
51448
51449 },
51450 axisTitleText: {
51451 valType: 'boolean',
51452 dflt: false,
51453
51454 },
51455 colorbarPosition: {
51456 valType: 'boolean',
51457 dflt: false,
51458
51459 },
51460 colorbarTitleText: {
51461 valType: 'boolean',
51462 dflt: false,
51463
51464 },
51465 legendPosition: {
51466 valType: 'boolean',
51467 dflt: false,
51468
51469 },
51470 legendText: {
51471 valType: 'boolean',
51472 dflt: false,
51473
51474 },
51475 shapePosition: {
51476 valType: 'boolean',
51477 dflt: false,
51478
51479 },
51480 titleText: {
51481 valType: 'boolean',
51482 dflt: false,
51483
51484 }
51485 },
51486
51487 autosizable: {
51488 valType: 'boolean',
51489 dflt: false,
51490
51491 },
51492 responsive: {
51493 valType: 'boolean',
51494 dflt: false,
51495
51496 },
51497 fillFrame: {
51498 valType: 'boolean',
51499 dflt: false,
51500
51501 },
51502 frameMargins: {
51503 valType: 'number',
51504 dflt: 0,
51505 min: 0,
51506 max: 0.5,
51507
51508 },
51509
51510 scrollZoom: {
51511 valType: 'flaglist',
51512 flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
51513 extras: [true, false],
51514 dflt: 'gl3d+geo+mapbox',
51515
51516 },
51517 doubleClick: {
51518 valType: 'enumerated',
51519 values: [false, 'reset', 'autosize', 'reset+autosize'],
51520 dflt: 'reset+autosize',
51521
51522 },
51523 doubleClickDelay: {
51524 valType: 'number',
51525 dflt: 300,
51526 min: 0,
51527
51528 },
51529
51530 showAxisDragHandles: {
51531 valType: 'boolean',
51532 dflt: true,
51533
51534 },
51535 showAxisRangeEntryBoxes: {
51536 valType: 'boolean',
51537 dflt: true,
51538
51539 },
51540
51541 showTips: {
51542 valType: 'boolean',
51543 dflt: true,
51544
51545 },
51546
51547 showLink: {
51548 valType: 'boolean',
51549 dflt: false,
51550
51551 },
51552 linkText: {
51553 valType: 'string',
51554 dflt: 'Edit chart',
51555 noBlank: true,
51556
51557 },
51558 sendData: {
51559 valType: 'boolean',
51560 dflt: true,
51561
51562 },
51563 showSources: {
51564 valType: 'any',
51565 dflt: false,
51566
51567 },
51568
51569 displayModeBar: {
51570 valType: 'enumerated',
51571 values: ['hover', true, false],
51572 dflt: 'hover',
51573
51574 },
51575 showSendToCloud: {
51576 valType: 'boolean',
51577 dflt: false,
51578
51579 },
51580 showEditInChartStudio: {
51581 valType: 'boolean',
51582 dflt: false,
51583
51584 },
51585 modeBarButtonsToRemove: {
51586 valType: 'any',
51587 dflt: [],
51588
51589 },
51590 modeBarButtonsToAdd: {
51591 valType: 'any',
51592 dflt: [],
51593
51594 },
51595 modeBarButtons: {
51596 valType: 'any',
51597 dflt: false,
51598
51599 },
51600 toImageButtonOptions: {
51601 valType: 'any',
51602 dflt: {},
51603
51604 },
51605 displaylogo: {
51606 valType: 'boolean',
51607 dflt: true,
51608
51609 },
51610 watermark: {
51611 valType: 'boolean',
51612 dflt: false,
51613
51614 },
51615
51616 plotGlPixelRatio: {
51617 valType: 'number',
51618 dflt: 2,
51619 min: 1,
51620 max: 4,
51621
51622 },
51623
51624 setBackground: {
51625 valType: 'any',
51626 dflt: 'transparent',
51627
51628 },
51629
51630 topojsonURL: {
51631 valType: 'string',
51632 noBlank: true,
51633 dflt: 'https://cdn.plot.ly/',
51634
51635 },
51636
51637 mapboxAccessToken: {
51638 valType: 'string',
51639 dflt: null,
51640
51641 },
51642
51643 logging: {
51644 valType: 'integer',
51645 min: 0,
51646 max: 2,
51647 dflt: 1,
51648
51649 },
51650
51651 notifyOnLogging: {
51652 valType: 'integer',
51653 min: 0,
51654 max: 2,
51655 dflt: 0,
51656
51657 },
51658
51659 queueLength: {
51660 valType: 'integer',
51661 min: 0,
51662 dflt: 0,
51663
51664 },
51665
51666 globalTransforms: {
51667 valType: 'any',
51668 dflt: [],
51669
51670 },
51671
51672 locale: {
51673 valType: 'string',
51674 dflt: 'en-US',
51675
51676 },
51677
51678 locales: {
51679 valType: 'any',
51680 dflt: {},
51681
51682 }
51683};
51684
51685var dfltConfig = {};
51686
51687function crawl(src, target) {
51688 for(var k in src) {
51689 var obj = src[k];
51690 if(obj.valType) {
51691 target[k] = obj.dflt;
51692 } else {
51693 if(!target[k]) {
51694 target[k] = {};
51695 }
51696 crawl(obj, target[k]);
51697 }
51698 }
51699}
51700
51701crawl(configAttributes, dfltConfig);
51702
51703module.exports = {
51704 configAttributes: configAttributes,
51705 dfltConfig: dfltConfig
51706};
51707
51708},{}],211:[function(_dereq_,module,exports){
51709/**
51710* Copyright 2012-2020, Plotly, Inc.
51711* All rights reserved.
51712*
51713* This source code is licensed under the MIT license found in the
51714* LICENSE file in the root directory of this source tree.
51715*/
51716
51717'use strict';
51718
51719var Registry = _dereq_('../registry');
51720var Lib = _dereq_('../lib');
51721
51722var baseAttributes = _dereq_('../plots/attributes');
51723var baseLayoutAttributes = _dereq_('../plots/layout_attributes');
51724var frameAttributes = _dereq_('../plots/frame_attributes');
51725var animationAttributes = _dereq_('../plots/animation_attributes');
51726var configAttributes = _dereq_('./plot_config').configAttributes;
51727
51728// polar attributes are not part of the Registry yet
51729var polarAreaAttrs = _dereq_('../plots/polar/legacy/area_attributes');
51730var polarAxisAttrs = _dereq_('../plots/polar/legacy/axis_attributes');
51731
51732var editTypes = _dereq_('./edit_types');
51733
51734var extendFlat = Lib.extendFlat;
51735var extendDeepAll = Lib.extendDeepAll;
51736var isPlainObject = Lib.isPlainObject;
51737var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
51738var nestedProperty = Lib.nestedProperty;
51739var valObjectMeta = Lib.valObjectMeta;
51740
51741var IS_SUBPLOT_OBJ = '_isSubplotObj';
51742var IS_LINKED_TO_ARRAY = '_isLinkedToArray';
51743var ARRAY_ATTR_REGEXPS = '_arrayAttrRegexps';
51744var DEPRECATED = '_deprecated';
51745var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, ARRAY_ATTR_REGEXPS, DEPRECATED];
51746
51747exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ;
51748exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY;
51749exports.DEPRECATED = DEPRECATED;
51750exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS;
51751
51752/** Outputs the full plotly.js plot schema
51753 *
51754 * @return {object}
51755 * - defs
51756 * - traces
51757 * - layout
51758 * - transforms
51759 * - frames
51760 * - animations
51761 * - config
51762 */
51763exports.get = function() {
51764 var traces = {};
51765
51766 Registry.allTypes.concat('area').forEach(function(type) {
51767 traces[type] = getTraceAttributes(type);
51768 });
51769
51770 var transforms = {};
51771
51772 Object.keys(Registry.transformsRegistry).forEach(function(type) {
51773 transforms[type] = getTransformAttributes(type);
51774 });
51775
51776 return {
51777 defs: {
51778 valObjects: valObjectMeta,
51779 metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']),
51780 editType: {
51781 traces: editTypes.traces,
51782 layout: editTypes.layout
51783 },
51784 impliedEdits: {
51785
51786 }
51787 },
51788
51789 traces: traces,
51790 layout: getLayoutAttributes(),
51791
51792 transforms: transforms,
51793
51794 frames: getFramesAttributes(),
51795 animation: formatAttributes(animationAttributes),
51796
51797 config: formatAttributes(configAttributes)
51798 };
51799};
51800
51801/**
51802 * Crawl the attribute tree, recursively calling a callback function
51803 *
51804 * @param {object} attrs
51805 * The node of the attribute tree (e.g. the root) from which recursion originates
51806 * @param {Function} callback
51807 * A callback function with the signature:
51808 * @callback callback
51809 * @param {object} attr an attribute
51810 * @param {String} attrName name string
51811 * @param {object[]} attrs all the attributes
51812 * @param {Number} level the recursion level, 0 at the root
51813 * @param {String} fullAttrString full attribute name (ie 'marker.line')
51814 * @param {Number} [specifiedLevel]
51815 * The level in the tree, in order to let the callback function detect descend or backtrack,
51816 * typically unsupplied (implied 0), just used by the self-recursive call.
51817 * The necessity arises because the tree traversal is not controlled by callback return values.
51818 * The decision to not use callback return values for controlling tree pruning arose from
51819 * the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions
51820 * precedes the callback call.
51821 * @param {string} [attrString]
51822 * the path to the current attribute, as an attribute string (ie 'marker.line')
51823 * typically unsupplied, but you may supply it if you want to disambiguate which attrs tree you
51824 * are starting from
51825 *
51826 * @return {object} transformOut
51827 * copy of transformIn that contains attribute defaults
51828 */
51829exports.crawl = function(attrs, callback, specifiedLevel, attrString) {
51830 var level = specifiedLevel || 0;
51831 attrString = attrString || '';
51832
51833 Object.keys(attrs).forEach(function(attrName) {
51834 var attr = attrs[attrName];
51835
51836 if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return;
51837
51838 var fullAttrString = (attrString ? attrString + '.' : '') + attrName;
51839 callback(attr, attrName, attrs, level, fullAttrString);
51840
51841 if(exports.isValObject(attr)) return;
51842
51843 if(isPlainObject(attr) && attrName !== 'impliedEdits') {
51844 exports.crawl(attr, callback, level + 1, fullAttrString);
51845 }
51846 });
51847};
51848
51849/** Is object a value object (or a container object)?
51850 *
51851 * @param {object} obj
51852 * @return {boolean}
51853 * returns true for a valid value object and
51854 * false for tree nodes in the attribute hierarchy
51855 */
51856exports.isValObject = function(obj) {
51857 return obj && obj.valType !== undefined;
51858};
51859
51860/**
51861 * Find all data array attributes in a given trace object - including
51862 * `arrayOk` attributes.
51863 *
51864 * @param {object} trace
51865 * full trace object that contains a reference to `_module.attributes`
51866 *
51867 * @return {array} arrayAttributes
51868 * list of array attributes for the given trace
51869 */
51870exports.findArrayAttributes = function(trace) {
51871 var arrayAttributes = [];
51872 var stack = [];
51873 var isArrayStack = [];
51874 var baseContainer, baseAttrName;
51875
51876 function callback(attr, attrName, attrs, level) {
51877 stack = stack.slice(0, level).concat([attrName]);
51878 isArrayStack = isArrayStack.slice(0, level).concat([attr && attr._isLinkedToArray]);
51879
51880 var splittableAttr = (
51881 attr &&
51882 (attr.valType === 'data_array' || attr.arrayOk === true) &&
51883 !(stack[level - 1] === 'colorbar' && (attrName === 'ticktext' || attrName === 'tickvals'))
51884 );
51885
51886 // Manually exclude 'colorbar.tickvals' and 'colorbar.ticktext' for now
51887 // which are declared as `valType: 'data_array'` but scale independently of
51888 // the coordinate arrays.
51889 //
51890 // Down the road, we might want to add a schema field (e.g `uncorrelatedArray: true`)
51891 // to distinguish attributes of the likes.
51892
51893 if(!splittableAttr) return;
51894
51895 crawlIntoTrace(baseContainer, 0, '');
51896 }
51897
51898 function crawlIntoTrace(container, i, astrPartial) {
51899 var item = container[stack[i]];
51900 var newAstrPartial = astrPartial + stack[i];
51901 if(i === stack.length - 1) {
51902 if(isArrayOrTypedArray(item)) {
51903 arrayAttributes.push(baseAttrName + newAstrPartial);
51904 }
51905 } else {
51906 if(isArrayStack[i]) {
51907 if(Array.isArray(item)) {
51908 for(var j = 0; j < item.length; j++) {
51909 if(isPlainObject(item[j])) {
51910 crawlIntoTrace(item[j], i + 1, newAstrPartial + '[' + j + '].');
51911 }
51912 }
51913 }
51914 } else if(isPlainObject(item)) {
51915 crawlIntoTrace(item, i + 1, newAstrPartial + '.');
51916 }
51917 }
51918 }
51919
51920 baseContainer = trace;
51921 baseAttrName = '';
51922 exports.crawl(baseAttributes, callback);
51923 if(trace._module && trace._module.attributes) {
51924 exports.crawl(trace._module.attributes, callback);
51925 }
51926
51927 var transforms = trace.transforms;
51928 if(transforms) {
51929 for(var i = 0; i < transforms.length; i++) {
51930 var transform = transforms[i];
51931 var module = transform._module;
51932
51933 if(module) {
51934 baseAttrName = 'transforms[' + i + '].';
51935 baseContainer = transform;
51936
51937 exports.crawl(module.attributes, callback);
51938 }
51939 }
51940 }
51941
51942 return arrayAttributes;
51943};
51944
51945/*
51946 * Find the valObject for one attribute in an existing trace
51947 *
51948 * @param {object} trace
51949 * full trace object that contains a reference to `_module.attributes`
51950 * @param {object} parts
51951 * an array of parts, like ['transforms', 1, 'value']
51952 * typically from nestedProperty(...).parts
51953 *
51954 * @return {object|false}
51955 * the valObject for this attribute, or the last found parent
51956 * in some cases the innermost valObject will not exist, for example
51957 * `valType: 'any'` attributes where we might set a part of the attribute.
51958 * In that case, stop at the deepest valObject we *do* find.
51959 */
51960exports.getTraceValObject = function(trace, parts) {
51961 var head = parts[0];
51962 var i = 1; // index to start recursing from
51963 var moduleAttrs, valObject;
51964
51965 if(head === 'transforms') {
51966 if(parts.length === 1) {
51967 return baseAttributes.transforms;
51968 }
51969 var transforms = trace.transforms;
51970 if(!Array.isArray(transforms) || !transforms.length) return false;
51971 var tNum = parts[1];
51972 if(!isIndex(tNum) || tNum >= transforms.length) {
51973 return false;
51974 }
51975 moduleAttrs = (Registry.transformsRegistry[transforms[tNum].type] || {}).attributes;
51976 valObject = moduleAttrs && moduleAttrs[parts[2]];
51977 i = 3; // start recursing only inside the transform
51978 } else if(trace.type === 'area') {
51979 valObject = polarAreaAttrs[head];
51980 } else {
51981 // first look in the module for this trace
51982 // components have already merged their trace attributes in here
51983 var _module = trace._module;
51984 if(!_module) _module = (Registry.modules[trace.type || baseAttributes.type.dflt] || {})._module;
51985 if(!_module) return false;
51986
51987 moduleAttrs = _module.attributes;
51988 valObject = moduleAttrs && moduleAttrs[head];
51989
51990 // then look in the subplot attributes
51991 if(!valObject) {
51992 var subplotModule = _module.basePlotModule;
51993 if(subplotModule && subplotModule.attributes) {
51994 valObject = subplotModule.attributes[head];
51995 }
51996 }
51997
51998 // finally look in the global attributes
51999 if(!valObject) valObject = baseAttributes[head];
52000 }
52001
52002 return recurseIntoValObject(valObject, parts, i);
52003};
52004
52005/*
52006 * Find the valObject for one layout attribute
52007 *
52008 * @param {array} parts
52009 * an array of parts, like ['annotations', 1, 'x']
52010 * typically from nestedProperty(...).parts
52011 *
52012 * @return {object|false}
52013 * the valObject for this attribute, or the last found parent
52014 * in some cases the innermost valObject will not exist, for example
52015 * `valType: 'any'` attributes where we might set a part of the attribute.
52016 * In that case, stop at the deepest valObject we *do* find.
52017 */
52018exports.getLayoutValObject = function(fullLayout, parts) {
52019 var valObject = layoutHeadAttr(fullLayout, parts[0]);
52020
52021 return recurseIntoValObject(valObject, parts, 1);
52022};
52023
52024function layoutHeadAttr(fullLayout, head) {
52025 var i, key, _module, attributes;
52026
52027 // look for attributes of the subplot types used on the plot
52028 var basePlotModules = fullLayout._basePlotModules;
52029 if(basePlotModules) {
52030 var out;
52031 for(i = 0; i < basePlotModules.length; i++) {
52032 _module = basePlotModules[i];
52033 if(_module.attrRegex && _module.attrRegex.test(head)) {
52034 // if a module defines overrides, these take precedence
52035 // initially this is to allow gl2d different editTypes from svg cartesian
52036 if(_module.layoutAttrOverrides) return _module.layoutAttrOverrides;
52037
52038 // otherwise take the first attributes we find
52039 if(!out && _module.layoutAttributes) out = _module.layoutAttributes;
52040 }
52041
52042 // a module can also override the behavior of base (and component) module layout attrs
52043 // again see gl2d for initial use case
52044 var baseOverrides = _module.baseLayoutAttrOverrides;
52045 if(baseOverrides && head in baseOverrides) return baseOverrides[head];
52046 }
52047 if(out) return out;
52048 }
52049
52050 // look for layout attributes contributed by traces on the plot
52051 var modules = fullLayout._modules;
52052 if(modules) {
52053 for(i = 0; i < modules.length; i++) {
52054 attributes = modules[i].layoutAttributes;
52055 if(attributes && head in attributes) {
52056 return attributes[head];
52057 }
52058 }
52059 }
52060
52061 /*
52062 * Next look in components.
52063 * Components that define a schema have already merged this into
52064 * base and subplot attribute defs, so ignore these.
52065 * Others (older style) all put all their attributes
52066 * inside a container matching the module `name`
52067 * eg `attributes` (array) or `legend` (object)
52068 */
52069 for(key in Registry.componentsRegistry) {
52070 _module = Registry.componentsRegistry[key];
52071 if(_module.name === 'colorscale' && head.indexOf('coloraxis') === 0) {
52072 return _module.layoutAttributes[head];
52073 } else if(!_module.schema && (head === _module.name)) {
52074 return _module.layoutAttributes;
52075 }
52076 }
52077
52078 if(head in baseLayoutAttributes) return baseLayoutAttributes[head];
52079
52080 // Polar doesn't populate _modules or _basePlotModules
52081 // just fall back on these when the others fail
52082 if(head === 'radialaxis' || head === 'angularaxis') {
52083 return polarAxisAttrs[head];
52084 }
52085 return polarAxisAttrs.layout[head] || false;
52086}
52087
52088function recurseIntoValObject(valObject, parts, i) {
52089 if(!valObject) return false;
52090
52091 if(valObject._isLinkedToArray) {
52092 // skip array index, abort if we try to dive into an array without an index
52093 if(isIndex(parts[i])) i++;
52094 else if(i < parts.length) return false;
52095 }
52096
52097 // now recurse as far as we can. Occasionally we have an attribute
52098 // setting an internal part below what's in the schema; just return
52099 // the innermost schema item we find.
52100 for(; i < parts.length; i++) {
52101 var newValObject = valObject[parts[i]];
52102 if(isPlainObject(newValObject)) valObject = newValObject;
52103 else break;
52104
52105 if(i === parts.length - 1) break;
52106
52107 if(valObject._isLinkedToArray) {
52108 i++;
52109 if(!isIndex(parts[i])) return false;
52110 } else if(valObject.valType === 'info_array') {
52111 i++;
52112 var index = parts[i];
52113 if(!isIndex(index)) return false;
52114
52115 var items = valObject.items;
52116 if(Array.isArray(items)) {
52117 if(index >= items.length) return false;
52118 if(valObject.dimensions === 2) {
52119 i++;
52120 if(parts.length === i) return valObject;
52121 var index2 = parts[i];
52122 if(!isIndex(index2)) return false;
52123 valObject = items[index][index2];
52124 } else valObject = items[index];
52125 } else {
52126 valObject = items;
52127 }
52128 }
52129 }
52130
52131 return valObject;
52132}
52133
52134// note: this is different from Lib.isIndex, this one doesn't accept numeric
52135// strings, only actual numbers.
52136function isIndex(val) {
52137 return val === Math.round(val) && val >= 0;
52138}
52139
52140function getTraceAttributes(type) {
52141 var _module, basePlotModule;
52142
52143 if(type === 'area') {
52144 _module = { attributes: polarAreaAttrs };
52145 basePlotModule = {};
52146 } else {
52147 _module = Registry.modules[type]._module,
52148 basePlotModule = _module.basePlotModule;
52149 }
52150
52151 var attributes = {};
52152
52153 // make 'type' the first attribute in the object
52154 attributes.type = null;
52155
52156 var copyBaseAttributes = extendDeepAll({}, baseAttributes);
52157 var copyModuleAttributes = extendDeepAll({}, _module.attributes);
52158
52159 // prune global-level trace attributes that are already defined in a trace
52160 exports.crawl(copyModuleAttributes, function(attr, attrName, attrs, level, fullAttrString) {
52161 nestedProperty(copyBaseAttributes, fullAttrString).set(undefined);
52162 // Prune undefined attributes
52163 if(attr === undefined) nestedProperty(copyModuleAttributes, fullAttrString).set(undefined);
52164 });
52165
52166 // base attributes (same for all trace types)
52167 extendDeepAll(attributes, copyBaseAttributes);
52168
52169 // prune-out base attributes based on trace module categories
52170 if(Registry.traceIs(type, 'noOpacity')) {
52171 delete attributes.opacity;
52172 }
52173 if(!Registry.traceIs(type, 'showLegend')) {
52174 delete attributes.showlegend;
52175 delete attributes.legendgroup;
52176 }
52177 if(Registry.traceIs(type, 'noHover')) {
52178 delete attributes.hoverinfo;
52179 delete attributes.hoverlabel;
52180 }
52181 if(!_module.selectPoints) {
52182 delete attributes.selectedpoints;
52183 }
52184
52185 // module attributes
52186 extendDeepAll(attributes, copyModuleAttributes);
52187
52188 // subplot attributes
52189 if(basePlotModule.attributes) {
52190 extendDeepAll(attributes, basePlotModule.attributes);
52191 }
52192
52193 // 'type' gets overwritten by baseAttributes; reset it here
52194 attributes.type = type;
52195
52196 var out = {
52197 meta: _module.meta || {},
52198 categories: _module.categories || {},
52199 animatable: Boolean(_module.animatable),
52200 type: type,
52201 attributes: formatAttributes(attributes),
52202 };
52203
52204 // trace-specific layout attributes
52205 if(_module.layoutAttributes) {
52206 var layoutAttributes = {};
52207
52208 extendDeepAll(layoutAttributes, _module.layoutAttributes);
52209 out.layoutAttributes = formatAttributes(layoutAttributes);
52210 }
52211
52212 // drop anim:true in non-animatable modules
52213 if(!_module.animatable) {
52214 exports.crawl(out, function(attr) {
52215 if(exports.isValObject(attr) && 'anim' in attr) {
52216 delete attr.anim;
52217 }
52218 });
52219 }
52220
52221 return out;
52222}
52223
52224function getLayoutAttributes() {
52225 var layoutAttributes = {};
52226 var key, _module;
52227
52228 // global layout attributes
52229 extendDeepAll(layoutAttributes, baseLayoutAttributes);
52230
52231 // add base plot module layout attributes
52232 for(key in Registry.subplotsRegistry) {
52233 _module = Registry.subplotsRegistry[key];
52234
52235 if(!_module.layoutAttributes) continue;
52236
52237 if(Array.isArray(_module.attr)) {
52238 for(var i = 0; i < _module.attr.length; i++) {
52239 handleBasePlotModule(layoutAttributes, _module, _module.attr[i]);
52240 }
52241 } else {
52242 var astr = _module.attr === 'subplot' ? _module.name : _module.attr;
52243 handleBasePlotModule(layoutAttributes, _module, astr);
52244 }
52245 }
52246
52247 // polar layout attributes
52248 layoutAttributes = assignPolarLayoutAttrs(layoutAttributes);
52249
52250 // add registered components layout attributes
52251 for(key in Registry.componentsRegistry) {
52252 _module = Registry.componentsRegistry[key];
52253 var schema = _module.schema;
52254
52255 if(schema && (schema.subplots || schema.layout)) {
52256 /*
52257 * Components with defined schema have already been merged in at register time
52258 * but a few components define attributes that apply only to xaxis
52259 * not yaxis (rangeselector, rangeslider) - delete from y schema.
52260 * Note that the input attributes for xaxis/yaxis are the same object
52261 * so it's not possible to only add them to xaxis from the start.
52262 * If we ever have such asymmetry the other way, or anywhere else,
52263 * we will need to extend both this code and mergeComponentAttrsToSubplot
52264 * (which will not find yaxis only for example)
52265 */
52266 var subplots = schema.subplots;
52267 if(subplots && subplots.xaxis && !subplots.yaxis) {
52268 for(var xkey in subplots.xaxis) {
52269 delete layoutAttributes.yaxis[xkey];
52270 }
52271 }
52272 } else if(_module.name === 'colorscale') {
52273 extendDeepAll(layoutAttributes, _module.layoutAttributes);
52274 } else if(_module.layoutAttributes) {
52275 // older style without schema need to be explicitly merged in now
52276 insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
52277 }
52278 }
52279
52280 return {
52281 layoutAttributes: formatAttributes(layoutAttributes)
52282 };
52283}
52284
52285function getTransformAttributes(type) {
52286 var _module = Registry.transformsRegistry[type];
52287 var attributes = extendDeepAll({}, _module.attributes);
52288
52289 // add registered components transform attributes
52290 Object.keys(Registry.componentsRegistry).forEach(function(k) {
52291 var _module = Registry.componentsRegistry[k];
52292
52293 if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) {
52294 Object.keys(_module.schema.transforms[type]).forEach(function(v) {
52295 insertAttrs(attributes, _module.schema.transforms[type][v], v);
52296 });
52297 }
52298 });
52299
52300 return {
52301 attributes: formatAttributes(attributes)
52302 };
52303}
52304
52305function getFramesAttributes() {
52306 var attrs = {
52307 frames: extendDeepAll({}, frameAttributes)
52308 };
52309
52310 formatAttributes(attrs);
52311
52312 return attrs.frames;
52313}
52314
52315function formatAttributes(attrs) {
52316 mergeValTypeAndRole(attrs);
52317 formatArrayContainers(attrs);
52318 stringify(attrs);
52319
52320 return attrs;
52321}
52322
52323function mergeValTypeAndRole(attrs) {
52324 function makeSrcAttr(attrName) {
52325 return {
52326 valType: 'string',
52327
52328
52329 editType: 'none'
52330 };
52331 }
52332
52333 function callback(attr, attrName, attrs) {
52334 if(exports.isValObject(attr)) {
52335 if(attr.valType === 'data_array') {
52336 // all 'data_array' attrs have role 'data'
52337 attr.role = 'data';
52338 // all 'data_array' attrs have a corresponding 'src' attr
52339 attrs[attrName + 'src'] = makeSrcAttr(attrName);
52340 } else if(attr.arrayOk === true) {
52341 // all 'arrayOk' attrs have a corresponding 'src' attr
52342 attrs[attrName + 'src'] = makeSrcAttr(attrName);
52343 }
52344 } else if(isPlainObject(attr)) {
52345 // all attrs container objects get role 'object'
52346 attr.role = 'object';
52347 }
52348 }
52349
52350 exports.crawl(attrs, callback);
52351}
52352
52353function formatArrayContainers(attrs) {
52354 function callback(attr, attrName, attrs) {
52355 if(!attr) return;
52356
52357 var itemName = attr[IS_LINKED_TO_ARRAY];
52358
52359 if(!itemName) return;
52360
52361 delete attr[IS_LINKED_TO_ARRAY];
52362
52363 attrs[attrName] = { items: {} };
52364 attrs[attrName].items[itemName] = attr;
52365 attrs[attrName].role = 'object';
52366 }
52367
52368 exports.crawl(attrs, callback);
52369}
52370
52371// this can take around 10ms and should only be run from PlotSchema.get(),
52372// to ensure JSON.stringify(PlotSchema.get()) gives the intended result.
52373function stringify(attrs) {
52374 function walk(attr) {
52375 for(var k in attr) {
52376 if(isPlainObject(attr[k])) {
52377 walk(attr[k]);
52378 } else if(Array.isArray(attr[k])) {
52379 for(var i = 0; i < attr[k].length; i++) {
52380 walk(attr[k][i]);
52381 }
52382 } else {
52383 // as JSON.stringify(/test/) // => {}
52384 if(attr[k] instanceof RegExp) {
52385 attr[k] = attr[k].toString();
52386 }
52387 }
52388 }
52389 }
52390
52391 walk(attrs);
52392}
52393
52394function assignPolarLayoutAttrs(layoutAttributes) {
52395 extendFlat(layoutAttributes, {
52396 radialaxis: polarAxisAttrs.radialaxis,
52397 angularaxis: polarAxisAttrs.angularaxis
52398 });
52399
52400 extendFlat(layoutAttributes, polarAxisAttrs.layout);
52401
52402 return layoutAttributes;
52403}
52404
52405function handleBasePlotModule(layoutAttributes, _module, astr) {
52406 var np = nestedProperty(layoutAttributes, astr);
52407 var attrs = extendDeepAll({}, _module.layoutAttributes);
52408
52409 attrs[IS_SUBPLOT_OBJ] = true;
52410 np.set(attrs);
52411}
52412
52413function insertAttrs(baseAttrs, newAttrs, astr) {
52414 var np = nestedProperty(baseAttrs, astr);
52415
52416 np.set(extendDeepAll(np.get() || {}, newAttrs));
52417}
52418
52419},{"../lib":177,"../plots/animation_attributes":217,"../plots/attributes":219,"../plots/frame_attributes":251,"../plots/layout_attributes":261,"../plots/polar/legacy/area_attributes":264,"../plots/polar/legacy/axis_attributes":265,"../registry":272,"./edit_types":205,"./plot_config":210}],212:[function(_dereq_,module,exports){
52420/**
52421* Copyright 2012-2020, Plotly, Inc.
52422* All rights reserved.
52423*
52424* This source code is licensed under the MIT license found in the
52425* LICENSE file in the root directory of this source tree.
52426*/
52427
52428
52429'use strict';
52430
52431var Lib = _dereq_('../lib');
52432var plotAttributes = _dereq_('../plots/attributes');
52433
52434var TEMPLATEITEMNAME = 'templateitemname';
52435
52436var templateAttrs = {
52437 name: {
52438 valType: 'string',
52439
52440 editType: 'none',
52441
52442 }
52443};
52444templateAttrs[TEMPLATEITEMNAME] = {
52445 valType: 'string',
52446
52447 editType: 'calc',
52448
52449};
52450
52451/**
52452 * templatedArray: decorate an attributes object with templating (and array)
52453 * properties.
52454 *
52455 * @param {string} name: the singular form of the array name. Sets
52456 * `_isLinkedToArray` to this, so the schema knows to treat this as an array.
52457 * @param {object} attrs: the item attributes. Since all callers are expected
52458 * to be constructing this object on the spot, we mutate it here for
52459 * performance, rather than extending a new object with it.
52460 *
52461 * @returns {object}: the decorated `attrs` object
52462 */
52463exports.templatedArray = function(name, attrs) {
52464 attrs._isLinkedToArray = name;
52465 attrs.name = templateAttrs.name;
52466 attrs[TEMPLATEITEMNAME] = templateAttrs[TEMPLATEITEMNAME];
52467 return attrs;
52468};
52469
52470/**
52471 * traceTemplater: logic for matching traces to trace templates
52472 *
52473 * @param {object} dataTemplate: collection of {traceType: [{template}, ...]}
52474 * ie each type the template applies to contains a list of template objects,
52475 * to be provided cyclically to data traces of that type.
52476 *
52477 * @returns {object}: {newTrace}, a function:
52478 * newTrace(traceIn): that takes the input traceIn, coerces its type, then
52479 * uses that type to find the next template to apply. returns the output
52480 * traceOut with template attached, ready to continue supplyDefaults.
52481 */
52482exports.traceTemplater = function(dataTemplate) {
52483 var traceCounts = {};
52484 var traceType, typeTemplates;
52485
52486 for(traceType in dataTemplate) {
52487 typeTemplates = dataTemplate[traceType];
52488 if(Array.isArray(typeTemplates) && typeTemplates.length) {
52489 traceCounts[traceType] = 0;
52490 }
52491 }
52492
52493 function newTrace(traceIn) {
52494 traceType = Lib.coerce(traceIn, {}, plotAttributes, 'type');
52495 var traceOut = {type: traceType, _template: null};
52496 if(traceType in traceCounts) {
52497 typeTemplates = dataTemplate[traceType];
52498 // cycle through traces in the template set for this type
52499 var typei = traceCounts[traceType] % typeTemplates.length;
52500 traceCounts[traceType]++;
52501 traceOut._template = typeTemplates[typei];
52502 } else {
52503 // TODO: anything we should do for types missing from the template?
52504 // try to apply some other type? Or just bail as we do here?
52505 // Actually I think yes, we should apply other types; would be nice
52506 // if all scatter* could inherit from each other, and if histogram
52507 // could inherit from bar, etc... but how to specify this? And do we
52508 // compose them, or if a type is present require it to be complete?
52509 // Actually this could apply to layout too - 3D annotations
52510 // inheriting from 2D, axes of different types inheriting from each
52511 // other...
52512 }
52513 return traceOut;
52514 }
52515
52516 return {
52517 newTrace: newTrace
52518 // TODO: function to figure out what's left & what didn't work
52519 };
52520};
52521
52522/**
52523 * newContainer: Create a new sub-container inside `container` and propagate any
52524 * applicable template to it. If there's no template, still propagates
52525 * `undefined` so relinkPrivate will not retain an old template!
52526 *
52527 * @param {object} container: the outer container, should already have _template
52528 * if there *is* a template for this plot
52529 * @param {string} name: the key of the new container to make
52530 * @param {string} baseName: if applicable, a base attribute to take the
52531 * template from, ie for xaxis3 the base would be xaxis
52532 *
52533 * @returns {object}: an object for inclusion _full*, empty except for the
52534 * appropriate template piece
52535 */
52536exports.newContainer = function(container, name, baseName) {
52537 var template = container._template;
52538 var part = template && (template[name] || (baseName && template[baseName]));
52539 if(!Lib.isPlainObject(part)) part = null;
52540
52541 var out = container[name] = {_template: part};
52542 return out;
52543};
52544
52545/**
52546 * arrayTemplater: special logic for templating both defaults and specific items
52547 * in a container array (annotations etc)
52548 *
52549 * @param {object} container: the outer container, should already have _template
52550 * if there *is* a template for this plot
52551 * @param {string} name: the name of the array to template (ie 'annotations')
52552 * will be used to find default ('annotationdefaults' object) and specific
52553 * ('annotations' array) template specs.
52554 * @param {string} inclusionAttr: the attribute determining this item's
52555 * inclusion in the output, usually 'visible' or 'enabled'
52556 *
52557 * @returns {object}: {newItem, defaultItems}, both functions:
52558 * newItem(itemIn): create an output item, bare except for the correct
52559 * template and name(s), as the base for supplyDefaults
52560 * defaultItems(): to be called after all newItem calls, return any
52561 * specific template items that have not already beeen included,
52562 * also as bare output items ready for supplyDefaults.
52563 */
52564exports.arrayTemplater = function(container, name, inclusionAttr) {
52565 var template = container._template;
52566 var defaultsTemplate = template && template[arrayDefaultKey(name)];
52567 var templateItems = template && template[name];
52568 if(!Array.isArray(templateItems) || !templateItems.length) {
52569 templateItems = [];
52570 }
52571
52572 var usedNames = {};
52573
52574 function newItem(itemIn) {
52575 // include name and templateitemname in the output object for ALL
52576 // container array items. Note: you could potentially use different
52577 // name and templateitemname, if you're using one template to make
52578 // another template. templateitemname would be the name in the original
52579 // template, and name is the new "subclassed" item name.
52580 var out = {name: itemIn.name, _input: itemIn};
52581 var templateItemName = out[TEMPLATEITEMNAME] = itemIn[TEMPLATEITEMNAME];
52582
52583 // no itemname: use the default template
52584 if(!validItemName(templateItemName)) {
52585 out._template = defaultsTemplate;
52586 return out;
52587 }
52588
52589 // look for an item matching this itemname
52590 // note these do not inherit from the default template, only the item.
52591 for(var i = 0; i < templateItems.length; i++) {
52592 var templateItem = templateItems[i];
52593 if(templateItem.name === templateItemName) {
52594 // Note: it's OK to use a template item more than once
52595 // but using it at least once will stop it from generating
52596 // a default item at the end.
52597 usedNames[templateItemName] = 1;
52598 out._template = templateItem;
52599 return out;
52600 }
52601 }
52602
52603 // Didn't find a matching template item, so since this item is intended
52604 // to only be modifications it's most likely broken. Hide it unless
52605 // it's explicitly marked visible - in which case it gets NO template,
52606 // not even the default.
52607 out[inclusionAttr] = itemIn[inclusionAttr] || false;
52608 // special falsy value we can look for in validateTemplate
52609 out._template = false;
52610 return out;
52611 }
52612
52613 function defaultItems() {
52614 var out = [];
52615 for(var i = 0; i < templateItems.length; i++) {
52616 var templateItem = templateItems[i];
52617 var name = templateItem.name;
52618 // only allow named items to be added as defaults,
52619 // and only allow each name once
52620 if(validItemName(name) && !usedNames[name]) {
52621 var outi = {
52622 _template: templateItem,
52623 name: name,
52624 _input: {_templateitemname: name}
52625 };
52626 outi[TEMPLATEITEMNAME] = templateItem[TEMPLATEITEMNAME];
52627 out.push(outi);
52628 usedNames[name] = 1;
52629 }
52630 }
52631 return out;
52632 }
52633
52634 return {
52635 newItem: newItem,
52636 defaultItems: defaultItems
52637 };
52638};
52639
52640function validItemName(name) {
52641 return name && typeof name === 'string';
52642}
52643
52644function arrayDefaultKey(name) {
52645 var lastChar = name.length - 1;
52646 if(name.charAt(lastChar) !== 's') {
52647 Lib.warn('bad argument to arrayDefaultKey: ' + name);
52648 }
52649 return name.substr(0, name.length - 1) + 'defaults';
52650}
52651exports.arrayDefaultKey = arrayDefaultKey;
52652
52653/**
52654 * arrayEditor: helper for editing array items that may have come from
52655 * template defaults (in which case they will not exist in the input yet)
52656 *
52657 * @param {object} parentIn: the input container (eg gd.layout)
52658 * @param {string} containerStr: the attribute string for the container inside
52659 * `parentIn`.
52660 * @param {object} itemOut: the _full* item (eg gd._fullLayout.annotations[0])
52661 * that we'll be editing. Assumed to have been created by `arrayTemplater`.
52662 *
52663 * @returns {object}: {modifyBase, modifyItem, getUpdateObj, applyUpdate}, all functions:
52664 * modifyBase(attr, value): Add an update that's *not* related to the item.
52665 * `attr` is the full attribute string.
52666 * modifyItem(attr, value): Add an update to the item. `attr` is just the
52667 * portion of the attribute string inside the item.
52668 * getUpdateObj(): Get the final constructed update object, to use in
52669 * `restyle` or `relayout`. Also resets the update object in case this
52670 * update was canceled.
52671 * applyUpdate(attr, value): optionally add an update `attr: value`,
52672 * then apply it to `parent` which should be the parent of `containerIn`,
52673 * ie the object to which `containerStr` is the attribute string.
52674 */
52675exports.arrayEditor = function(parentIn, containerStr, itemOut) {
52676 var lengthIn = (Lib.nestedProperty(parentIn, containerStr).get() || []).length;
52677 var index = itemOut._index;
52678 // Check that we are indeed off the end of this container.
52679 // Otherwise a devious user could put a key `_templateitemname` in their
52680 // own input and break lots of things.
52681 var templateItemName = (index >= lengthIn) && (itemOut._input || {})._templateitemname;
52682 if(templateItemName) index = lengthIn;
52683 var itemStr = containerStr + '[' + index + ']';
52684
52685 var update;
52686 function resetUpdate() {
52687 update = {};
52688 if(templateItemName) {
52689 update[itemStr] = {};
52690 update[itemStr][TEMPLATEITEMNAME] = templateItemName;
52691 }
52692 }
52693 resetUpdate();
52694
52695 function modifyBase(attr, value) {
52696 update[attr] = value;
52697 }
52698
52699 function modifyItem(attr, value) {
52700 if(templateItemName) {
52701 // we're making a new object: edit that object
52702 Lib.nestedProperty(update[itemStr], attr).set(value);
52703 } else {
52704 // we're editing an existing object: include *just* the edit
52705 update[itemStr + '.' + attr] = value;
52706 }
52707 }
52708
52709 function getUpdateObj() {
52710 var updateOut = update;
52711 resetUpdate();
52712 return updateOut;
52713 }
52714
52715 function applyUpdate(attr, value) {
52716 if(attr) modifyItem(attr, value);
52717 var updateToApply = getUpdateObj();
52718 for(var key in updateToApply) {
52719 Lib.nestedProperty(parentIn, key).set(updateToApply[key]);
52720 }
52721 }
52722
52723 return {
52724 modifyBase: modifyBase,
52725 modifyItem: modifyItem,
52726 getUpdateObj: getUpdateObj,
52727 applyUpdate: applyUpdate
52728 };
52729};
52730
52731},{"../lib":177,"../plots/attributes":219}],213:[function(_dereq_,module,exports){
52732/**
52733* Copyright 2012-2020, Plotly, Inc.
52734* All rights reserved.
52735*
52736* This source code is licensed under the MIT license found in the
52737* LICENSE file in the root directory of this source tree.
52738*/
52739
52740'use strict';
52741
52742var d3 = _dereq_('d3');
52743var Registry = _dereq_('../registry');
52744var Plots = _dereq_('../plots/plots');
52745
52746var Lib = _dereq_('../lib');
52747var clearGlCanvases = _dereq_('../lib/clear_gl_canvases');
52748
52749var Color = _dereq_('../components/color');
52750var Drawing = _dereq_('../components/drawing');
52751var Titles = _dereq_('../components/titles');
52752var ModeBar = _dereq_('../components/modebar');
52753
52754var Axes = _dereq_('../plots/cartesian/axes');
52755var alignmentConstants = _dereq_('../constants/alignment');
52756var axisConstraints = _dereq_('../plots/cartesian/constraints');
52757var enforceAxisConstraints = axisConstraints.enforce;
52758var cleanAxisConstraints = axisConstraints.clean;
52759var doAutoRange = _dereq_('../plots/cartesian/autorange').doAutoRange;
52760
52761var SVG_TEXT_ANCHOR_START = 'start';
52762var SVG_TEXT_ANCHOR_MIDDLE = 'middle';
52763var SVG_TEXT_ANCHOR_END = 'end';
52764
52765exports.layoutStyles = function(gd) {
52766 return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd);
52767};
52768
52769function overlappingDomain(xDomain, yDomain, domains) {
52770 for(var i = 0; i < domains.length; i++) {
52771 var existingX = domains[i][0];
52772 var existingY = domains[i][1];
52773
52774 if(existingX[0] >= xDomain[1] || existingX[1] <= xDomain[0]) {
52775 continue;
52776 }
52777 if(existingY[0] < yDomain[1] && existingY[1] > yDomain[0]) {
52778 return true;
52779 }
52780 }
52781 return false;
52782}
52783
52784function lsInner(gd) {
52785 var fullLayout = gd._fullLayout;
52786 var gs = fullLayout._size;
52787 var pad = gs.p;
52788 var axList = Axes.list(gd, '', true);
52789 var i, subplot, plotinfo, ax, xa, ya;
52790
52791 fullLayout._paperdiv.style({
52792 width: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroWidth && !gd.layout.width) ? '100%' : fullLayout.width + 'px',
52793 height: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroHeight && !gd.layout.height) ? '100%' : fullLayout.height + 'px'
52794 })
52795 .selectAll('.main-svg')
52796 .call(Drawing.setSize, fullLayout.width, fullLayout.height);
52797 gd._context.setBackground(gd, fullLayout.paper_bgcolor);
52798
52799 exports.drawMainTitle(gd);
52800 ModeBar.manage(gd);
52801
52802 // _has('cartesian') means SVG specifically, not GL2D - but GL2D
52803 // can still get here because it makes some of the SVG structure
52804 // for shared features like selections.
52805 if(!fullLayout._has('cartesian')) {
52806 return Plots.previousPromises(gd);
52807 }
52808
52809 function getLinePosition(ax, counterAx, side) {
52810 var lwHalf = ax._lw / 2;
52811
52812 if(ax._id.charAt(0) === 'x') {
52813 if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1);
52814 else if(side === 'top') return counterAx._offset - pad - lwHalf;
52815 return counterAx._offset + counterAx._length + pad + lwHalf;
52816 }
52817
52818 if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1);
52819 else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf;
52820 return counterAx._offset - pad - lwHalf;
52821 }
52822
52823 // some preparation of axis position info
52824 for(i = 0; i < axList.length; i++) {
52825 ax = axList[i];
52826
52827 var counterAx = ax._anchorAxis;
52828
52829 // clear axis line positions, to be set in the subplot loop below
52830 ax._linepositions = {};
52831
52832 // stash crispRounded linewidth so we don't need to pass gd all over the place
52833 ax._lw = Drawing.crispRound(gd, ax.linewidth, 1);
52834
52835 // figure out the main axis line and main mirror line position.
52836 // it's easier to follow the logic if we handle these separately from
52837 // ax._linepositions, which are only used by mirror=allticks
52838 // for non-main-subplot ticks, and mirror=all(ticks)? for zero line
52839 // hiding logic
52840 ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side);
52841 ax._mainMirrorPosition = (ax.mirror && counterAx) ?
52842 getLinePosition(ax, counterAx,
52843 alignmentConstants.OPPOSITE_SIDE[ax.side]) : null;
52844 }
52845
52846 // figure out which backgrounds we need to draw,
52847 // and in which layers to put them
52848 var lowerBackgroundIDs = [];
52849 var backgroundIds = [];
52850 var lowerDomains = [];
52851 // no need to draw background when paper and plot color are the same color,
52852 // activate mode just for large splom (which benefit the most from this
52853 // optimization), but this could apply to all cartesian subplots.
52854 var noNeedForBg = (
52855 Color.opacity(fullLayout.paper_bgcolor) === 1 &&
52856 Color.opacity(fullLayout.plot_bgcolor) === 1 &&
52857 fullLayout.paper_bgcolor === fullLayout.plot_bgcolor
52858 );
52859
52860 for(subplot in fullLayout._plots) {
52861 plotinfo = fullLayout._plots[subplot];
52862
52863 if(plotinfo.mainplot) {
52864 // mainplot is a reference to the main plot this one is overlaid on
52865 // so if it exists, this is an overlaid plot and we don't need to
52866 // give it its own background
52867 if(plotinfo.bg) {
52868 plotinfo.bg.remove();
52869 }
52870 plotinfo.bg = undefined;
52871 } else {
52872 var xDomain = plotinfo.xaxis.domain;
52873 var yDomain = plotinfo.yaxis.domain;
52874 var plotgroup = plotinfo.plotgroup;
52875
52876 if(overlappingDomain(xDomain, yDomain, lowerDomains)) {
52877 var pgNode = plotgroup.node();
52878 var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg');
52879 pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]);
52880 backgroundIds.push(subplot);
52881 } else {
52882 plotgroup.select('rect.bg').remove();
52883 lowerDomains.push([xDomain, yDomain]);
52884 if(!noNeedForBg) {
52885 lowerBackgroundIDs.push(subplot);
52886 backgroundIds.push(subplot);
52887 }
52888 }
52889 }
52890 }
52891
52892 // now create all the lower-layer backgrounds at once now that
52893 // we have the list of subplots that need them
52894 var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg')
52895 .data(lowerBackgroundIDs);
52896
52897 lowerBackgrounds.enter().append('rect')
52898 .classed('bg', true);
52899
52900 lowerBackgrounds.exit().remove();
52901
52902 lowerBackgrounds.each(function(subplot) {
52903 fullLayout._plots[subplot].bg = d3.select(this);
52904 });
52905
52906 // style all backgrounds
52907 for(i = 0; i < backgroundIds.length; i++) {
52908 plotinfo = fullLayout._plots[backgroundIds[i]];
52909 xa = plotinfo.xaxis;
52910 ya = plotinfo.yaxis;
52911
52912 if(plotinfo.bg) {
52913 plotinfo.bg
52914 .call(Drawing.setRect,
52915 xa._offset - pad, ya._offset - pad,
52916 xa._length + 2 * pad, ya._length + 2 * pad)
52917 .call(Color.fill, fullLayout.plot_bgcolor)
52918 .style('stroke-width', 0);
52919 }
52920 }
52921
52922 if(!fullLayout._hasOnlyLargeSploms) {
52923 for(subplot in fullLayout._plots) {
52924 plotinfo = fullLayout._plots[subplot];
52925 xa = plotinfo.xaxis;
52926 ya = plotinfo.yaxis;
52927
52928 // Clip so that data only shows up on the plot area.
52929 var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
52930
52931 var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
52932 s.classed('plotclip', true)
52933 .append('rect');
52934 });
52935
52936 plotinfo.clipRect = plotClip.select('rect').attr({
52937 width: xa._length,
52938 height: ya._length
52939 });
52940
52941 Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset);
52942
52943 var plotClipId;
52944 var layerClipId;
52945
52946 if(plotinfo._hasClipOnAxisFalse) {
52947 plotClipId = null;
52948 layerClipId = clipId;
52949 } else {
52950 plotClipId = clipId;
52951 layerClipId = null;
52952 }
52953
52954 Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);
52955
52956 // stash layer clipId value (null or same as clipId)
52957 // to DRY up Drawing.setClipUrl calls on trace-module and trace layers
52958 // downstream
52959 plotinfo.layerClipId = layerClipId;
52960 }
52961 }
52962
52963 var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop,
52964 leftYLineWidth, rightYLineWidth;
52965 var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight,
52966 connectYBottom, connectYTop;
52967 var extraSubplot;
52968
52969 function xLinePath(y) {
52970 return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight;
52971 }
52972
52973 function xLinePathFree(y) {
52974 return 'M' + xa._offset + ',' + y + 'h' + xa._length;
52975 }
52976
52977 function yLinePath(x) {
52978 return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom;
52979 }
52980
52981 function yLinePathFree(x) {
52982 return 'M' + x + ',' + ya._offset + 'v' + ya._length;
52983 }
52984
52985 function mainPath(ax, pathFn, pathFnFree) {
52986 if(!ax.showline || subplot !== ax._mainSubplot) return '';
52987 if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition);
52988 var out = pathFn(ax._mainLinePosition);
52989 if(ax.mirror) out += pathFn(ax._mainMirrorPosition);
52990 return out;
52991 }
52992
52993 for(subplot in fullLayout._plots) {
52994 plotinfo = fullLayout._plots[subplot];
52995 xa = plotinfo.xaxis;
52996 ya = plotinfo.yaxis;
52997
52998 /*
52999 * x lines get longer where they meet y lines, to make a crisp corner.
53000 * The x lines get the padding (margin.pad) plus the y line width to
53001 * fill up the corner nicely. Free x lines are excluded - they always
53002 * span exactly the data area of the plot
53003 *
53004 * | XXXXX
53005 * | XXXXX
53006 * |
53007 * +------
53008 * x1
53009 * -----
53010 * x2
53011 */
53012 var xPath = 'M0,0';
53013 if(shouldShowLinesOrTicks(xa, subplot)) {
53014 leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList);
53015 xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0);
53016 rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList);
53017 xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0);
53018 xLinesYBottom = getLinePosition(xa, ya, 'bottom');
53019 xLinesYTop = getLinePosition(xa, ya, 'top');
53020
53021 // save axis line positions for extra ticks to reference
53022 // each subplot that gets ticks from "allticks" gets an entry:
53023 // [left or bottom, right or top]
53024 extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot);
53025 if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) {
53026 xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
53027 }
53028
53029 xPath = mainPath(xa, xLinePath, xLinePathFree);
53030 if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) {
53031 xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop);
53032 }
53033
53034 plotinfo.xlines
53035 .style('stroke-width', xa._lw + 'px')
53036 .call(Color.stroke, xa.showline ?
53037 xa.linecolor : 'rgba(0,0,0,0)');
53038 }
53039 plotinfo.xlines.attr('d', xPath);
53040
53041 /*
53042 * y lines that meet x axes get longer only by margin.pad, because
53043 * the x axes fill in the corner space. Free y axes, like free x axes,
53044 * always span exactly the data area of the plot
53045 *
53046 * | | XXXX
53047 * y2| y1| XXXX
53048 * | | XXXX
53049 * |
53050 * +-----
53051 */
53052 var yPath = 'M0,0';
53053 if(shouldShowLinesOrTicks(ya, subplot)) {
53054 connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList);
53055 yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0);
53056 connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList);
53057 yLinesYTop = ya._offset - (connectYTop ? pad : 0);
53058 yLinesXLeft = getLinePosition(ya, xa, 'left');
53059 yLinesXRight = getLinePosition(ya, xa, 'right');
53060
53061 extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot);
53062 if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) {
53063 ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
53064 }
53065
53066 yPath = mainPath(ya, yLinePath, yLinePathFree);
53067 if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) {
53068 yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight);
53069 }
53070
53071 plotinfo.ylines
53072 .style('stroke-width', ya._lw + 'px')
53073 .call(Color.stroke, ya.showline ?
53074 ya.linecolor : 'rgba(0,0,0,0)');
53075 }
53076 plotinfo.ylines.attr('d', yPath);
53077 }
53078
53079 Axes.makeClipPaths(gd);
53080
53081 return Plots.previousPromises(gd);
53082}
53083
53084function shouldShowLinesOrTicks(ax, subplot) {
53085 return (ax.ticks || ax.showline) &&
53086 (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks');
53087}
53088
53089/*
53090 * should we draw a line on counterAx at this side of ax?
53091 * It's assumed that counterAx is known to overlay the subplot we're working on
53092 * but it may not be its main axis.
53093 */
53094function shouldShowLineThisSide(ax, side, counterAx) {
53095 // does counterAx get a line at all?
53096 if(!counterAx.showline || !counterAx._lw) return false;
53097
53098 // are we drawing *all* lines for counterAx?
53099 if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true;
53100
53101 var anchorAx = counterAx._anchorAxis;
53102
53103 // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring
53104 if(!anchorAx) return false;
53105
53106 // in order to handle cases where the user forgot to anchor this axis correctly
53107 // (because its default anchor has the same domain on the relevant end)
53108 // check whether the relevant position is the same.
53109 var sideIndex = alignmentConstants.FROM_BL[side];
53110 if(counterAx.side === side) {
53111 return anchorAx.domain[sideIndex] === ax.domain[sideIndex];
53112 }
53113 return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex];
53114}
53115
53116/*
53117 * Is there another axis intersecting `side` end of `ax`?
53118 * First look at `counterAx` (the axis for this subplot),
53119 * then at all other potential counteraxes on or overlaying this subplot.
53120 * Take the line width from the first one that has a line.
53121 */
53122function findCounterAxisLineWidth(ax, side, counterAx, axList) {
53123 if(shouldShowLineThisSide(ax, side, counterAx)) {
53124 return counterAx._lw;
53125 }
53126 for(var i = 0; i < axList.length; i++) {
53127 var axi = axList[i];
53128 if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) {
53129 return axi._lw;
53130 }
53131 }
53132 return 0;
53133}
53134
53135exports.drawMainTitle = function(gd) {
53136 var fullLayout = gd._fullLayout;
53137
53138 var textAnchor = getMainTitleTextAnchor(fullLayout);
53139 var dy = getMainTitleDy(fullLayout);
53140
53141 Titles.draw(gd, 'gtitle', {
53142 propContainer: fullLayout,
53143 propName: 'title.text',
53144 placeholder: fullLayout._dfltTitle.plot,
53145 attributes: {
53146 x: getMainTitleX(fullLayout, textAnchor),
53147 y: getMainTitleY(fullLayout, dy),
53148 'text-anchor': textAnchor,
53149 dy: dy
53150 }
53151 });
53152};
53153
53154function getMainTitleX(fullLayout, textAnchor) {
53155 var title = fullLayout.title;
53156 var gs = fullLayout._size;
53157 var hPadShift = 0;
53158
53159 if(textAnchor === SVG_TEXT_ANCHOR_START) {
53160 hPadShift = title.pad.l;
53161 } else if(textAnchor === SVG_TEXT_ANCHOR_END) {
53162 hPadShift = -title.pad.r;
53163 }
53164
53165 switch(title.xref) {
53166 case 'paper':
53167 return gs.l + gs.w * title.x + hPadShift;
53168 case 'container':
53169 default:
53170 return fullLayout.width * title.x + hPadShift;
53171 }
53172}
53173
53174function getMainTitleY(fullLayout, dy) {
53175 var title = fullLayout.title;
53176 var gs = fullLayout._size;
53177 var vPadShift = 0;
53178
53179 if(dy === '0em' || !dy) {
53180 vPadShift = -title.pad.b;
53181 } else if(dy === alignmentConstants.CAP_SHIFT + 'em') {
53182 vPadShift = title.pad.t;
53183 }
53184
53185 if(title.y === 'auto') {
53186 return gs.t / 2;
53187 } else {
53188 switch(title.yref) {
53189 case 'paper':
53190 return gs.t + gs.h - gs.h * title.y + vPadShift;
53191 case 'container':
53192 default:
53193 return fullLayout.height - fullLayout.height * title.y + vPadShift;
53194 }
53195 }
53196}
53197
53198function getMainTitleTextAnchor(fullLayout) {
53199 var title = fullLayout.title;
53200
53201 var textAnchor = SVG_TEXT_ANCHOR_MIDDLE;
53202 if(Lib.isRightAnchor(title)) {
53203 textAnchor = SVG_TEXT_ANCHOR_END;
53204 } else if(Lib.isLeftAnchor(title)) {
53205 textAnchor = SVG_TEXT_ANCHOR_START;
53206 }
53207
53208 return textAnchor;
53209}
53210
53211function getMainTitleDy(fullLayout) {
53212 var title = fullLayout.title;
53213
53214 var dy = '0em';
53215 if(Lib.isTopAnchor(title)) {
53216 dy = alignmentConstants.CAP_SHIFT + 'em';
53217 } else if(Lib.isMiddleAnchor(title)) {
53218 dy = alignmentConstants.MID_SHIFT + 'em';
53219 }
53220
53221 return dy;
53222}
53223
53224exports.doTraceStyle = function(gd) {
53225 var calcdata = gd.calcdata;
53226 var editStyleCalls = [];
53227 var i;
53228
53229 for(i = 0; i < calcdata.length; i++) {
53230 var cd = calcdata[i];
53231 var cd0 = cd[0] || {};
53232 var trace = cd0.trace || {};
53233 var _module = trace._module || {};
53234
53235 // See if we need to do arraysToCalcdata
53236 // call it regardless of what change we made, in case
53237 // supplyDefaults brought in an array that was already
53238 // in gd.data but not in gd._fullData previously
53239 var arraysToCalcdata = _module.arraysToCalcdata;
53240 if(arraysToCalcdata) arraysToCalcdata(cd, trace);
53241
53242 var editStyle = _module.editStyle;
53243 if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0});
53244 }
53245
53246 if(editStyleCalls.length) {
53247 for(i = 0; i < editStyleCalls.length; i++) {
53248 var edit = editStyleCalls[i];
53249 edit.fn(gd, edit.cd0);
53250 }
53251 clearGlCanvases(gd);
53252 exports.redrawReglTraces(gd);
53253 }
53254
53255 Plots.style(gd);
53256 Registry.getComponentMethod('legend', 'draw')(gd);
53257
53258 return Plots.previousPromises(gd);
53259};
53260
53261exports.doColorBars = function(gd) {
53262 Registry.getComponentMethod('colorbar', 'draw')(gd);
53263 return Plots.previousPromises(gd);
53264};
53265
53266// force plot() to redo the layout and replot with the modified layout
53267exports.layoutReplot = function(gd) {
53268 var layout = gd.layout;
53269 gd.layout = undefined;
53270 return Registry.call('plot', gd, '', layout);
53271};
53272
53273exports.doLegend = function(gd) {
53274 Registry.getComponentMethod('legend', 'draw')(gd);
53275 return Plots.previousPromises(gd);
53276};
53277
53278exports.doTicksRelayout = function(gd) {
53279 Axes.draw(gd, 'redraw');
53280
53281 if(gd._fullLayout._hasOnlyLargeSploms) {
53282 Registry.subplotsRegistry.splom.updateGrid(gd);
53283 clearGlCanvases(gd);
53284 exports.redrawReglTraces(gd);
53285 }
53286
53287 exports.drawMainTitle(gd);
53288 return Plots.previousPromises(gd);
53289};
53290
53291exports.doModeBar = function(gd) {
53292 var fullLayout = gd._fullLayout;
53293
53294 ModeBar.manage(gd);
53295
53296 for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
53297 var updateFx = fullLayout._basePlotModules[i].updateFx;
53298 if(updateFx) updateFx(gd);
53299 }
53300
53301 return Plots.previousPromises(gd);
53302};
53303
53304exports.doCamera = function(gd) {
53305 var fullLayout = gd._fullLayout;
53306 var sceneIds = fullLayout._subplots.gl3d;
53307
53308 for(var i = 0; i < sceneIds.length; i++) {
53309 var sceneLayout = fullLayout[sceneIds[i]];
53310 var scene = sceneLayout._scene;
53311
53312 scene.setViewport(sceneLayout);
53313 }
53314};
53315
53316exports.drawData = function(gd) {
53317 var fullLayout = gd._fullLayout;
53318
53319 clearGlCanvases(gd);
53320
53321 // loop over the base plot modules present on graph
53322 var basePlotModules = fullLayout._basePlotModules;
53323 for(var i = 0; i < basePlotModules.length; i++) {
53324 basePlotModules[i].plot(gd);
53325 }
53326
53327 exports.redrawReglTraces(gd);
53328
53329 // styling separate from drawing
53330 Plots.style(gd);
53331
53332 // draw components that can be drawn on axes,
53333 // and that do not push the margins
53334 Registry.getComponentMethod('shapes', 'draw')(gd);
53335 Registry.getComponentMethod('annotations', 'draw')(gd);
53336 Registry.getComponentMethod('images', 'draw')(gd);
53337
53338 // Mark the first render as complete
53339 fullLayout._replotting = false;
53340
53341 return Plots.previousPromises(gd);
53342};
53343
53344// Draw (or redraw) all regl-based traces in one go,
53345// useful during drag and selection where buffers of targeted traces are updated,
53346// but all traces need to be redrawn following clearGlCanvases.
53347//
53348// Note that _module.plot for regl trace does NOT draw things
53349// on the canvas, they only update the buffers.
53350// Drawing is perform here.
53351//
53352// TODO try adding per-subplot option using gl.SCISSOR_TEST for
53353// non-overlaying, disjoint subplots.
53354//
53355// TODO try to include parcoords in here.
53356// https://github.com/plotly/plotly.js/issues/3069
53357exports.redrawReglTraces = function(gd) {
53358 var fullLayout = gd._fullLayout;
53359
53360 if(fullLayout._has('regl')) {
53361 var fullData = gd._fullData;
53362 var cartesianIds = [];
53363 var polarIds = [];
53364 var i, sp;
53365
53366 if(fullLayout._hasOnlyLargeSploms) {
53367 fullLayout._splomGrid.draw();
53368 }
53369
53370 // N.B.
53371 // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering
53372 // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces
53373 // of a given module are `visible !== true`
53374 for(i = 0; i < fullData.length; i++) {
53375 var trace = fullData[i];
53376
53377 if(trace.visible === true && trace._length !== 0) {
53378 if(trace.type === 'splom') {
53379 fullLayout._splomScenes[trace.uid].draw();
53380 } else if(trace.type === 'scattergl') {
53381 Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis);
53382 } else if(trace.type === 'scatterpolargl') {
53383 Lib.pushUnique(polarIds, trace.subplot);
53384 }
53385 }
53386 }
53387
53388 for(i = 0; i < cartesianIds.length; i++) {
53389 sp = fullLayout._plots[cartesianIds[i]];
53390 if(sp._scene) sp._scene.draw();
53391 }
53392
53393 for(i = 0; i < polarIds.length; i++) {
53394 sp = fullLayout[polarIds[i]]._subplot;
53395 if(sp._scene) sp._scene.draw();
53396 }
53397 }
53398};
53399
53400exports.doAutoRangeAndConstraints = function(gd) {
53401 var fullLayout = gd._fullLayout;
53402 var axList = Axes.list(gd, '', true);
53403 var matchGroups = fullLayout._axisMatchGroups || [];
53404 var axLookup = {};
53405 var ax;
53406 var axRng;
53407
53408 for(var i = 0; i < axList.length; i++) {
53409 ax = axList[i];
53410 cleanAxisConstraints(gd, ax);
53411 doAutoRange(gd, ax);
53412 axLookup[ax._id] = 1;
53413 }
53414
53415 enforceAxisConstraints(gd);
53416
53417 groupLoop:
53418 for(var j = 0; j < matchGroups.length; j++) {
53419 var group = matchGroups[j];
53420 var rng = null;
53421 var id;
53422
53423 for(id in group) {
53424 ax = Axes.getFromId(gd, id);
53425
53426 // skip over 'missing' axes which do not pass through doAutoRange
53427 if(!axLookup[ax._id]) continue;
53428 // if one axis has autorange false, we're done
53429 if(ax.autorange === false) continue groupLoop;
53430
53431 axRng = Lib.simpleMap(ax.range, ax.r2l);
53432 if(rng) {
53433 if(rng[0] < rng[1]) {
53434 rng[0] = Math.min(rng[0], axRng[0]);
53435 rng[1] = Math.max(rng[1], axRng[1]);
53436 } else {
53437 rng[0] = Math.max(rng[0], axRng[0]);
53438 rng[1] = Math.min(rng[1], axRng[1]);
53439 }
53440 } else {
53441 rng = axRng;
53442 }
53443 }
53444
53445 for(id in group) {
53446 ax = Axes.getFromId(gd, id);
53447 ax.range = Lib.simpleMap(rng, ax.l2r);
53448 ax._input.range = ax.range.slice();
53449 ax.setScale();
53450 }
53451 }
53452};
53453
53454// An initial paint must be completed before these components can be
53455// correctly sized and the whole plot re-margined. fullLayout._replotting must
53456// be set to false before these will work properly.
53457exports.finalDraw = function(gd) {
53458 // TODO: rangesliders really belong in marginPushers but they need to be
53459 // drawn after data - can we at least get the margin pushing part separated
53460 // out and done earlier?
53461 Registry.getComponentMethod('rangeslider', 'draw')(gd);
53462 // TODO: rangeselector only needs to be here (in addition to drawMarginPushers)
53463 // because the margins need to be fully determined before we can call
53464 // autorange and update axis ranges (which rangeselector needs to know which
53465 // button is active). Can we break out its automargin step from its draw step?
53466 Registry.getComponentMethod('rangeselector', 'draw')(gd);
53467};
53468
53469exports.drawMarginPushers = function(gd) {
53470 Registry.getComponentMethod('legend', 'draw')(gd);
53471 Registry.getComponentMethod('rangeselector', 'draw')(gd);
53472 Registry.getComponentMethod('sliders', 'draw')(gd);
53473 Registry.getComponentMethod('updatemenus', 'draw')(gd);
53474 Registry.getComponentMethod('colorbar', 'draw')(gd);
53475};
53476
53477},{"../components/color":50,"../components/drawing":72,"../components/modebar":110,"../components/titles":145,"../constants/alignment":152,"../lib":177,"../lib/clear_gl_canvases":164,"../plots/cartesian/autorange":221,"../plots/cartesian/axes":222,"../plots/cartesian/constraints":229,"../plots/plots":263,"../registry":272,"d3":13}],214:[function(_dereq_,module,exports){
53478/**
53479* Copyright 2012-2020, Plotly, Inc.
53480* All rights reserved.
53481*
53482* This source code is licensed under the MIT license found in the
53483* LICENSE file in the root directory of this source tree.
53484*/
53485
53486
53487'use strict';
53488
53489var Lib = _dereq_('../lib');
53490var isPlainObject = Lib.isPlainObject;
53491var PlotSchema = _dereq_('./plot_schema');
53492var Plots = _dereq_('../plots/plots');
53493var plotAttributes = _dereq_('../plots/attributes');
53494var Template = _dereq_('./plot_template');
53495var dfltConfig = _dereq_('./plot_config').dfltConfig;
53496
53497/**
53498 * Plotly.makeTemplate: create a template off an existing figure to reuse
53499 * style attributes on other figures.
53500 *
53501 * Note: separated from the rest of templates because otherwise we get circular
53502 * references due to PlotSchema.
53503 *
53504 * @param {object|DOM element|string} figure: The figure to base the template on
53505 * should contain a trace array `figure.data`
53506 * and a layout object `figure.layout`
53507 * @returns {object} template: the extracted template - can then be used as
53508 * `layout.template` in another figure.
53509 */
53510exports.makeTemplate = function(figure) {
53511 figure = Lib.isPlainObject(figure) ? figure : Lib.getGraphDiv(figure);
53512 figure = Lib.extendDeep({_context: dfltConfig}, {data: figure.data, layout: figure.layout});
53513 Plots.supplyDefaults(figure);
53514 var data = figure.data || [];
53515 var layout = figure.layout || {};
53516 // copy over a few items to help follow the schema
53517 layout._basePlotModules = figure._fullLayout._basePlotModules;
53518 layout._modules = figure._fullLayout._modules;
53519
53520 var template = {
53521 data: {},
53522 layout: {}
53523 };
53524
53525 /*
53526 * Note: we do NOT validate template values, we just take what's in the
53527 * user inputs data and layout, not the validated values in fullData and
53528 * fullLayout. Even if we were to validate here, there's no guarantee that
53529 * these values would still be valid when applied to a new figure, which
53530 * may contain different trace modes, different axes, etc. So it's
53531 * important that when applying a template we still validate the template
53532 * values, rather than just using them as defaults.
53533 */
53534
53535 data.forEach(function(trace) {
53536 // TODO: What if no style info is extracted for this trace. We may
53537 // not want an empty object as the null value.
53538 // TODO: allow transforms to contribute to templates?
53539 // as it stands they are ignored, which may be for the best...
53540
53541 var traceTemplate = {};
53542 walkStyleKeys(trace, traceTemplate, getTraceInfo.bind(null, trace));
53543
53544 var traceType = Lib.coerce(trace, {}, plotAttributes, 'type');
53545 var typeTemplates = template.data[traceType];
53546 if(!typeTemplates) typeTemplates = template.data[traceType] = [];
53547 typeTemplates.push(traceTemplate);
53548 });
53549
53550 walkStyleKeys(layout, template.layout, getLayoutInfo.bind(null, layout));
53551
53552 /*
53553 * Compose the new template with an existing one to the same effect
53554 *
53555 * NOTE: there's a possibility of slightly different behavior: if the plot
53556 * has an invalid value and the old template has a valid value for the same
53557 * attribute, the plot will use the old template value but this routine
53558 * will pull the invalid value (resulting in the original default).
53559 * In the general case it's not possible to solve this with a single value,
53560 * since valid options can be context-dependent. It could be solved with
53561 * a *list* of values, but that would be huge complexity for little gain.
53562 */
53563 delete template.layout.template;
53564 var oldTemplate = layout.template;
53565 if(isPlainObject(oldTemplate)) {
53566 var oldLayoutTemplate = oldTemplate.layout;
53567
53568 var i, traceType, oldTypeTemplates, oldTypeLen, typeTemplates, typeLen;
53569
53570 if(isPlainObject(oldLayoutTemplate)) {
53571 mergeTemplates(oldLayoutTemplate, template.layout);
53572 }
53573 var oldDataTemplate = oldTemplate.data;
53574 if(isPlainObject(oldDataTemplate)) {
53575 for(traceType in template.data) {
53576 oldTypeTemplates = oldDataTemplate[traceType];
53577 if(Array.isArray(oldTypeTemplates)) {
53578 typeTemplates = template.data[traceType];
53579 typeLen = typeTemplates.length;
53580 oldTypeLen = oldTypeTemplates.length;
53581 for(i = 0; i < typeLen; i++) {
53582 mergeTemplates(oldTypeTemplates[i % oldTypeLen], typeTemplates[i]);
53583 }
53584 for(i = typeLen; i < oldTypeLen; i++) {
53585 typeTemplates.push(Lib.extendDeep({}, oldTypeTemplates[i]));
53586 }
53587 }
53588 }
53589 for(traceType in oldDataTemplate) {
53590 if(!(traceType in template.data)) {
53591 template.data[traceType] = Lib.extendDeep([], oldDataTemplate[traceType]);
53592 }
53593 }
53594 }
53595 }
53596
53597 return template;
53598};
53599
53600function mergeTemplates(oldTemplate, newTemplate) {
53601 // we don't care about speed here, just make sure we have a totally
53602 // distinct object from the previous template
53603 oldTemplate = Lib.extendDeep({}, oldTemplate);
53604
53605 // sort keys so we always get annotationdefaults before annotations etc
53606 // so arrayTemplater will work right
53607 var oldKeys = Object.keys(oldTemplate).sort();
53608 var i, j;
53609
53610 function mergeOne(oldVal, newVal, key) {
53611 if(isPlainObject(newVal) && isPlainObject(oldVal)) {
53612 mergeTemplates(oldVal, newVal);
53613 } else if(Array.isArray(newVal) && Array.isArray(oldVal)) {
53614 // Note: omitted `inclusionAttr` from arrayTemplater here,
53615 // it's irrelevant as we only want the resulting `_template`.
53616 var templater = Template.arrayTemplater({_template: oldTemplate}, key);
53617 for(j = 0; j < newVal.length; j++) {
53618 var item = newVal[j];
53619 var oldItem = templater.newItem(item)._template;
53620 if(oldItem) mergeTemplates(oldItem, item);
53621 }
53622 var defaultItems = templater.defaultItems();
53623 for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template);
53624
53625 // templateitemname only applies to receiving plots
53626 for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname;
53627 }
53628 }
53629
53630 for(i = 0; i < oldKeys.length; i++) {
53631 var key = oldKeys[i];
53632 var oldVal = oldTemplate[key];
53633 if(key in newTemplate) {
53634 mergeOne(oldVal, newTemplate[key], key);
53635 } else newTemplate[key] = oldVal;
53636
53637 // if this is a base key from the old template (eg xaxis), look for
53638 // extended keys (eg xaxis2) in the new template to merge into
53639 if(getBaseKey(key) === key) {
53640 for(var key2 in newTemplate) {
53641 var baseKey2 = getBaseKey(key2);
53642 if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) {
53643 mergeOne(oldVal, newTemplate[key2], key);
53644 }
53645 }
53646 }
53647 }
53648}
53649
53650function getBaseKey(key) {
53651 return key.replace(/[0-9]+$/, '');
53652}
53653
53654function walkStyleKeys(parent, templateOut, getAttributeInfo, path, basePath) {
53655 var pathAttr = basePath && getAttributeInfo(basePath);
53656 for(var key in parent) {
53657 var child = parent[key];
53658 var nextPath = getNextPath(parent, key, path);
53659 var nextBasePath = getNextPath(parent, key, basePath);
53660 var attr = getAttributeInfo(nextBasePath);
53661 if(!attr) {
53662 var baseKey = getBaseKey(key);
53663 if(baseKey !== key) {
53664 nextBasePath = getNextPath(parent, baseKey, basePath);
53665 attr = getAttributeInfo(nextBasePath);
53666 }
53667 }
53668
53669 // we'll get an attr if path starts with a valid part, then has an
53670 // invalid ending. Make sure we got all the way to the end.
53671 if(pathAttr && (pathAttr === attr)) continue;
53672
53673 if(!attr || attr._noTemplating ||
53674 attr.valType === 'data_array' ||
53675 (attr.arrayOk && Array.isArray(child))
53676 ) {
53677 continue;
53678 }
53679
53680 if(!attr.valType && isPlainObject(child)) {
53681 walkStyleKeys(child, templateOut, getAttributeInfo, nextPath, nextBasePath);
53682 } else if(attr._isLinkedToArray && Array.isArray(child)) {
53683 var dfltDone = false;
53684 var namedIndex = 0;
53685 var usedNames = {};
53686 for(var i = 0; i < child.length; i++) {
53687 var item = child[i];
53688 if(isPlainObject(item)) {
53689 var name = item.name;
53690 if(name) {
53691 if(!usedNames[name]) {
53692 // named array items: allow all attributes except data arrays
53693 walkStyleKeys(item, templateOut, getAttributeInfo,
53694 getNextPath(child, namedIndex, nextPath),
53695 getNextPath(child, namedIndex, nextBasePath));
53696 namedIndex++;
53697 usedNames[name] = 1;
53698 }
53699 } else if(!dfltDone) {
53700 var dfltKey = Template.arrayDefaultKey(key);
53701 var dfltPath = getNextPath(parent, dfltKey, path);
53702
53703 // getAttributeInfo will fail if we try to use dfltKey directly.
53704 // Instead put this item into the next array element, then
53705 // pull it out and move it to dfltKey.
53706 var pathInArray = getNextPath(child, namedIndex, nextPath);
53707 walkStyleKeys(item, templateOut, getAttributeInfo, pathInArray,
53708 getNextPath(child, namedIndex, nextBasePath));
53709 var itemPropInArray = Lib.nestedProperty(templateOut, pathInArray);
53710 var dfltProp = Lib.nestedProperty(templateOut, dfltPath);
53711 dfltProp.set(itemPropInArray.get());
53712 itemPropInArray.set(null);
53713
53714 dfltDone = true;
53715 }
53716 }
53717 }
53718 } else {
53719 var templateProp = Lib.nestedProperty(templateOut, nextPath);
53720 templateProp.set(child);
53721 }
53722 }
53723}
53724
53725function getLayoutInfo(layout, path) {
53726 return PlotSchema.getLayoutValObject(
53727 layout, Lib.nestedProperty({}, path).parts
53728 );
53729}
53730
53731function getTraceInfo(trace, path) {
53732 return PlotSchema.getTraceValObject(
53733 trace, Lib.nestedProperty({}, path).parts
53734 );
53735}
53736
53737function getNextPath(parent, key, path) {
53738 var nextPath;
53739 if(!path) nextPath = key;
53740 else if(Array.isArray(parent)) nextPath = path + '[' + key + ']';
53741 else nextPath = path + '.' + key;
53742
53743 return nextPath;
53744}
53745
53746/**
53747 * validateTemplate: Test for consistency between the given figure and
53748 * a template, either already included in the figure or given separately.
53749 * Note that not every issue we identify here is necessarily a problem,
53750 * it depends on what you're using the template for.
53751 *
53752 * @param {object|DOM element} figure: the plot, with {data, layout} members,
53753 * to test the template against
53754 * @param {Optional(object)} template: the template, with its own {data, layout},
53755 * to test. If omitted, we will look for a template already attached as the
53756 * plot's `layout.template` attribute.
53757 *
53758 * @returns {array} array of error objects each containing:
53759 * - {string} code
53760 * error code ('missing', 'unused', 'reused', 'noLayout', 'noData')
53761 * - {string} msg
53762 * a full readable description of the issue.
53763 */
53764exports.validateTemplate = function(figureIn, template) {
53765 var figure = Lib.extendDeep({}, {
53766 _context: dfltConfig,
53767 data: figureIn.data,
53768 layout: figureIn.layout
53769 });
53770 var layout = figure.layout || {};
53771 if(!isPlainObject(template)) template = layout.template || {};
53772 var layoutTemplate = template.layout;
53773 var dataTemplate = template.data;
53774 var errorList = [];
53775
53776 figure.layout = layout;
53777 figure.layout.template = template;
53778 Plots.supplyDefaults(figure);
53779
53780 var fullLayout = figure._fullLayout;
53781 var fullData = figure._fullData;
53782
53783 var layoutPaths = {};
53784 function crawlLayoutForContainers(obj, paths) {
53785 for(var key in obj) {
53786 if(key.charAt(0) !== '_' && isPlainObject(obj[key])) {
53787 var baseKey = getBaseKey(key);
53788 var nextPaths = [];
53789 var i;
53790 for(i = 0; i < paths.length; i++) {
53791 nextPaths.push(getNextPath(obj, key, paths[i]));
53792 if(baseKey !== key) nextPaths.push(getNextPath(obj, baseKey, paths[i]));
53793 }
53794 for(i = 0; i < nextPaths.length; i++) {
53795 layoutPaths[nextPaths[i]] = 1;
53796 }
53797 crawlLayoutForContainers(obj[key], nextPaths);
53798 }
53799 }
53800 }
53801
53802 function crawlLayoutTemplateForContainers(obj, path) {
53803 for(var key in obj) {
53804 if(key.indexOf('defaults') === -1 && isPlainObject(obj[key])) {
53805 var nextPath = getNextPath(obj, key, path);
53806 if(layoutPaths[nextPath]) {
53807 crawlLayoutTemplateForContainers(obj[key], nextPath);
53808 } else {
53809 errorList.push({code: 'unused', path: nextPath});
53810 }
53811 }
53812 }
53813 }
53814
53815 if(!isPlainObject(layoutTemplate)) {
53816 errorList.push({code: 'layout'});
53817 } else {
53818 crawlLayoutForContainers(fullLayout, ['layout']);
53819 crawlLayoutTemplateForContainers(layoutTemplate, 'layout');
53820 }
53821
53822 if(!isPlainObject(dataTemplate)) {
53823 errorList.push({code: 'data'});
53824 } else {
53825 var typeCount = {};
53826 var traceType;
53827 for(var i = 0; i < fullData.length; i++) {
53828 var fullTrace = fullData[i];
53829 traceType = fullTrace.type;
53830 typeCount[traceType] = (typeCount[traceType] || 0) + 1;
53831 if(!fullTrace._fullInput._template) {
53832 // this takes care of the case of traceType in the data but not
53833 // the template
53834 errorList.push({
53835 code: 'missing',
53836 index: fullTrace._fullInput.index,
53837 traceType: traceType
53838 });
53839 }
53840 }
53841 for(traceType in dataTemplate) {
53842 var templateCount = dataTemplate[traceType].length;
53843 var dataCount = typeCount[traceType] || 0;
53844 if(templateCount > dataCount) {
53845 errorList.push({
53846 code: 'unused',
53847 traceType: traceType,
53848 templateCount: templateCount,
53849 dataCount: dataCount
53850 });
53851 } else if(dataCount > templateCount) {
53852 errorList.push({
53853 code: 'reused',
53854 traceType: traceType,
53855 templateCount: templateCount,
53856 dataCount: dataCount
53857 });
53858 }
53859 }
53860 }
53861
53862 // _template: false is when someone tried to modify an array item
53863 // but there was no template with matching name
53864 function crawlForMissingTemplates(obj, path) {
53865 for(var key in obj) {
53866 if(key.charAt(0) === '_') continue;
53867 var val = obj[key];
53868 var nextPath = getNextPath(obj, key, path);
53869 if(isPlainObject(val)) {
53870 if(Array.isArray(obj) && val._template === false && val.templateitemname) {
53871 errorList.push({
53872 code: 'missing',
53873 path: nextPath,
53874 templateitemname: val.templateitemname
53875 });
53876 }
53877 crawlForMissingTemplates(val, nextPath);
53878 } else if(Array.isArray(val) && hasPlainObject(val)) {
53879 crawlForMissingTemplates(val, nextPath);
53880 }
53881 }
53882 }
53883 crawlForMissingTemplates({data: fullData, layout: fullLayout}, '');
53884
53885 if(errorList.length) return errorList.map(format);
53886};
53887
53888function hasPlainObject(arr) {
53889 for(var i = 0; i < arr.length; i++) {
53890 if(isPlainObject(arr[i])) return true;
53891 }
53892}
53893
53894function format(opts) {
53895 var msg;
53896 switch(opts.code) {
53897 case 'data':
53898 msg = 'The template has no key data.';
53899 break;
53900 case 'layout':
53901 msg = 'The template has no key layout.';
53902 break;
53903 case 'missing':
53904 if(opts.path) {
53905 msg = 'There are no templates for item ' + opts.path +
53906 ' with name ' + opts.templateitemname;
53907 } else {
53908 msg = 'There are no templates for trace ' + opts.index +
53909 ', of type ' + opts.traceType + '.';
53910 }
53911 break;
53912 case 'unused':
53913 if(opts.path) {
53914 msg = 'The template item at ' + opts.path +
53915 ' was not used in constructing the plot.';
53916 } else if(opts.dataCount) {
53917 msg = 'Some of the templates of type ' + opts.traceType +
53918 ' were not used. The template has ' + opts.templateCount +
53919 ' traces, the data only has ' + opts.dataCount +
53920 ' of this type.';
53921 } else {
53922 msg = 'The template has ' + opts.templateCount +
53923 ' traces of type ' + opts.traceType +
53924 ' but there are none in the data.';
53925 }
53926 break;
53927 case 'reused':
53928 msg = 'Some of the templates of type ' + opts.traceType +
53929 ' were used more than once. The template has ' +
53930 opts.templateCount + ' traces, the data has ' +
53931 opts.dataCount + ' of this type.';
53932 break;
53933 }
53934 opts.msg = msg;
53935
53936 return opts;
53937}
53938
53939},{"../lib":177,"../plots/attributes":219,"../plots/plots":263,"./plot_config":210,"./plot_schema":211,"./plot_template":212}],215:[function(_dereq_,module,exports){
53940/**
53941* Copyright 2012-2020, Plotly, Inc.
53942* All rights reserved.
53943*
53944* This source code is licensed under the MIT license found in the
53945* LICENSE file in the root directory of this source tree.
53946*/
53947
53948'use strict';
53949
53950var isNumeric = _dereq_('fast-isnumeric');
53951
53952var plotApi = _dereq_('./plot_api');
53953var plots = _dereq_('../plots/plots');
53954var Lib = _dereq_('../lib');
53955
53956var helpers = _dereq_('../snapshot/helpers');
53957var toSVG = _dereq_('../snapshot/tosvg');
53958var svgToImg = _dereq_('../snapshot/svgtoimg');
53959var version = _dereq_('../version').version;
53960
53961var attrs = {
53962 format: {
53963 valType: 'enumerated',
53964 values: ['png', 'jpeg', 'webp', 'svg', 'full-json'],
53965 dflt: 'png',
53966
53967 },
53968 width: {
53969 valType: 'number',
53970 min: 1,
53971
53972 },
53973 height: {
53974 valType: 'number',
53975 min: 1,
53976
53977 },
53978 scale: {
53979 valType: 'number',
53980 min: 0,
53981 dflt: 1,
53982
53983 },
53984 setBackground: {
53985 valType: 'any',
53986 dflt: false,
53987
53988 },
53989 imageDataOnly: {
53990 valType: 'boolean',
53991 dflt: false,
53992
53993 }
53994};
53995
53996/** Plotly.toImage
53997 *
53998 * @param {object | string | HTML div} gd
53999 * can either be a data/layout/config object
54000 * or an existing graph <div>
54001 * or an id to an existing graph <div>
54002 * @param {object} opts (see above)
54003 * @return {promise}
54004 */
54005function toImage(gd, opts) {
54006 opts = opts || {};
54007
54008 var data;
54009 var layout;
54010 var config;
54011 var fullLayout;
54012
54013 if(Lib.isPlainObject(gd)) {
54014 data = gd.data || [];
54015 layout = gd.layout || {};
54016 config = gd.config || {};
54017 fullLayout = {};
54018 } else {
54019 gd = Lib.getGraphDiv(gd);
54020 data = Lib.extendDeep([], gd.data);
54021 layout = Lib.extendDeep({}, gd.layout);
54022 config = gd._context;
54023 fullLayout = gd._fullLayout || {};
54024 }
54025
54026 function isImpliedOrValid(attr) {
54027 return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
54028 }
54029
54030 if((!isImpliedOrValid('width') && opts.width !== null) ||
54031 (!isImpliedOrValid('height') && opts.height !== null)) {
54032 throw new Error('Height and width should be pixel values.');
54033 }
54034
54035 if(!isImpliedOrValid('format')) {
54036 throw new Error('Image format is not jpeg, png, svg or webp.');
54037 }
54038
54039 var fullOpts = {};
54040
54041 function coerce(attr, dflt) {
54042 return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
54043 }
54044
54045 var format = coerce('format');
54046 var width = coerce('width');
54047 var height = coerce('height');
54048 var scale = coerce('scale');
54049 var setBackground = coerce('setBackground');
54050 var imageDataOnly = coerce('imageDataOnly');
54051
54052 // put the cloned div somewhere off screen before attaching to DOM
54053 var clonedGd = document.createElement('div');
54054 clonedGd.style.position = 'absolute';
54055 clonedGd.style.left = '-5000px';
54056 document.body.appendChild(clonedGd);
54057
54058 // extend layout with image options
54059 var layoutImage = Lib.extendFlat({}, layout);
54060 if(width) {
54061 layoutImage.width = width;
54062 } else if(opts.width === null && isNumeric(fullLayout.width)) {
54063 layoutImage.width = fullLayout.width;
54064 }
54065 if(height) {
54066 layoutImage.height = height;
54067 } else if(opts.height === null && isNumeric(fullLayout.height)) {
54068 layoutImage.height = fullLayout.height;
54069 }
54070
54071 // extend config for static plot
54072 var configImage = Lib.extendFlat({}, config, {
54073 _exportedPlot: true,
54074 staticPlot: true,
54075 setBackground: setBackground
54076 });
54077
54078 var redrawFunc = helpers.getRedrawFunc(clonedGd);
54079
54080 function wait() {
54081 return new Promise(function(resolve) {
54082 setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
54083 });
54084 }
54085
54086 function convert() {
54087 return new Promise(function(resolve, reject) {
54088 var svg = toSVG(clonedGd, format, scale);
54089 var width = clonedGd._fullLayout.width;
54090 var height = clonedGd._fullLayout.height;
54091
54092 function cleanup() {
54093 plotApi.purge(clonedGd);
54094 document.body.removeChild(clonedGd);
54095 }
54096
54097 if(format === 'full-json') {
54098 var json = plots.graphJson(clonedGd, false, 'keepdata', 'object', true, true);
54099 json.version = version;
54100 json = JSON.stringify(json);
54101 cleanup();
54102 if(imageDataOnly) {
54103 return resolve(json);
54104 } else {
54105 return resolve(helpers.encodeJSON(json));
54106 }
54107 }
54108
54109 cleanup();
54110
54111 if(format === 'svg') {
54112 if(imageDataOnly) {
54113 return resolve(svg);
54114 } else {
54115 return resolve(helpers.encodeSVG(svg));
54116 }
54117 }
54118
54119 var canvas = document.createElement('canvas');
54120 canvas.id = Lib.randstr();
54121
54122 svgToImg({
54123 format: format,
54124 width: width,
54125 height: height,
54126 scale: scale,
54127 canvas: canvas,
54128 svg: svg,
54129 // ask svgToImg to return a Promise
54130 // rather than EventEmitter
54131 // leave EventEmitter for backward
54132 // compatibility
54133 promise: true
54134 })
54135 .then(resolve)
54136 .catch(reject);
54137 });
54138 }
54139
54140 function urlToImageData(url) {
54141 if(imageDataOnly) {
54142 return url.replace(helpers.IMAGE_URL_PREFIX, '');
54143 } else {
54144 return url;
54145 }
54146 }
54147
54148 return new Promise(function(resolve, reject) {
54149 plotApi.plot(clonedGd, data, layoutImage, configImage)
54150 .then(redrawFunc)
54151 .then(wait)
54152 .then(convert)
54153 .then(function(url) { resolve(urlToImageData(url)); })
54154 .catch(function(err) { reject(err); });
54155 });
54156}
54157
54158module.exports = toImage;
54159
54160},{"../lib":177,"../plots/plots":263,"../snapshot/helpers":276,"../snapshot/svgtoimg":278,"../snapshot/tosvg":280,"../version":331,"./plot_api":209,"fast-isnumeric":15}],216:[function(_dereq_,module,exports){
54161/**
54162* Copyright 2012-2020, Plotly, Inc.
54163* All rights reserved.
54164*
54165* This source code is licensed under the MIT license found in the
54166* LICENSE file in the root directory of this source tree.
54167*/
54168
54169'use strict';
54170
54171var Lib = _dereq_('../lib');
54172var Plots = _dereq_('../plots/plots');
54173var PlotSchema = _dereq_('./plot_schema');
54174var dfltConfig = _dereq_('./plot_config').dfltConfig;
54175
54176var isPlainObject = Lib.isPlainObject;
54177var isArray = Array.isArray;
54178var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
54179
54180/**
54181 * Validate a data array and layout object.
54182 *
54183 * @param {array} data
54184 * @param {object} layout
54185 *
54186 * @return {array} array of error objects each containing:
54187 * - {string} code
54188 * error code ('object', 'array', 'schema', 'unused', 'invisible' or 'value')
54189 * - {string} container
54190 * container where the error occurs ('data' or 'layout')
54191 * - {number} trace
54192 * trace index of the 'data' container where the error occurs
54193 * - {array} path
54194 * nested path to the key that causes the error
54195 * - {string} astr
54196 * attribute string variant of 'path' compatible with Plotly.restyle and
54197 * Plotly.relayout.
54198 * - {string} msg
54199 * error message (shown in console in logger config argument is enable)
54200 */
54201module.exports = function validate(data, layout) {
54202 var schema = PlotSchema.get();
54203 var errorList = [];
54204 var gd = {_context: Lib.extendFlat({}, dfltConfig)};
54205
54206 var dataIn, layoutIn;
54207
54208 if(isArray(data)) {
54209 gd.data = Lib.extendDeep([], data);
54210 dataIn = data;
54211 } else {
54212 gd.data = [];
54213 dataIn = [];
54214 errorList.push(format('array', 'data'));
54215 }
54216
54217 if(isPlainObject(layout)) {
54218 gd.layout = Lib.extendDeep({}, layout);
54219 layoutIn = layout;
54220 } else {
54221 gd.layout = {};
54222 layoutIn = {};
54223 if(arguments.length > 1) {
54224 errorList.push(format('object', 'layout'));
54225 }
54226 }
54227
54228 // N.B. dataIn and layoutIn are in general not the same as
54229 // gd.data and gd.layout after supplyDefaults as some attributes
54230 // in gd.data and gd.layout (still) get mutated during this step.
54231
54232 Plots.supplyDefaults(gd);
54233
54234 var dataOut = gd._fullData;
54235 var len = dataIn.length;
54236
54237 for(var i = 0; i < len; i++) {
54238 var traceIn = dataIn[i];
54239 var base = ['data', i];
54240
54241 if(!isPlainObject(traceIn)) {
54242 errorList.push(format('object', base));
54243 continue;
54244 }
54245
54246 var traceOut = dataOut[i];
54247 var traceType = traceOut.type;
54248 var traceSchema = schema.traces[traceType].attributes;
54249
54250 // PlotSchema does something fancy with trace 'type', reset it here
54251 // to make the trace schema compatible with Lib.validate.
54252 traceSchema.type = {
54253 valType: 'enumerated',
54254 values: [traceType]
54255 };
54256
54257 if(traceOut.visible === false && traceIn.visible !== false) {
54258 errorList.push(format('invisible', base));
54259 }
54260
54261 crawl(traceIn, traceOut, traceSchema, errorList, base);
54262
54263 var transformsIn = traceIn.transforms;
54264 var transformsOut = traceOut.transforms;
54265
54266 if(transformsIn) {
54267 if(!isArray(transformsIn)) {
54268 errorList.push(format('array', base, ['transforms']));
54269 }
54270
54271 base.push('transforms');
54272
54273 for(var j = 0; j < transformsIn.length; j++) {
54274 var path = ['transforms', j];
54275 var transformType = transformsIn[j].type;
54276
54277 if(!isPlainObject(transformsIn[j])) {
54278 errorList.push(format('object', base, path));
54279 continue;
54280 }
54281
54282 var transformSchema = schema.transforms[transformType] ?
54283 schema.transforms[transformType].attributes :
54284 {};
54285
54286 // add 'type' to transform schema to validate the transform type
54287 transformSchema.type = {
54288 valType: 'enumerated',
54289 values: Object.keys(schema.transforms)
54290 };
54291
54292 crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path);
54293 }
54294 }
54295 }
54296
54297 var layoutOut = gd._fullLayout;
54298 var layoutSchema = fillLayoutSchema(schema, dataOut);
54299
54300 crawl(layoutIn, layoutOut, layoutSchema, errorList, 'layout');
54301
54302 // return undefined if no validation errors were found
54303 return (errorList.length === 0) ? void(0) : errorList;
54304};
54305
54306function crawl(objIn, objOut, schema, list, base, path) {
54307 path = path || [];
54308
54309 var keys = Object.keys(objIn);
54310
54311 for(var i = 0; i < keys.length; i++) {
54312 var k = keys[i];
54313
54314 // transforms are handled separately
54315 if(k === 'transforms') continue;
54316
54317 var p = path.slice();
54318 p.push(k);
54319
54320 var valIn = objIn[k];
54321 var valOut = objOut[k];
54322
54323 var nestedSchema = getNestedSchema(schema, k);
54324 var nestedValType = (nestedSchema || {}).valType;
54325 var isInfoArray = nestedValType === 'info_array';
54326 var isColorscale = nestedValType === 'colorscale';
54327 var items = (nestedSchema || {}).items;
54328
54329 if(!isInSchema(schema, k)) {
54330 list.push(format('schema', base, p));
54331 } else if(isPlainObject(valIn) && isPlainObject(valOut) && nestedValType !== 'any') {
54332 crawl(valIn, valOut, nestedSchema, list, base, p);
54333 } else if(isInfoArray && isArray(valIn)) {
54334 if(valIn.length > valOut.length) {
54335 list.push(format('unused', base, p.concat(valOut.length)));
54336 }
54337 var len = valOut.length;
54338 var arrayItems = Array.isArray(items);
54339 if(arrayItems) len = Math.min(len, items.length);
54340 var m, n, item, valInPart, valOutPart;
54341 if(nestedSchema.dimensions === 2) {
54342 for(n = 0; n < len; n++) {
54343 if(isArray(valIn[n])) {
54344 if(valIn[n].length > valOut[n].length) {
54345 list.push(format('unused', base, p.concat(n, valOut[n].length)));
54346 }
54347 var len2 = valOut[n].length;
54348 for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) {
54349 item = arrayItems ? items[n][m] : items;
54350 valInPart = valIn[n][m];
54351 valOutPart = valOut[n][m];
54352 if(!Lib.validate(valInPart, item)) {
54353 list.push(format('value', base, p.concat(n, m), valInPart));
54354 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
54355 list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart));
54356 }
54357 }
54358 } else {
54359 list.push(format('array', base, p.concat(n), valIn[n]));
54360 }
54361 }
54362 } else {
54363 for(n = 0; n < len; n++) {
54364 item = arrayItems ? items[n] : items;
54365 valInPart = valIn[n];
54366 valOutPart = valOut[n];
54367 if(!Lib.validate(valInPart, item)) {
54368 list.push(format('value', base, p.concat(n), valInPart));
54369 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
54370 list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart));
54371 }
54372 }
54373 }
54374 } else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
54375 var _nestedSchema = items[Object.keys(items)[0]];
54376 var indexList = [];
54377
54378 var j, _p;
54379
54380 // loop over valOut items while keeping track of their
54381 // corresponding input container index (given by _index)
54382 for(j = 0; j < valOut.length; j++) {
54383 var _index = valOut[j]._index || j;
54384
54385 _p = p.slice();
54386 _p.push(_index);
54387
54388 if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) {
54389 indexList.push(_index);
54390 var valInj = valIn[_index];
54391 var valOutj = valOut[j];
54392 if(isPlainObject(valInj) && valInj.visible !== false && valOutj.visible === false) {
54393 list.push(format('invisible', base, _p));
54394 } else crawl(valInj, valOutj, _nestedSchema, list, base, _p);
54395 }
54396 }
54397
54398 // loop over valIn to determine where it went wrong for some items
54399 for(j = 0; j < valIn.length; j++) {
54400 _p = p.slice();
54401 _p.push(j);
54402
54403 if(!isPlainObject(valIn[j])) {
54404 list.push(format('object', base, _p, valIn[j]));
54405 } else if(indexList.indexOf(j) === -1) {
54406 list.push(format('unused', base, _p));
54407 }
54408 }
54409 } else if(!isPlainObject(valIn) && isPlainObject(valOut)) {
54410 list.push(format('object', base, p, valIn));
54411 } else if(!isArrayOrTypedArray(valIn) && isArrayOrTypedArray(valOut) && !isInfoArray && !isColorscale) {
54412 list.push(format('array', base, p, valIn));
54413 } else if(!(k in objOut)) {
54414 list.push(format('unused', base, p, valIn));
54415 } else if(!Lib.validate(valIn, nestedSchema)) {
54416 list.push(format('value', base, p, valIn));
54417 } else if(nestedSchema.valType === 'enumerated' &&
54418 ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut)
54419 ) {
54420 list.push(format('dynamic', base, p, valIn, valOut));
54421 }
54422 }
54423
54424 return list;
54425}
54426
54427// the 'full' layout schema depends on the traces types presents
54428function fillLayoutSchema(schema, dataOut) {
54429 var layoutSchema = schema.layout.layoutAttributes;
54430
54431 for(var i = 0; i < dataOut.length; i++) {
54432 var traceOut = dataOut[i];
54433 var traceSchema = schema.traces[traceOut.type];
54434 var traceLayoutAttr = traceSchema.layoutAttributes;
54435
54436 if(traceLayoutAttr) {
54437 if(traceOut.subplot) {
54438 Lib.extendFlat(layoutSchema[traceSchema.attributes.subplot.dflt], traceLayoutAttr);
54439 } else {
54440 Lib.extendFlat(layoutSchema, traceLayoutAttr);
54441 }
54442 }
54443 }
54444
54445 return layoutSchema;
54446}
54447
54448// validation error codes
54449var code2msgFunc = {
54450 object: function(base, astr) {
54451 var prefix;
54452
54453 if(base === 'layout' && astr === '') prefix = 'The layout argument';
54454 else if(base[0] === 'data' && astr === '') {
54455 prefix = 'Trace ' + base[1] + ' in the data argument';
54456 } else prefix = inBase(base) + 'key ' + astr;
54457
54458 return prefix + ' must be linked to an object container';
54459 },
54460 array: function(base, astr) {
54461 var prefix;
54462
54463 if(base === 'data') prefix = 'The data argument';
54464 else prefix = inBase(base) + 'key ' + astr;
54465
54466 return prefix + ' must be linked to an array container';
54467 },
54468 schema: function(base, astr) {
54469 return inBase(base) + 'key ' + astr + ' is not part of the schema';
54470 },
54471 unused: function(base, astr, valIn) {
54472 var target = isPlainObject(valIn) ? 'container' : 'key';
54473
54474 return inBase(base) + target + ' ' + astr + ' did not get coerced';
54475 },
54476 dynamic: function(base, astr, valIn, valOut) {
54477 return [
54478 inBase(base) + 'key',
54479 astr,
54480 '(set to \'' + valIn + '\')',
54481 'got reset to',
54482 '\'' + valOut + '\'',
54483 'during defaults.'
54484 ].join(' ');
54485 },
54486 invisible: function(base, astr) {
54487 return (
54488 astr ? (inBase(base) + 'item ' + astr) : ('Trace ' + base[1])
54489 ) + ' got defaulted to be not visible';
54490 },
54491 value: function(base, astr, valIn) {
54492 return [
54493 inBase(base) + 'key ' + astr,
54494 'is set to an invalid value (' + valIn + ')'
54495 ].join(' ');
54496 }
54497};
54498
54499function inBase(base) {
54500 if(isArray(base)) return 'In data trace ' + base[1] + ', ';
54501
54502 return 'In ' + base + ', ';
54503}
54504
54505function format(code, base, path, valIn, valOut) {
54506 path = path || '';
54507
54508 var container, trace;
54509
54510 // container is either 'data' or 'layout
54511 // trace is the trace index if 'data', null otherwise
54512
54513 if(isArray(base)) {
54514 container = base[0];
54515 trace = base[1];
54516 } else {
54517 container = base;
54518 trace = null;
54519 }
54520
54521 var astr = convertPathToAttributeString(path);
54522 var msg = code2msgFunc[code](base, astr, valIn, valOut);
54523
54524 // log to console if logger config option is enabled
54525 Lib.log(msg);
54526
54527 return {
54528 code: code,
54529 container: container,
54530 trace: trace,
54531 path: path,
54532 astr: astr,
54533 msg: msg
54534 };
54535}
54536
54537function isInSchema(schema, key) {
54538 var parts = splitKey(key);
54539 var keyMinusId = parts.keyMinusId;
54540 var id = parts.id;
54541
54542 if((keyMinusId in schema) && schema[keyMinusId]._isSubplotObj && id) {
54543 return true;
54544 }
54545
54546 return (key in schema);
54547}
54548
54549function getNestedSchema(schema, key) {
54550 if(key in schema) return schema[key];
54551
54552 var parts = splitKey(key);
54553
54554 return schema[parts.keyMinusId];
54555}
54556
54557var idRegex = Lib.counterRegex('([a-z]+)');
54558
54559function splitKey(key) {
54560 var idMatch = key.match(idRegex);
54561
54562 return {
54563 keyMinusId: idMatch && idMatch[1],
54564 id: idMatch && idMatch[2]
54565 };
54566}
54567
54568function convertPathToAttributeString(path) {
54569 if(!isArray(path)) return String(path);
54570
54571 var astr = '';
54572
54573 for(var i = 0; i < path.length; i++) {
54574 var p = path[i];
54575
54576 if(typeof p === 'number') {
54577 astr = astr.substr(0, astr.length - 1) + '[' + p + ']';
54578 } else {
54579 astr += p;
54580 }
54581
54582 if(i < path.length - 1) astr += '.';
54583 }
54584
54585 return astr;
54586}
54587
54588},{"../lib":177,"../plots/plots":263,"./plot_config":210,"./plot_schema":211}],217:[function(_dereq_,module,exports){
54589/**
54590* Copyright 2012-2020, Plotly, Inc.
54591* All rights reserved.
54592*
54593* This source code is licensed under the MIT license found in the
54594* LICENSE file in the root directory of this source tree.
54595*/
54596
54597'use strict';
54598
54599module.exports = {
54600 mode: {
54601 valType: 'enumerated',
54602 dflt: 'afterall',
54603
54604 values: ['immediate', 'next', 'afterall'],
54605
54606 },
54607 direction: {
54608 valType: 'enumerated',
54609
54610 values: ['forward', 'reverse'],
54611 dflt: 'forward',
54612
54613 },
54614 fromcurrent: {
54615 valType: 'boolean',
54616 dflt: false,
54617
54618
54619 },
54620 frame: {
54621 duration: {
54622 valType: 'number',
54623
54624 min: 0,
54625 dflt: 500,
54626
54627 },
54628 redraw: {
54629 valType: 'boolean',
54630
54631 dflt: true,
54632
54633 },
54634 },
54635 transition: {
54636 duration: {
54637 valType: 'number',
54638
54639 min: 0,
54640 dflt: 500,
54641 editType: 'none',
54642
54643 },
54644 easing: {
54645 valType: 'enumerated',
54646 dflt: 'cubic-in-out',
54647 values: [
54648 'linear',
54649 'quad',
54650 'cubic',
54651 'sin',
54652 'exp',
54653 'circle',
54654 'elastic',
54655 'back',
54656 'bounce',
54657 'linear-in',
54658 'quad-in',
54659 'cubic-in',
54660 'sin-in',
54661 'exp-in',
54662 'circle-in',
54663 'elastic-in',
54664 'back-in',
54665 'bounce-in',
54666 'linear-out',
54667 'quad-out',
54668 'cubic-out',
54669 'sin-out',
54670 'exp-out',
54671 'circle-out',
54672 'elastic-out',
54673 'back-out',
54674 'bounce-out',
54675 'linear-in-out',
54676 'quad-in-out',
54677 'cubic-in-out',
54678 'sin-in-out',
54679 'exp-in-out',
54680 'circle-in-out',
54681 'elastic-in-out',
54682 'back-in-out',
54683 'bounce-in-out'
54684 ],
54685
54686 editType: 'none',
54687
54688 },
54689 ordering: {
54690 valType: 'enumerated',
54691 values: ['layout first', 'traces first'],
54692 dflt: 'layout first',
54693
54694 editType: 'none',
54695
54696 }
54697 }
54698};
54699
54700},{}],218:[function(_dereq_,module,exports){
54701/**
54702* Copyright 2012-2020, Plotly, Inc.
54703* All rights reserved.
54704*
54705* This source code is licensed under the MIT license found in the
54706* LICENSE file in the root directory of this source tree.
54707*/
54708
54709'use strict';
54710
54711var Lib = _dereq_('../lib');
54712var Template = _dereq_('../plot_api/plot_template');
54713
54714/** Convenience wrapper for making array container logic DRY and consistent
54715 *
54716 * @param {object} parentObjIn
54717 * user input object where the container in question is linked
54718 * (i.e. either a user trace object or the user layout object)
54719 *
54720 * @param {object} parentObjOut
54721 * full object where the coerced container will be linked
54722 * (i.e. either a full trace object or the full layout object)
54723 *
54724 * @param {object} opts
54725 * options object:
54726 * - name {string}
54727 * name of the key linking the container in question
54728 * - inclusionAttr {string}
54729 * name of the item attribute for inclusion/exclusion. Default is 'visible'.
54730 * Since inclusion is true, use eg 'enabled' instead of 'disabled'.
54731 * - handleItemDefaults {function}
54732 * defaults method to be called on each item in the array container in question
54733 *
54734 * Its arguments are:
54735 * - itemIn {object} item in user layout
54736 * - itemOut {object} item in full layout
54737 * - parentObj {object} (as in closure)
54738 * - opts {object} (as in closure)
54739 * N.B.
54740 *
54741 * - opts is passed to handleItemDefaults so it can also store
54742 * links to supplementary data (e.g. fullData for layout components)
54743 *
54744 */
54745module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut, opts) {
54746 var name = opts.name;
54747 var inclusionAttr = opts.inclusionAttr || 'visible';
54748
54749 var previousContOut = parentObjOut[name];
54750
54751 var contIn = Lib.isArrayOrTypedArray(parentObjIn[name]) ? parentObjIn[name] : [];
54752 var contOut = parentObjOut[name] = [];
54753 var templater = Template.arrayTemplater(parentObjOut, name, inclusionAttr);
54754 var i, itemOut;
54755
54756 for(i = 0; i < contIn.length; i++) {
54757 var itemIn = contIn[i];
54758
54759 if(!Lib.isPlainObject(itemIn)) {
54760 itemOut = templater.newItem({});
54761 itemOut[inclusionAttr] = false;
54762 } else {
54763 itemOut = templater.newItem(itemIn);
54764 }
54765
54766 itemOut._index = i;
54767
54768 if(itemOut[inclusionAttr] !== false) {
54769 opts.handleItemDefaults(itemIn, itemOut, parentObjOut, opts);
54770 }
54771
54772 contOut.push(itemOut);
54773 }
54774
54775 var defaultItems = templater.defaultItems();
54776 for(i = 0; i < defaultItems.length; i++) {
54777 itemOut = defaultItems[i];
54778 itemOut._index = contOut.length;
54779 opts.handleItemDefaults({}, itemOut, parentObjOut, opts, {});
54780 contOut.push(itemOut);
54781 }
54782
54783 // in case this array gets its defaults rebuilt independent of the whole layout,
54784 // relink the private keys just for this array.
54785 if(Lib.isArrayOrTypedArray(previousContOut)) {
54786 var len = Math.min(previousContOut.length, contOut.length);
54787 for(i = 0; i < len; i++) {
54788 Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
54789 }
54790 }
54791
54792 return contOut;
54793};
54794
54795},{"../lib":177,"../plot_api/plot_template":212}],219:[function(_dereq_,module,exports){
54796/**
54797* Copyright 2012-2020, Plotly, Inc.
54798* All rights reserved.
54799*
54800* This source code is licensed under the MIT license found in the
54801* LICENSE file in the root directory of this source tree.
54802*/
54803
54804'use strict';
54805
54806var fxAttrs = _dereq_('../components/fx/attributes');
54807
54808module.exports = {
54809 type: {
54810 valType: 'enumerated',
54811
54812 values: [], // listed dynamically
54813 dflt: 'scatter',
54814 editType: 'calc+clearAxisTypes',
54815 _noTemplating: true // we handle this at a higher level
54816 },
54817 visible: {
54818 valType: 'enumerated',
54819 values: [true, false, 'legendonly'],
54820
54821 dflt: true,
54822 editType: 'calc',
54823
54824 },
54825 showlegend: {
54826 valType: 'boolean',
54827
54828 dflt: true,
54829 editType: 'style',
54830
54831 },
54832 legendgroup: {
54833 valType: 'string',
54834
54835 dflt: '',
54836 editType: 'style',
54837
54838 },
54839 opacity: {
54840 valType: 'number',
54841
54842 min: 0,
54843 max: 1,
54844 dflt: 1,
54845 editType: 'style',
54846
54847 },
54848 name: {
54849 valType: 'string',
54850
54851 editType: 'style',
54852
54853 },
54854 uid: {
54855 valType: 'string',
54856
54857 editType: 'plot',
54858 anim: true,
54859
54860 },
54861 ids: {
54862 valType: 'data_array',
54863 editType: 'calc',
54864 anim: true,
54865
54866 },
54867 customdata: {
54868 valType: 'data_array',
54869 editType: 'calc',
54870
54871 },
54872 meta: {
54873 valType: 'any',
54874 arrayOk: true,
54875
54876 editType: 'plot',
54877
54878 },
54879
54880 // N.B. these cannot be 'data_array' as they do not have the same length as
54881 // other data arrays and arrayOk attributes in general
54882 //
54883 // Maybe add another valType:
54884 // https://github.com/plotly/plotly.js/issues/1894
54885 selectedpoints: {
54886 valType: 'any',
54887
54888 editType: 'calc',
54889
54890 },
54891
54892 hoverinfo: {
54893 valType: 'flaglist',
54894
54895 flags: ['x', 'y', 'z', 'text', 'name'],
54896 extras: ['all', 'none', 'skip'],
54897 arrayOk: true,
54898 dflt: 'all',
54899 editType: 'none',
54900
54901 },
54902 hoverlabel: fxAttrs.hoverlabel,
54903 stream: {
54904 token: {
54905 valType: 'string',
54906 noBlank: true,
54907 strict: true,
54908
54909 editType: 'calc',
54910
54911 },
54912 maxpoints: {
54913 valType: 'number',
54914 min: 0,
54915 max: 10000,
54916 dflt: 500,
54917
54918 editType: 'calc',
54919
54920 },
54921 editType: 'calc'
54922 },
54923 transforms: {
54924 _isLinkedToArray: 'transform',
54925 editType: 'calc',
54926
54927 },
54928 uirevision: {
54929 valType: 'any',
54930
54931 editType: 'none',
54932
54933 }
54934};
54935
54936},{"../components/fx/attributes":81}],220:[function(_dereq_,module,exports){
54937/**
54938* Copyright 2012-2020, Plotly, Inc.
54939* All rights reserved.
54940*
54941* This source code is licensed under the MIT license found in the
54942* LICENSE file in the root directory of this source tree.
54943*/
54944
54945'use strict';
54946
54947
54948module.exports = {
54949 xaxis: {
54950 valType: 'subplotid',
54951
54952 dflt: 'x',
54953 editType: 'calc+clearAxisTypes',
54954
54955 },
54956 yaxis: {
54957 valType: 'subplotid',
54958
54959 dflt: 'y',
54960 editType: 'calc+clearAxisTypes',
54961
54962 }
54963};
54964
54965},{}],221:[function(_dereq_,module,exports){
54966/**
54967* Copyright 2012-2020, Plotly, Inc.
54968* All rights reserved.
54969*
54970* This source code is licensed under the MIT license found in the
54971* LICENSE file in the root directory of this source tree.
54972*/
54973
54974'use strict';
54975
54976var isNumeric = _dereq_('fast-isnumeric');
54977
54978var Lib = _dereq_('../../lib');
54979var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE;
54980var Registry = _dereq_('../../registry');
54981
54982module.exports = {
54983 getAutoRange: getAutoRange,
54984 makePadFn: makePadFn,
54985 doAutoRange: doAutoRange,
54986 findExtremes: findExtremes,
54987 concatExtremes: concatExtremes
54988};
54989
54990/**
54991 * getAutoRange
54992 *
54993 * Collects all _extremes values corresponding to a given axis
54994 * and computes its auto range.
54995 *
54996 * Note that getAutoRange uses return values from findExtremes.
54997 *
54998 * @param {object} gd:
54999 * graph div object with filled-in fullData and fullLayout, in particular
55000 * with filled-in '_extremes' containers:
55001 * {
55002 * val: calcdata value,
55003 * pad: extra pixels beyond this value,
55004 * extrapad: bool, does this point want 5% extra padding
55005 * }
55006 * @param {object} ax:
55007 * full axis object, in particular with filled-in '_traceIndices'
55008 * and '_annIndices' / '_shapeIndices' if applicable
55009 * @return {array}
55010 * an array of [min, max]. These are calcdata for log and category axes
55011 * and data for linear and date axes.
55012 *
55013 * TODO: we want to change log to data as well, but it's hard to do this
55014 * maintaining backward compatibility. category will always have to use calcdata
55015 * though, because otherwise values between categories (or outside all categories)
55016 * would be impossible.
55017 */
55018function getAutoRange(gd, ax) {
55019 var i, j;
55020 var newRange = [];
55021
55022 var getPad = makePadFn(ax);
55023 var extremes = concatExtremes(gd, ax);
55024 var minArray = extremes.min;
55025 var maxArray = extremes.max;
55026
55027 if(minArray.length === 0 || maxArray.length === 0) {
55028 return Lib.simpleMap(ax.range, ax.r2l);
55029 }
55030
55031 var minmin = minArray[0].val;
55032 var maxmax = maxArray[0].val;
55033
55034 for(i = 1; i < minArray.length; i++) {
55035 if(minmin !== maxmax) break;
55036 minmin = Math.min(minmin, minArray[i].val);
55037 }
55038 for(i = 1; i < maxArray.length; i++) {
55039 if(minmin !== maxmax) break;
55040 maxmax = Math.max(maxmax, maxArray[i].val);
55041 }
55042
55043 var axReverse = false;
55044
55045 if(ax.range) {
55046 var rng = Lib.simpleMap(ax.range, ax.r2l);
55047 axReverse = rng[1] < rng[0];
55048 }
55049 // one-time setting to easily reverse the axis
55050 // when plotting from code
55051 if(ax.autorange === 'reversed') {
55052 axReverse = true;
55053 ax.autorange = true;
55054 }
55055
55056 var rangeMode = ax.rangemode;
55057 var toZero = rangeMode === 'tozero';
55058 var nonNegative = rangeMode === 'nonnegative';
55059 var axLen = ax._length;
55060 // don't allow padding to reduce the data to < 10% of the length
55061 var minSpan = axLen / 10;
55062
55063 // find axis rangebreaks in [v0,v1] and compute its length in value space
55064 var calcBreaksLength = function(v0, v1) {
55065 var lBreaks = 0;
55066 if(ax.rangebreaks) {
55067 var rangebreaksOut = ax.locateBreaks(v0, v1);
55068 for(var i = 0; i < rangebreaksOut.length; i++) {
55069 var brk = rangebreaksOut[i];
55070 lBreaks += brk.max - brk.min;
55071 }
55072 }
55073 return lBreaks;
55074 };
55075
55076 var mbest = 0;
55077 var minpt, maxpt, minbest, maxbest, dp, dv;
55078
55079 for(i = 0; i < minArray.length; i++) {
55080 minpt = minArray[i];
55081 for(j = 0; j < maxArray.length; j++) {
55082 maxpt = maxArray[j];
55083 dv = maxpt.val - minpt.val - calcBreaksLength(minpt.val, maxpt.val);
55084 if(dv > 0) {
55085 dp = axLen - getPad(minpt) - getPad(maxpt);
55086 if(dp > minSpan) {
55087 if(dv / dp > mbest) {
55088 minbest = minpt;
55089 maxbest = maxpt;
55090 mbest = dv / dp;
55091 }
55092 } else if(dv / axLen > mbest) {
55093 // in case of padding longer than the axis
55094 // at least include the unpadded data values.
55095 minbest = {val: minpt.val, pad: 0};
55096 maxbest = {val: maxpt.val, pad: 0};
55097 mbest = dv / axLen;
55098 }
55099 }
55100 }
55101 }
55102
55103 function getMaxPad(prev, pt) {
55104 return Math.max(prev, getPad(pt));
55105 }
55106
55107 if(minmin === maxmax) {
55108 var lower = minmin - 1;
55109 var upper = minmin + 1;
55110 if(toZero) {
55111 if(minmin === 0) {
55112 // The only value we have on this axis is 0, and we want to
55113 // autorange so zero is one end.
55114 // In principle this could be [0, 1] or [-1, 0] but usually
55115 // 'tozero' pins 0 to the low end, so follow that.
55116 newRange = [0, 1];
55117 } else {
55118 var maxPad = (minmin > 0 ? maxArray : minArray).reduce(getMaxPad, 0);
55119 // we're pushing a single value away from the edge due to its
55120 // padding, with the other end clamped at zero
55121 // 0.5 means don't push it farther than the center.
55122 var rangeEnd = minmin / (1 - Math.min(0.5, maxPad / axLen));
55123 newRange = minmin > 0 ? [0, rangeEnd] : [rangeEnd, 0];
55124 }
55125 } else if(nonNegative) {
55126 newRange = [Math.max(0, lower), Math.max(1, upper)];
55127 } else {
55128 newRange = [lower, upper];
55129 }
55130 } else {
55131 if(toZero) {
55132 if(minbest.val >= 0) {
55133 minbest = {val: 0, pad: 0};
55134 }
55135 if(maxbest.val <= 0) {
55136 maxbest = {val: 0, pad: 0};
55137 }
55138 } else if(nonNegative) {
55139 if(minbest.val - mbest * getPad(minbest) < 0) {
55140 minbest = {val: 0, pad: 0};
55141 }
55142 if(maxbest.val <= 0) {
55143 maxbest = {val: 1, pad: 0};
55144 }
55145 }
55146
55147 // in case it changed again...
55148 mbest = (maxbest.val - minbest.val - calcBreaksLength(minpt.val, maxpt.val)) /
55149 (axLen - getPad(minbest) - getPad(maxbest));
55150
55151 newRange = [
55152 minbest.val - mbest * getPad(minbest),
55153 maxbest.val + mbest * getPad(maxbest)
55154 ];
55155 }
55156
55157 // maintain reversal
55158 if(axReverse) newRange.reverse();
55159
55160 return Lib.simpleMap(newRange, ax.l2r || Number);
55161}
55162
55163/*
55164 * calculate the pixel padding for ax._min and ax._max entries with
55165 * optional extrapad as 5% of the total axis length
55166 */
55167function makePadFn(ax) {
55168 // 5% padding for points that specify extrapad: true
55169 var extrappad = ax._length / 20;
55170
55171 // domain-constrained axes: base extrappad on the unconstrained
55172 // domain so it's consistent as the domain changes
55173 if((ax.constrain === 'domain') && ax._inputDomain) {
55174 extrappad *= (ax._inputDomain[1] - ax._inputDomain[0]) /
55175 (ax.domain[1] - ax.domain[0]);
55176 }
55177
55178 return function getPad(pt) { return pt.pad + (pt.extrapad ? extrappad : 0); };
55179}
55180
55181function concatExtremes(gd, ax) {
55182 var axId = ax._id;
55183 var fullData = gd._fullData;
55184 var fullLayout = gd._fullLayout;
55185 var minArray = [];
55186 var maxArray = [];
55187 var i, j, d;
55188
55189 function _concat(cont, indices) {
55190 for(i = 0; i < indices.length; i++) {
55191 var item = cont[indices[i]];
55192 var extremes = (item._extremes || {})[axId];
55193 if(item.visible === true && extremes) {
55194 for(j = 0; j < extremes.min.length; j++) {
55195 d = extremes.min[j];
55196 collapseMinArray(minArray, d.val, d.pad, {extrapad: d.extrapad});
55197 }
55198 for(j = 0; j < extremes.max.length; j++) {
55199 d = extremes.max[j];
55200 collapseMaxArray(maxArray, d.val, d.pad, {extrapad: d.extrapad});
55201 }
55202 }
55203 }
55204 }
55205
55206 _concat(fullData, ax._traceIndices);
55207 _concat(fullLayout.annotations || [], ax._annIndices || []);
55208 _concat(fullLayout.shapes || [], ax._shapeIndices || []);
55209
55210 return {min: minArray, max: maxArray};
55211}
55212
55213function doAutoRange(gd, ax) {
55214 ax.setScale();
55215
55216 if(ax.autorange) {
55217 ax.range = getAutoRange(gd, ax);
55218
55219 ax._r = ax.range.slice();
55220 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
55221
55222 // doAutoRange will get called on fullLayout,
55223 // but we want to report its results back to layout
55224
55225 var axIn = ax._input;
55226
55227 // before we edit _input, store preGUI values
55228 var edits = {};
55229 edits[ax._attr + '.range'] = ax.range;
55230 edits[ax._attr + '.autorange'] = ax.autorange;
55231 Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, edits);
55232
55233 axIn.range = ax.range.slice();
55234 axIn.autorange = ax.autorange;
55235 }
55236
55237 var anchorAx = ax._anchorAxis;
55238
55239 if(anchorAx && anchorAx.rangeslider) {
55240 var axeRangeOpts = anchorAx.rangeslider[ax._name];
55241 if(axeRangeOpts) {
55242 if(axeRangeOpts.rangemode === 'auto') {
55243 axeRangeOpts.range = getAutoRange(gd, ax);
55244 }
55245 }
55246 anchorAx._input.rangeslider[ax._name] = Lib.extendFlat({}, axeRangeOpts);
55247 }
55248}
55249
55250/**
55251 * findExtremes
55252 *
55253 * Find min/max extremes of an array of coordinates on a given axis.
55254 *
55255 * Note that findExtremes is called during `calc`, when we don't yet know the axis
55256 * length; all the inputs should be based solely on the trace data, nothing
55257 * about the axis layout.
55258 *
55259 * Note that `ppad` and `vpad` as well as their asymmetric variants refer to
55260 * the before and after padding of the passed `data` array, not to the whole axis.
55261 *
55262 * @param {object} ax: full axis object
55263 * relies on
55264 * - ax.type
55265 * - ax._m (just its sign)
55266 * - ax.d2l
55267 * @param {array} data:
55268 * array of numbers (i.e. already run though ax.d2c)
55269 * @param {object} opts:
55270 * available keys are:
55271 * vpad: (number or number array) pad values (data value +-vpad)
55272 * ppad: (number or number array) pad pixels (pixel location +-ppad)
55273 * ppadplus, ppadminus, vpadplus, vpadminus:
55274 * separate padding for each side, overrides symmetric
55275 * padded: (boolean) add 5% padding to both ends
55276 * (unless one end is overridden by tozero)
55277 * tozero: (boolean) make sure to include zero if axis is linear,
55278 * and make it a tight bound if possible
55279 * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus)
55280 * is linearized (for log scale axes)
55281 *
55282 * @return {object}
55283 * - min {array of objects}
55284 * - max {array of objects}
55285 * each object item has fields:
55286 * - val {number}
55287 * - pad {number}
55288 * - extrappad {number}
55289 * - opts {object}: a ref to the passed "options" object
55290 */
55291function findExtremes(ax, data, opts) {
55292 if(!opts) opts = {};
55293 if(!ax._m) ax.setScale();
55294
55295 var minArray = [];
55296 var maxArray = [];
55297
55298 var len = data.length;
55299 var extrapad = opts.padded || false;
55300 var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-');
55301 var isLog = ax.type === 'log';
55302 var hasArrayOption = false;
55303 var vpadLinearized = opts.vpadLinearized || false;
55304 var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax;
55305
55306 function makePadAccessor(item) {
55307 if(Array.isArray(item)) {
55308 hasArrayOption = true;
55309 return function(i) { return Math.max(Number(item[i]||0), 0); };
55310 } else {
55311 var v = Math.max(Number(item||0), 0);
55312 return function() { return v; };
55313 }
55314 }
55315
55316 var ppadplus = makePadAccessor((ax._m > 0 ?
55317 opts.ppadplus : opts.ppadminus) || opts.ppad || 0);
55318 var ppadminus = makePadAccessor((ax._m > 0 ?
55319 opts.ppadminus : opts.ppadplus) || opts.ppad || 0);
55320 var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad);
55321 var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad);
55322
55323 if(!hasArrayOption) {
55324 // with no arrays other than `data` we don't need to consider
55325 // every point, only the extreme data points
55326 vmin = Infinity;
55327 vmax = -Infinity;
55328
55329 if(isLog) {
55330 for(i = 0; i < len; i++) {
55331 v = data[i];
55332 // data is not linearized yet so we still have to filter out negative logs
55333 if(v < vmin && v > 0) vmin = v;
55334 if(v > vmax && v < FP_SAFE) vmax = v;
55335 }
55336 } else {
55337 for(i = 0; i < len; i++) {
55338 v = data[i];
55339 if(v < vmin && v > -FP_SAFE) vmin = v;
55340 if(v > vmax && v < FP_SAFE) vmax = v;
55341 }
55342 }
55343
55344 data = [vmin, vmax];
55345 len = 2;
55346 }
55347
55348 var collapseOpts = {tozero: tozero, extrapad: extrapad};
55349
55350 function addItem(i) {
55351 di = data[i];
55352 if(!isNumeric(di)) return;
55353 ppadiplus = ppadplus(i);
55354 ppadiminus = ppadminus(i);
55355
55356 if(vpadLinearized) {
55357 dmin = ax.c2l(di) - vpadminus(i);
55358 dmax = ax.c2l(di) + vpadplus(i);
55359 } else {
55360 vmin = di - vpadminus(i);
55361 vmax = di + vpadplus(i);
55362 // special case for log axes: if vpad makes this object span
55363 // more than an order of mag, clip it to one order. This is so
55364 // we don't have non-positive errors or absurdly large lower
55365 // range due to rounding errors
55366 if(isLog && vmin < vmax / 10) vmin = vmax / 10;
55367
55368 dmin = ax.c2l(vmin);
55369 dmax = ax.c2l(vmax);
55370 }
55371
55372 if(tozero) {
55373 dmin = Math.min(0, dmin);
55374 dmax = Math.max(0, dmax);
55375 }
55376 if(goodNumber(dmin)) {
55377 collapseMinArray(minArray, dmin, ppadiminus, collapseOpts);
55378 }
55379 if(goodNumber(dmax)) {
55380 collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts);
55381 }
55382 }
55383
55384 // For efficiency covering monotonic or near-monotonic data,
55385 // check a few points at both ends first and then sweep
55386 // through the middle
55387 var iMax = Math.min(6, len);
55388 for(i = 0; i < iMax; i++) addItem(i);
55389 for(i = len - 1; i >= iMax; i--) addItem(i);
55390
55391 return {
55392 min: minArray,
55393 max: maxArray,
55394 opts: opts
55395 };
55396}
55397
55398function collapseMinArray(array, newVal, newPad, opts) {
55399 collapseArray(array, newVal, newPad, opts, lessOrEqual);
55400}
55401
55402function collapseMaxArray(array, newVal, newPad, opts) {
55403 collapseArray(array, newVal, newPad, opts, greaterOrEqual);
55404}
55405
55406/**
55407 * collapseArray
55408 *
55409 * Takes items from 'array' and compares them to 'newVal', 'newPad'.
55410 *
55411 * @param {array} array:
55412 * current set of min or max extremes
55413 * @param {number} newVal:
55414 * new value to compare against
55415 * @param {number} newPad:
55416 * pad value associated with 'newVal'
55417 * @param {object} opts:
55418 * - tozero {boolean}
55419 * - extrapad {number}
55420 * @param {function} atLeastAsExtreme:
55421 * comparison function, use
55422 * - lessOrEqual for min 'array' and
55423 * - greaterOrEqual for max 'array'
55424 *
55425 * In practice, 'array' is either
55426 * - 'extremes[ax._id].min' or
55427 * - 'extremes[ax._id].max
55428 * found in traces and layout items that affect autorange.
55429 *
55430 * Since we don't yet know the relationship between pixels and values
55431 * (that's what we're trying to figure out!) AND we don't yet know how
55432 * many pixels `extrapad` represents (it's going to be 5% of the length,
55433 * but we don't want to have to redo calc just because length changed)
55434 * two point must satisfy three criteria simultaneously for one to supersede the other:
55435 * - at least as extreme a `val`
55436 * - at least as big a `pad`
55437 * - an unpadded point cannot supersede a padded point, but any other combination can
55438 *
55439 * Then:
55440 * - If the item supersedes the new point, set includeThis false
55441 * - If the new pt supersedes the item, delete it from 'array'
55442 */
55443function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) {
55444 var tozero = opts.tozero;
55445 var extrapad = opts.extrapad;
55446 var includeThis = true;
55447
55448 for(var j = 0; j < array.length && includeThis; j++) {
55449 var v = array[j];
55450 if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) {
55451 includeThis = false;
55452 break;
55453 } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) {
55454 array.splice(j, 1);
55455 j--;
55456 }
55457 }
55458 if(includeThis) {
55459 var clipAtZero = (tozero && newVal === 0);
55460 array.push({
55461 val: newVal,
55462 pad: clipAtZero ? 0 : newPad,
55463 extrapad: clipAtZero ? false : extrapad
55464 });
55465 }
55466}
55467
55468// In order to stop overflow errors, don't consider points
55469// too close to the limits of js floating point
55470function goodNumber(v) {
55471 return isNumeric(v) && Math.abs(v) < FP_SAFE;
55472}
55473
55474function lessOrEqual(v0, v1) { return v0 <= v1; }
55475function greaterOrEqual(v0, v1) { return v0 >= v1; }
55476
55477},{"../../constants/numerical":155,"../../lib":177,"../../registry":272,"fast-isnumeric":15}],222:[function(_dereq_,module,exports){
55478/**
55479* Copyright 2012-2020, Plotly, Inc.
55480* All rights reserved.
55481*
55482* This source code is licensed under the MIT license found in the
55483* LICENSE file in the root directory of this source tree.
55484*/
55485
55486'use strict';
55487
55488var d3 = _dereq_('d3');
55489var isNumeric = _dereq_('fast-isnumeric');
55490var Plots = _dereq_('../../plots/plots');
55491
55492var Registry = _dereq_('../../registry');
55493var Lib = _dereq_('../../lib');
55494var svgTextUtils = _dereq_('../../lib/svg_text_utils');
55495var Titles = _dereq_('../../components/titles');
55496var Color = _dereq_('../../components/color');
55497var Drawing = _dereq_('../../components/drawing');
55498
55499var axAttrs = _dereq_('./layout_attributes');
55500var cleanTicks = _dereq_('./clean_ticks');
55501
55502var constants = _dereq_('../../constants/numerical');
55503var ONEAVGYEAR = constants.ONEAVGYEAR;
55504var ONEAVGMONTH = constants.ONEAVGMONTH;
55505var ONEDAY = constants.ONEDAY;
55506var ONEHOUR = constants.ONEHOUR;
55507var ONEMIN = constants.ONEMIN;
55508var ONESEC = constants.ONESEC;
55509var MINUS_SIGN = constants.MINUS_SIGN;
55510var BADNUM = constants.BADNUM;
55511
55512var alignmentConstants = _dereq_('../../constants/alignment');
55513var MID_SHIFT = alignmentConstants.MID_SHIFT;
55514var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
55515var LINE_SPACING = alignmentConstants.LINE_SPACING;
55516var OPPOSITE_SIDE = alignmentConstants.OPPOSITE_SIDE;
55517
55518var axes = module.exports = {};
55519
55520axes.setConvert = _dereq_('./set_convert');
55521var autoType = _dereq_('./axis_autotype');
55522
55523var axisIds = _dereq_('./axis_ids');
55524axes.id2name = axisIds.id2name;
55525axes.name2id = axisIds.name2id;
55526axes.cleanId = axisIds.cleanId;
55527axes.list = axisIds.list;
55528axes.listIds = axisIds.listIds;
55529axes.getFromId = axisIds.getFromId;
55530axes.getFromTrace = axisIds.getFromTrace;
55531
55532var autorange = _dereq_('./autorange');
55533axes.getAutoRange = autorange.getAutoRange;
55534axes.findExtremes = autorange.findExtremes;
55535
55536var epsilon = 0.0001;
55537function expandRange(range) {
55538 var delta = (range[1] - range[0]) * epsilon;
55539 return [
55540 range[0] - delta,
55541 range[1] + delta
55542 ];
55543}
55544
55545/*
55546 * find the list of possible axes to reference with an xref or yref attribute
55547 * and coerce it to that list
55548 *
55549 * attr: the attribute we're generating a reference for. Should end in 'x' or 'y'
55550 * but can be prefixed, like 'ax' for annotation's arrow x
55551 * dflt: the default to coerce to, or blank to use the first axis (falling back on
55552 * extraOption if there is no axis)
55553 * extraOption: aside from existing axes with this letter, what non-axis value is allowed?
55554 * Only required if it's different from `dflt`
55555 */
55556axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
55557 var axLetter = attr.charAt(attr.length - 1);
55558 var axlist = gd._fullLayout._subplots[axLetter + 'axis'];
55559 var refAttr = attr + 'ref';
55560 var attrDef = {};
55561
55562 if(!dflt) dflt = axlist[0] || extraOption;
55563 if(!extraOption) extraOption = dflt;
55564
55565 // data-ref annotations are not supported in gl2d yet
55566
55567 attrDef[refAttr] = {
55568 valType: 'enumerated',
55569 values: axlist.concat(extraOption ? [extraOption] : []),
55570 dflt: dflt
55571 };
55572
55573 // xref, yref
55574 return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
55575};
55576
55577/*
55578 * coerce position attributes (range-type) that can be either on axes or absolute
55579 * (paper or pixel) referenced. The biggest complication here is that we don't know
55580 * before looking at the axis whether the value must be a number or not (it may be
55581 * a date string), so we can't use the regular valType='number' machinery
55582 *
55583 * axRef (string): the axis this position is referenced to, or:
55584 * paper: fraction of the plot area
55585 * pixel: pixels relative to some starting position
55586 * attr (string): the attribute in containerOut we are coercing
55587 * dflt (number): the default position, as a fraction or pixels. If the attribute
55588 * is to be axis-referenced, this will be converted to an axis data value
55589 *
55590 * Also cleans the values, since the attribute definition itself has to say
55591 * valType: 'any' to handle date axes. This allows us to accept:
55592 * - for category axes: category names, and convert them here into serial numbers.
55593 * Note that this will NOT work for axis range endpoints, because we don't know
55594 * the category list yet (it's set by ax.makeCalcdata during calc)
55595 * but it works for component (note, shape, images) positions.
55596 * - for date axes: JS Dates or milliseconds, and convert to date strings
55597 * - for other types: coerce them to numbers
55598 */
55599axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) {
55600 var cleanPos, pos;
55601
55602 if(axRef === 'paper' || axRef === 'pixel') {
55603 cleanPos = Lib.ensureNumber;
55604 pos = coerce(attr, dflt);
55605 } else {
55606 var ax = axes.getFromId(gd, axRef);
55607 dflt = ax.fraction2r(dflt);
55608 pos = coerce(attr, dflt);
55609 cleanPos = ax.cleanPos;
55610 }
55611
55612 containerOut[attr] = cleanPos(pos);
55613};
55614
55615axes.cleanPosition = function(pos, gd, axRef) {
55616 var cleanPos = (axRef === 'paper' || axRef === 'pixel') ?
55617 Lib.ensureNumber :
55618 axes.getFromId(gd, axRef).cleanPos;
55619
55620 return cleanPos(pos);
55621};
55622
55623axes.redrawComponents = function(gd, axIds) {
55624 axIds = axIds ? axIds : axes.listIds(gd);
55625
55626 var fullLayout = gd._fullLayout;
55627
55628 function _redrawOneComp(moduleName, methodName, stashName, shortCircuit) {
55629 var method = Registry.getComponentMethod(moduleName, methodName);
55630 var stash = {};
55631
55632 for(var i = 0; i < axIds.length; i++) {
55633 var ax = fullLayout[axes.id2name(axIds[i])];
55634 var indices = ax[stashName];
55635
55636 for(var j = 0; j < indices.length; j++) {
55637 var ind = indices[j];
55638
55639 if(!stash[ind]) {
55640 method(gd, ind);
55641 stash[ind] = 1;
55642 // once is enough for images (which doesn't use the `i` arg anyway)
55643 if(shortCircuit) return;
55644 }
55645 }
55646 }
55647 }
55648
55649 // annotations and shapes 'draw' method is slow,
55650 // use the finer-grained 'drawOne' method instead
55651 _redrawOneComp('annotations', 'drawOne', '_annIndices');
55652 _redrawOneComp('shapes', 'drawOne', '_shapeIndices');
55653 _redrawOneComp('images', 'draw', '_imgIndices', true);
55654};
55655
55656var getDataConversions = axes.getDataConversions = function(gd, trace, target, targetArray) {
55657 var ax;
55658
55659 // If target points to an axis, use the type we already have for that
55660 // axis to find the data type. Otherwise use the values to autotype.
55661 var d2cTarget = (target === 'x' || target === 'y' || target === 'z') ?
55662 target :
55663 targetArray;
55664
55665 // In the case of an array target, make a mock data array
55666 // and call supplyDefaults to the data type and
55667 // setup the data-to-calc method.
55668 if(Array.isArray(d2cTarget)) {
55669 ax = {
55670 type: autoType(targetArray),
55671 _categories: []
55672 };
55673 axes.setConvert(ax);
55674
55675 // build up ax._categories (usually done during ax.makeCalcdata()
55676 if(ax.type === 'category') {
55677 for(var i = 0; i < targetArray.length; i++) {
55678 ax.d2c(targetArray[i]);
55679 }
55680 }
55681 // TODO what to do for transforms?
55682 } else {
55683 ax = axes.getFromTrace(gd, trace, d2cTarget);
55684 }
55685
55686 // if 'target' has corresponding axis
55687 // -> use setConvert method
55688 if(ax) return {d2c: ax.d2c, c2d: ax.c2d};
55689
55690 // special case for 'ids'
55691 // -> cast to String
55692 if(d2cTarget === 'ids') return {d2c: toString, c2d: toString};
55693
55694 // otherwise (e.g. numeric-array of 'marker.color' or 'marker.size')
55695 // -> cast to Number
55696
55697 return {d2c: toNum, c2d: toNum};
55698};
55699
55700function toNum(v) { return +v; }
55701function toString(v) { return String(v); }
55702
55703axes.getDataToCoordFunc = function(gd, trace, target, targetArray) {
55704 return getDataConversions(gd, trace, target, targetArray).d2c;
55705};
55706
55707// get counteraxis letter for this axis (name or id)
55708// this can also be used as the id for default counter axis
55709axes.counterLetter = function(id) {
55710 var axLetter = id.charAt(0);
55711 if(axLetter === 'x') return 'y';
55712 if(axLetter === 'y') return 'x';
55713};
55714
55715// incorporate a new minimum difference and first tick into
55716// forced
55717// note that _forceTick0 is linearized, so needs to be turned into
55718// a range value for setting tick0
55719axes.minDtick = function(ax, newDiff, newFirst, allow) {
55720 // doesn't make sense to do forced min dTick on log or category axes,
55721 // and the plot itself may decide to cancel (ie non-grouped bars)
55722 if(['log', 'category', 'multicategory'].indexOf(ax.type) !== -1 || !allow) {
55723 ax._minDtick = 0;
55724 } else if(ax._minDtick === undefined) {
55725 // undefined means there's nothing there yet
55726
55727 ax._minDtick = newDiff;
55728 ax._forceTick0 = newFirst;
55729 } else if(ax._minDtick) {
55730 if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 &&
55731 // existing minDtick is an integer multiple of newDiff
55732 // (within rounding err)
55733 // and forceTick0 can be shifted to newFirst
55734
55735 (((newFirst - ax._forceTick0) / newDiff % 1) +
55736 1.000001) % 1 < 2e-6) {
55737 ax._minDtick = newDiff;
55738 ax._forceTick0 = newFirst;
55739 } else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 ||
55740 // if the converse is true (newDiff is a multiple of minDtick and
55741 // newFirst can be shifted to forceTick0) then do nothing - same
55742 // forcing stands. Otherwise, cancel forced minimum
55743
55744 (((newFirst - ax._forceTick0) / ax._minDtick % 1) +
55745 1.000001) % 1 > 2e-6) {
55746 ax._minDtick = 0;
55747 }
55748 }
55749};
55750
55751// save a copy of the initial axis ranges in fullLayout
55752// use them in mode bar and dblclick events
55753axes.saveRangeInitial = function(gd, overwrite) {
55754 var axList = axes.list(gd, '', true);
55755 var hasOneAxisChanged = false;
55756
55757 for(var i = 0; i < axList.length; i++) {
55758 var ax = axList[i];
55759 var isNew = (ax._rangeInitial === undefined);
55760 var hasChanged = isNew || !(
55761 ax.range[0] === ax._rangeInitial[0] &&
55762 ax.range[1] === ax._rangeInitial[1]
55763 );
55764
55765 if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
55766 ax._rangeInitial = ax.range.slice();
55767 hasOneAxisChanged = true;
55768 }
55769 }
55770
55771 return hasOneAxisChanged;
55772};
55773
55774// save a copy of the initial spike visibility
55775axes.saveShowSpikeInitial = function(gd, overwrite) {
55776 var axList = axes.list(gd, '', true);
55777 var hasOneAxisChanged = false;
55778 var allSpikesEnabled = 'on';
55779
55780 for(var i = 0; i < axList.length; i++) {
55781 var ax = axList[i];
55782 var isNew = (ax._showSpikeInitial === undefined);
55783 var hasChanged = isNew || !(ax.showspikes === ax._showspikes);
55784
55785 if(isNew || (overwrite && hasChanged)) {
55786 ax._showSpikeInitial = ax.showspikes;
55787 hasOneAxisChanged = true;
55788 }
55789
55790 if(allSpikesEnabled === 'on' && !ax.showspikes) {
55791 allSpikesEnabled = 'off';
55792 }
55793 }
55794 gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
55795 return hasOneAxisChanged;
55796};
55797
55798axes.autoBin = function(data, ax, nbins, is2d, calendar, size) {
55799 var dataMin = Lib.aggNums(Math.min, null, data);
55800 var dataMax = Lib.aggNums(Math.max, null, data);
55801
55802 if(ax.type === 'category' || ax.type === 'multicategory') {
55803 return {
55804 start: dataMin - 0.5,
55805 end: dataMax + 0.5,
55806 size: Math.max(1, Math.round(size) || 1),
55807 _dataSpan: dataMax - dataMin,
55808 };
55809 }
55810
55811 if(!calendar) calendar = ax.calendar;
55812
55813 // piggyback off tick code to make "nice" bin sizes and edges
55814 var dummyAx;
55815 if(ax.type === 'log') {
55816 dummyAx = {
55817 type: 'linear',
55818 range: [dataMin, dataMax]
55819 };
55820 } else {
55821 dummyAx = {
55822 type: ax.type,
55823 range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
55824 calendar: calendar
55825 };
55826 }
55827 axes.setConvert(dummyAx);
55828
55829 size = size && cleanTicks.dtick(size, dummyAx.type);
55830
55831 if(size) {
55832 dummyAx.dtick = size;
55833 dummyAx.tick0 = cleanTicks.tick0(undefined, dummyAx.type, calendar);
55834 } else {
55835 var size0;
55836 if(nbins) size0 = ((dataMax - dataMin) / nbins);
55837 else {
55838 // totally auto: scale off std deviation so the highest bin is
55839 // somewhat taller than the total number of bins, but don't let
55840 // the size get smaller than the 'nice' rounded down minimum
55841 // difference between values
55842 var distinctData = Lib.distinctVals(data);
55843 var msexp = Math.pow(10, Math.floor(
55844 Math.log(distinctData.minDiff) / Math.LN10));
55845 var minSize = msexp * Lib.roundUp(
55846 distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
55847 size0 = Math.max(minSize, 2 * Lib.stdev(data) /
55848 Math.pow(data.length, is2d ? 0.25 : 0.4));
55849
55850 // fallback if ax.d2c output BADNUMs
55851 // e.g. when user try to plot categorical bins
55852 // on a layout.xaxis.type: 'linear'
55853 if(!isNumeric(size0)) size0 = 1;
55854 }
55855
55856 axes.autoTicks(dummyAx, size0);
55857 }
55858
55859 var finalSize = dummyAx.dtick;
55860 var binStart = axes.tickIncrement(
55861 axes.tickFirst(dummyAx), finalSize, 'reverse', calendar);
55862 var binEnd, bincount;
55863
55864 // check for too many data points right at the edges of bins
55865 // (>50% within 1% of bin edges) or all data points integral
55866 // and offset the bins accordingly
55867 if(typeof finalSize === 'number') {
55868 binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
55869
55870 bincount = 1 + Math.floor((dataMax - binStart) / finalSize);
55871 binEnd = binStart + bincount * finalSize;
55872 } else {
55873 // month ticks - should be the only nonlinear kind we have at this point.
55874 // dtick (as supplied by axes.autoTick) only has nonlinear values on
55875 // date and log axes, but even if you display a histogram on a log axis
55876 // we bin it on a linear axis (which one could argue against, but that's
55877 // a separate issue)
55878 if(dummyAx.dtick.charAt(0) === 'M') {
55879 binStart = autoShiftMonthBins(binStart, data, finalSize, dataMin, calendar);
55880 }
55881
55882 // calculate the endpoint for nonlinear ticks - you have to
55883 // just increment until you're done
55884 binEnd = binStart;
55885 bincount = 0;
55886 while(binEnd <= dataMax) {
55887 binEnd = axes.tickIncrement(binEnd, finalSize, false, calendar);
55888 bincount++;
55889 }
55890 }
55891
55892 return {
55893 start: ax.c2r(binStart, 0, calendar),
55894 end: ax.c2r(binEnd, 0, calendar),
55895 size: finalSize,
55896 _dataSpan: dataMax - dataMin
55897 };
55898};
55899
55900
55901function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) {
55902 var edgecount = 0;
55903 var midcount = 0;
55904 var intcount = 0;
55905 var blankCount = 0;
55906
55907 function nearEdge(v) {
55908 // is a value within 1% of a bin edge?
55909 return (1 + (v - binStart) * 100 / ax.dtick) % 100 < 2;
55910 }
55911
55912 for(var i = 0; i < data.length; i++) {
55913 if(data[i] % 1 === 0) intcount++;
55914 else if(!isNumeric(data[i])) blankCount++;
55915
55916 if(nearEdge(data[i])) edgecount++;
55917 if(nearEdge(data[i] + ax.dtick / 2)) midcount++;
55918 }
55919 var dataCount = data.length - blankCount;
55920
55921 if(intcount === dataCount && ax.type !== 'date') {
55922 if(ax.dtick < 1) {
55923 // all integers: if bin size is <1, it's because
55924 // that was specifically requested (large nbins)
55925 // so respect that... but center the bins containing
55926 // integers on those integers
55927
55928 binStart = dataMin - 0.5 * ax.dtick;
55929 } else {
55930 // otherwise start half an integer down regardless of
55931 // the bin size, just enough to clear up endpoint
55932 // ambiguity about which integers are in which bins.
55933
55934 binStart -= 0.5;
55935 if(binStart + ax.dtick < dataMin) binStart += ax.dtick;
55936 }
55937 } else if(midcount < dataCount * 0.1) {
55938 if(edgecount > dataCount * 0.3 ||
55939 nearEdge(dataMin) || nearEdge(dataMax)) {
55940 // lots of points at the edge, not many in the middle
55941 // shift half a bin
55942 var binshift = ax.dtick / 2;
55943 binStart += (binStart + binshift < dataMin) ? binshift : -binshift;
55944 }
55945 }
55946 return binStart;
55947}
55948
55949
55950function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
55951 var stats = Lib.findExactDates(data, calendar);
55952 // number of data points that needs to be an exact value
55953 // to shift that increment to (near) the bin center
55954 var threshold = 0.8;
55955
55956 if(stats.exactDays > threshold) {
55957 var numMonths = Number(dtick.substr(1));
55958
55959 if((stats.exactYears > threshold) && (numMonths % 12 === 0)) {
55960 // The exact middle of a non-leap-year is 1.5 days into July
55961 // so if we start the bins here, all but leap years will
55962 // get hover-labeled as exact years.
55963 binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5;
55964 } else if(stats.exactMonths > threshold) {
55965 // Months are not as clean, but if we shift half the *longest*
55966 // month (31/2 days) then 31-day months will get labeled exactly
55967 // and shorter months will get labeled with the correct month
55968 // but shifted 12-36 hours into it.
55969 binStart = axes.tickIncrement(binStart, 'M1', 'reverse') + ONEDAY * 15.5;
55970 } else {
55971 // Shifting half a day is exact, but since these are month bins it
55972 // will always give a somewhat odd-looking label, until we do something
55973 // smarter like showing the bin boundaries (or the bounds of the actual
55974 // data in each bin)
55975 binStart -= ONEDAY / 2;
55976 }
55977 var nextBinStart = axes.tickIncrement(binStart, dtick);
55978
55979 if(nextBinStart <= dataMin) return nextBinStart;
55980 }
55981 return binStart;
55982}
55983
55984// ----------------------------------------------------
55985// Ticks and grids
55986// ----------------------------------------------------
55987
55988// ensure we have tick0, dtick, and tick rounding calculated
55989axes.prepTicks = function(ax, opts) {
55990 var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);
55991
55992 // calculate max number of (auto) ticks to display based on plot size
55993 if(ax.tickmode === 'auto' || !ax.dtick) {
55994 var nt = ax.nticks;
55995 var minPx;
55996
55997 if(!nt) {
55998 if(ax.type === 'category' || ax.type === 'multicategory') {
55999 minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15;
56000 nt = ax._length / minPx;
56001 } else {
56002 minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
56003 nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
56004 }
56005
56006 // radial axes span half their domain,
56007 // multiply nticks value by two to get correct number of auto ticks.
56008 if(ax._name === 'radialaxis') nt *= 2;
56009 }
56010
56011 // add a couple of extra digits for filling in ticks when we
56012 // have explicit tickvals without tick text
56013 if(ax.tickmode === 'array') nt *= 100;
56014
56015
56016 ax._roughDTick = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / nt;
56017 axes.autoTicks(ax, ax._roughDTick);
56018
56019 // check for a forced minimum dtick
56020 if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) {
56021 ax.dtick = ax._minDtick;
56022 ax.tick0 = ax.l2r(ax._forceTick0);
56023 }
56024 }
56025
56026 // check for missing tick0
56027 if(!ax.tick0) {
56028 ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0;
56029 }
56030
56031 // ensure we don't try to make ticks below our minimum precision
56032 // see https://github.com/plotly/plotly.js/issues/2892
56033 if(ax.type === 'date' && ax.dtick < 0.1) ax.dtick = 0.1;
56034
56035 // now figure out rounding of tick values
56036 autoTickRound(ax);
56037};
56038
56039// calculate the ticks: text, values, positioning
56040// if ticks are set to automatic, determine the right values (tick0,dtick)
56041// in any case, set tickround to # of digits to round tick labels to,
56042// or codes to this effect for log and date scales
56043axes.calcTicks = function calcTicks(ax, opts) {
56044 axes.prepTicks(ax, opts);
56045 var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);
56046
56047 // now that we've figured out the auto values for formatting
56048 // in case we're missing some ticktext, we can break out for array ticks
56049 if(ax.tickmode === 'array') return arrayTicks(ax);
56050
56051 // find the first tick
56052 ax._tmin = axes.tickFirst(ax, opts);
56053
56054 // add a tiny bit so we get ticks which may have rounded out
56055 var exRng = expandRange(rng);
56056 var startTick = exRng[0];
56057 var endTick = exRng[1];
56058 // check for reversed axis
56059 var axrev = (rng[1] < rng[0]);
56060
56061 // No visible ticks? Quit.
56062 // I've only seen this on category axes with all categories off the edge.
56063 if((ax._tmin < startTick) !== axrev) return [];
56064
56065 // return the full set of tick vals
56066 if(ax.type === 'category' || ax.type === 'multicategory') {
56067 endTick = (axrev) ? Math.max(-0.5, endTick) :
56068 Math.min(ax._categories.length - 0.5, endTick);
56069 }
56070
56071 var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L');
56072
56073 var tickVals;
56074 function generateTicks() {
56075 var xPrevious = null;
56076 var maxTicks = Math.max(1000, ax._length || 0);
56077 tickVals = [];
56078 for(var x = ax._tmin;
56079 (axrev) ? (x >= endTick) : (x <= endTick);
56080 x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) {
56081 // prevent infinite loops - no more than one tick per pixel,
56082 // and make sure each value is different from the previous
56083 if(tickVals.length > maxTicks || x === xPrevious) break;
56084 xPrevious = x;
56085
56086 var minor = false;
56087 if(isDLog && (x !== (x | 0))) {
56088 minor = true;
56089 }
56090
56091 tickVals.push({
56092 minor: minor,
56093 value: x
56094 });
56095 }
56096 }
56097
56098 generateTicks();
56099
56100 if(ax.rangebreaks) {
56101 // replace ticks inside breaks that would get a tick
56102 // and reduce ticks
56103 var len = tickVals.length;
56104 if(len) {
56105 var tf = 0;
56106 if(ax.tickmode === 'auto') {
56107 tf =
56108 (ax._id.charAt(0) === 'y' ? 2 : 6) *
56109 (ax.tickfont ? ax.tickfont.size : 12);
56110 }
56111
56112 var newTickVals = [];
56113 var prevPos;
56114
56115 var dir = axrev ? 1 : -1;
56116 var first = axrev ? 0 : len - 1;
56117 var last = axrev ? len - 1 : 0;
56118 for(var q = first; dir * q <= dir * last; q += dir) {
56119 var tickVal = tickVals[q];
56120 if(ax.maskBreaks(tickVal.value) === BADNUM) {
56121 tickVal.value = moveOutsideBreak(tickVal.value, ax);
56122
56123 if(ax._rl && (
56124 ax._rl[0] === tickVal.value ||
56125 ax._rl[1] === tickVal.value
56126 )) continue;
56127 }
56128
56129 var pos = ax.c2p(tickVal.value);
56130
56131 if(pos === prevPos) {
56132 if(newTickVals[newTickVals.length - 1].value < tickVal.value) {
56133 newTickVals[newTickVals.length - 1] = tickVal;
56134 }
56135 } else if(prevPos === undefined || Math.abs(pos - prevPos) > tf) {
56136 prevPos = pos;
56137 newTickVals.push(tickVal);
56138 }
56139 }
56140 tickVals = newTickVals.reverse();
56141 }
56142 }
56143
56144 // If same angle over a full circle, the last tick vals is a duplicate.
56145 // TODO must do something similar for angular date axes.
56146 if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
56147 tickVals.pop();
56148 }
56149
56150 // save the last tick as well as first, so we can
56151 // show the exponent only on the last one
56152 ax._tmax = (tickVals[tickVals.length - 1] || {}).value;
56153
56154 // for showing the rest of a date when the main tick label is only the
56155 // latter part: ax._prevDateHead holds what we showed most recently.
56156 // Start with it cleared and mark that we're in calcTicks (ie calculating a
56157 // whole string of these so we should care what the previous date head was!)
56158 ax._prevDateHead = '';
56159 ax._inCalcTicks = true;
56160
56161 var ticksOut = new Array(tickVals.length);
56162 for(var i = 0; i < tickVals.length; i++) {
56163 var _minor = tickVals[i].minor;
56164 var _value = tickVals[i].value;
56165
56166 ticksOut[i] = axes.tickText(
56167 ax,
56168 _value,
56169 false, // hover
56170 _minor // noSuffixPrefix
56171 );
56172 }
56173
56174 ax._inCalcTicks = false;
56175
56176 return ticksOut;
56177};
56178
56179function arrayTicks(ax) {
56180 var vals = ax.tickvals;
56181 var text = ax.ticktext;
56182 var ticksOut = new Array(vals.length);
56183 var rng = Lib.simpleMap(ax.range, ax.r2l);
56184 var exRng = expandRange(rng);
56185 var tickMin = Math.min(exRng[0], exRng[1]);
56186 var tickMax = Math.max(exRng[0], exRng[1]);
56187 var j = 0;
56188
56189 // without a text array, just format the given values as any other ticks
56190 // except with more precision to the numbers
56191 if(!Array.isArray(text)) text = [];
56192
56193 // make sure showing ticks doesn't accidentally add new categories
56194 // TODO multicategory, if we allow ticktext / tickvals
56195 var tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l;
56196
56197 // array ticks on log axes always show the full number
56198 // (if no explicit ticktext overrides it)
56199 if(ax.type === 'log' && String(ax.dtick).charAt(0) !== 'L') {
56200 ax.dtick = 'L' + Math.pow(10, Math.floor(Math.min(ax.range[0], ax.range[1])) - 1);
56201 }
56202
56203 for(var i = 0; i < vals.length; i++) {
56204 var vali = tickVal2l(vals[i]);
56205 if(vali > tickMin && vali < tickMax) {
56206 if(text[i] === undefined) ticksOut[j] = axes.tickText(ax, vali);
56207 else ticksOut[j] = tickTextObj(ax, vali, String(text[i]));
56208 j++;
56209 }
56210 }
56211
56212 if(j < vals.length) ticksOut.splice(j, vals.length - j);
56213
56214 if(ax.rangebreaks) {
56215 // remove ticks falling inside rangebreaks
56216 ticksOut = ticksOut.filter(function(d) {
56217 return ax.maskBreaks(d.x) !== BADNUM;
56218 });
56219 }
56220
56221 return ticksOut;
56222}
56223
56224var roundBase10 = [2, 5, 10];
56225var roundBase24 = [1, 2, 3, 6, 12];
56226var roundBase60 = [1, 2, 5, 10, 15, 30];
56227// 2&3 day ticks are weird, but need something btwn 1&7
56228var roundDays = [1, 2, 3, 7, 14];
56229// approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2)
56230// these don't have to be exact, just close enough to round to the right value
56231var roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1];
56232var roundLog2 = [-0.301, 0, 0.301, 0.699, 1];
56233// N.B. `thetaunit; 'radians' angular axes must be converted to degrees
56234var roundAngles = [15, 30, 45, 90, 180];
56235
56236function roundDTick(roughDTick, base, roundingSet) {
56237 return base * Lib.roundUp(roughDTick / base, roundingSet);
56238}
56239
56240// autoTicks: calculate best guess at pleasant ticks for this axis
56241// inputs:
56242// ax - an axis object
56243// roughDTick - rough tick spacing (to be turned into a nice round number)
56244// outputs (into ax):
56245// tick0: starting point for ticks (not necessarily on the graph)
56246// usually 0 for numeric (=10^0=1 for log) or jan 1, 2000 for dates
56247// dtick: the actual, nice round tick spacing, usually a little larger than roughDTick
56248// if the ticks are spaced linearly (linear scale, categories,
56249// log with only full powers, date ticks < month),
56250// this will just be a number
56251// months: M#
56252// years: M# where # is 12*number of years
56253// log with linear ticks: L# where # is the linear tick spacing
56254// log showing powers plus some intermediates:
56255// D1 shows all digits, D2 shows 2 and 5
56256axes.autoTicks = function(ax, roughDTick) {
56257 var base;
56258
56259 function getBase(v) {
56260 return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10));
56261 }
56262
56263 if(ax.type === 'date') {
56264 ax.tick0 = Lib.dateTick0(ax.calendar);
56265 // the criteria below are all based on the rough spacing we calculate
56266 // being > half of the final unit - so precalculate twice the rough val
56267 var roughX2 = 2 * roughDTick;
56268
56269 if(roughX2 > ONEAVGYEAR) {
56270 roughDTick /= ONEAVGYEAR;
56271 base = getBase(10);
56272 ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10));
56273 } else if(roughX2 > ONEAVGMONTH) {
56274 roughDTick /= ONEAVGMONTH;
56275 ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24);
56276 } else if(roughX2 > ONEDAY) {
56277 ax.dtick = roundDTick(roughDTick, ONEDAY, ax._hasDayOfWeekBreaks ? [1, 2, 7, 14] : roundDays);
56278 // get week ticks on sunday
56279 // this will also move the base tick off 2000-01-01 if dtick is
56280 // 2 or 3 days... but that's a weird enough case that we'll ignore it.
56281 ax.tick0 = Lib.dateTick0(ax.calendar, true);
56282 } else if(roughX2 > ONEHOUR) {
56283 ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24);
56284 } else if(roughX2 > ONEMIN) {
56285 ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60);
56286 } else if(roughX2 > ONESEC) {
56287 ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60);
56288 } else {
56289 // milliseconds
56290 base = getBase(10);
56291 ax.dtick = roundDTick(roughDTick, base, roundBase10);
56292 }
56293 } else if(ax.type === 'log') {
56294 ax.tick0 = 0;
56295 var rng = Lib.simpleMap(ax.range, ax.r2l);
56296
56297 if(roughDTick > 0.7) {
56298 // only show powers of 10
56299 ax.dtick = Math.ceil(roughDTick);
56300 } else if(Math.abs(rng[1] - rng[0]) < 1) {
56301 // span is less than one power of 10
56302 var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick);
56303
56304 // ticks on a linear scale, labeled fully
56305 roughDTick = Math.abs(Math.pow(10, rng[1]) -
56306 Math.pow(10, rng[0])) / nt;
56307 base = getBase(10);
56308 ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
56309 } else {
56310 // include intermediates between powers of 10,
56311 // labeled with small digits
56312 // ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
56313 ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
56314 }
56315 } else if(ax.type === 'category' || ax.type === 'multicategory') {
56316 ax.tick0 = 0;
56317 ax.dtick = Math.ceil(Math.max(roughDTick, 1));
56318 } else if(isAngular(ax)) {
56319 ax.tick0 = 0;
56320 base = 1;
56321 ax.dtick = roundDTick(roughDTick, base, roundAngles);
56322 } else {
56323 // auto ticks always start at 0
56324 ax.tick0 = 0;
56325 base = getBase(10);
56326 ax.dtick = roundDTick(roughDTick, base, roundBase10);
56327 }
56328
56329 // prevent infinite loops
56330 if(ax.dtick === 0) ax.dtick = 1;
56331
56332 // TODO: this is from log axis histograms with autorange off
56333 if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') {
56334 var olddtick = ax.dtick;
56335 ax.dtick = 1;
56336 throw 'ax.dtick error: ' + String(olddtick);
56337 }
56338};
56339
56340// after dtick is already known, find tickround = precision
56341// to display in tick labels
56342// for numeric ticks, integer # digits after . to round to
56343// for date ticks, the last date part to show (y,m,d,H,M,S)
56344// or an integer # digits past seconds
56345function autoTickRound(ax) {
56346 var dtick = ax.dtick;
56347
56348 ax._tickexponent = 0;
56349 if(!isNumeric(dtick) && typeof dtick !== 'string') {
56350 dtick = 1;
56351 }
56352
56353 if(ax.type === 'category' || ax.type === 'multicategory') {
56354 ax._tickround = null;
56355 }
56356 if(ax.type === 'date') {
56357 // If tick0 is unusual, give tickround a bit more information
56358 // not necessarily *all* the information in tick0 though, if it's really odd
56359 // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
56360 // take off a leading minus (year < 0) and i (intercalary month) so length is consistent
56361 var tick0ms = ax.r2l(ax.tick0);
56362 var tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, '');
56363 var tick0len = tick0str.length;
56364
56365 if(String(dtick).charAt(0) === 'M') {
56366 // any tick0 more specific than a year: alway show the full date
56367 if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd';
56368 // show the month unless ticks are full multiples of a year
56369 else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm';
56370 } else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd';
56371 else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M';
56372 else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S';
56373 else {
56374 // tickround is a number of digits of fractional seconds
56375 // of any two adjacent ticks, at least one will have the maximum fractional digits
56376 // of all possible ticks - so take the max. length of tick0 and the next one
56377 var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length;
56378 ax._tickround = Math.max(tick0len, tick1len) - 20;
56379
56380 // We shouldn't get here... but in case there's a situation I'm
56381 // not thinking of where tick0str and tick1str are identical or
56382 // something, fall back on maximum precision
56383 if(ax._tickround < 0) ax._tickround = 4;
56384 }
56385 } else if(isNumeric(dtick) || dtick.charAt(0) === 'L') {
56386 // linear or log (except D1, D2)
56387 var rng = ax.range.map(ax.r2d || Number);
56388 if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
56389 // 2 digits past largest digit of dtick
56390 ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
56391
56392 var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
56393 var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
56394 if(Math.abs(rangeexp) > 3) {
56395 if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
56396 ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
56397 } else ax._tickexponent = rangeexp;
56398 }
56399 } else {
56400 // D1 or D2 (log)
56401 ax._tickround = null;
56402 }
56403}
56404
56405// months and years don't have constant millisecond values
56406// (but a year is always 12 months so we only need months)
56407// log-scale ticks are also not consistently spaced, except
56408// for pure powers of 10
56409// numeric ticks always have constant differences, other datetime ticks
56410// can all be calculated as constant number of milliseconds
56411axes.tickIncrement = function(x, dtick, axrev, calendar) {
56412 var axSign = axrev ? -1 : 1;
56413
56414 // includes linear, all dates smaller than month, and pure 10^n in log
56415 if(isNumeric(dtick)) return x + axSign * dtick;
56416
56417 // everything else is a string, one character plus a number
56418 var tType = dtick.charAt(0);
56419 var dtSigned = axSign * Number(dtick.substr(1));
56420
56421 // Dates: months (or years - see Lib.incrementMonth)
56422 if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
56423
56424 // Log scales: Linear, Digits
56425 if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
56426
56427 // log10 of 2,5,10, or all digits (logs just have to be
56428 // close enough to round)
56429 if(tType === 'D') {
56430 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
56431 var x2 = x + axSign * 0.01;
56432 var frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
56433
56434 return Math.floor(x2) +
56435 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
56436 }
56437
56438 throw 'unrecognized dtick ' + String(dtick);
56439};
56440
56441// calculate the first tick on an axis
56442axes.tickFirst = function(ax, opts) {
56443 var r2l = ax.r2l || Number;
56444 var rng = Lib.simpleMap(ax.range, r2l, undefined, undefined, opts);
56445 var axrev = rng[1] < rng[0];
56446 var sRound = axrev ? Math.floor : Math.ceil;
56447 // add a tiny extra bit to make sure we get ticks
56448 // that may have been rounded out
56449 var r0 = expandRange(rng)[0];
56450 var dtick = ax.dtick;
56451 var tick0 = r2l(ax.tick0);
56452
56453 if(isNumeric(dtick)) {
56454 var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0;
56455
56456 // make sure no ticks outside the category list
56457 if(ax.type === 'category' || ax.type === 'multicategory') {
56458 tmin = Lib.constrain(tmin, 0, ax._categories.length - 1);
56459 }
56460 return tmin;
56461 }
56462
56463 var tType = dtick.charAt(0);
56464 var dtNum = Number(dtick.substr(1));
56465
56466 // Dates: months (or years)
56467 if(tType === 'M') {
56468 var cnt = 0;
56469 var t0 = tick0;
56470 var t1, mult, newDTick;
56471
56472 // This algorithm should work for *any* nonlinear (but close to linear!)
56473 // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3.
56474 while(cnt < 10) {
56475 t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar);
56476 if((t1 - r0) * (t0 - r0) <= 0) {
56477 // t1 and t0 are on opposite sides of r0! we've succeeded!
56478 if(axrev) return Math.min(t0, t1);
56479 return Math.max(t0, t1);
56480 }
56481 mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0);
56482 newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum);
56483 t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar);
56484 cnt++;
56485 }
56486 Lib.error('tickFirst did not converge', ax);
56487 return t0;
56488 } else if(tType === 'L') {
56489 // Log scales: Linear, Digits
56490
56491 return Math.log(sRound(
56492 (Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10;
56493 } else if(tType === 'D') {
56494 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
56495 var frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev);
56496
56497 return Math.floor(r0) +
56498 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
56499 } else throw 'unrecognized dtick ' + String(dtick);
56500};
56501
56502// draw the text for one tick.
56503// px,py are the location on gd.paper
56504// prefix is there so the x axis ticks can be dropped a line
56505// ax is the axis layout, x is the tick value
56506// hover is a (truthy) flag for whether to show numbers with a bit
56507// more precision for hovertext
56508axes.tickText = function(ax, x, hover, noSuffixPrefix) {
56509 var out = tickTextObj(ax, x);
56510 var arrayMode = ax.tickmode === 'array';
56511 var extraPrecision = hover || arrayMode;
56512 var axType = ax.type;
56513 // TODO multicategory, if we allow ticktext / tickvals
56514 var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l;
56515 var i;
56516
56517 if(arrayMode && Array.isArray(ax.ticktext)) {
56518 var rng = Lib.simpleMap(ax.range, ax.r2l);
56519 var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000;
56520
56521 for(i = 0; i < ax.ticktext.length; i++) {
56522 if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
56523 }
56524 if(i < ax.ticktext.length) {
56525 out.text = String(ax.ticktext[i]);
56526 return out;
56527 }
56528 }
56529
56530 function isHidden(showAttr) {
56531 if(showAttr === undefined) return true;
56532 if(hover) return showAttr === 'none';
56533
56534 var firstOrLast = {
56535 first: ax._tmin,
56536 last: ax._tmax
56537 }[showAttr];
56538
56539 return showAttr !== 'all' && x !== firstOrLast;
56540 }
56541
56542 var hideexp = hover ?
56543 'never' :
56544 ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : '';
56545
56546 if(axType === 'date') formatDate(ax, out, hover, extraPrecision);
56547 else if(axType === 'log') formatLog(ax, out, hover, extraPrecision, hideexp);
56548 else if(axType === 'category') formatCategory(ax, out);
56549 else if(axType === 'multicategory') formatMultiCategory(ax, out, hover);
56550 else if(isAngular(ax)) formatAngle(ax, out, hover, extraPrecision, hideexp);
56551 else formatLinear(ax, out, hover, extraPrecision, hideexp);
56552
56553 // add prefix and suffix
56554 if(!noSuffixPrefix) {
56555 if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text;
56556 if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix;
56557 }
56558
56559 // Setup ticks and grid lines boundaries
56560 // at 1/2 a 'category' to the left/bottom
56561 if(ax.tickson === 'boundaries' || ax.showdividers) {
56562 var inbounds = function(v) {
56563 var p = ax.l2p(v);
56564 return p >= 0 && p <= ax._length ? v : null;
56565 };
56566
56567 out.xbnd = [
56568 inbounds(out.x - 0.5),
56569 inbounds(out.x + ax.dtick - 0.5)
56570 ];
56571 }
56572
56573 return out;
56574};
56575
56576/**
56577 * create text for a hover label on this axis, with special handling of
56578 * log axes (where negative values can't be displayed but can appear in hover text)
56579 *
56580 * @param {object} ax: the axis to format text for
56581 * @param {number} val: calcdata value to format
56582 * @param {Optional(number)} val2: a second value to display
56583 *
56584 * @returns {string} `val` formatted as a string appropriate to this axis, or
56585 * `val` and `val2` as a range (ie '<val> - <val2>') if `val2` is provided and
56586 * it's different from `val`.
56587 */
56588axes.hoverLabelText = function(ax, val, val2) {
56589 if(val2 !== BADNUM && val2 !== val) {
56590 return axes.hoverLabelText(ax, val) + ' - ' + axes.hoverLabelText(ax, val2);
56591 }
56592
56593 var logOffScale = (ax.type === 'log' && val <= 0);
56594 var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text;
56595
56596 if(logOffScale) {
56597 return val === 0 ? '0' : MINUS_SIGN + tx;
56598 }
56599
56600 // TODO: should we do something special if the axis calendar and
56601 // the data calendar are different? Somehow display both dates with
56602 // their system names? Right now it will just display in the axis calendar
56603 // but users could add the other one as text.
56604 return tx;
56605};
56606
56607function tickTextObj(ax, x, text) {
56608 var tf = ax.tickfont || {};
56609
56610 return {
56611 x: x,
56612 dx: 0,
56613 dy: 0,
56614 text: text || '',
56615 fontSize: tf.size,
56616 font: tf.family,
56617 fontColor: tf.color
56618 };
56619}
56620
56621function formatDate(ax, out, hover, extraPrecision) {
56622 var tr = ax._tickround;
56623 var fmt = (hover && ax.hoverformat) || axes.getTickFormat(ax);
56624
56625 if(extraPrecision) {
56626 // second or sub-second precision: extra always shows max digits.
56627 // for other fields, extra precision just adds one field.
56628 if(isNumeric(tr)) tr = 4;
56629 else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr];
56630 }
56631
56632 var dateStr = Lib.formatDate(out.x, fmt, tr, ax._dateFormat, ax.calendar, ax._extraFormat);
56633 var headStr;
56634
56635 var splitIndex = dateStr.indexOf('\n');
56636 if(splitIndex !== -1) {
56637 headStr = dateStr.substr(splitIndex + 1);
56638 dateStr = dateStr.substr(0, splitIndex);
56639 }
56640
56641 if(extraPrecision) {
56642 // if extraPrecision led to trailing zeros, strip them off
56643 // actually, this can lead to removing even more zeros than
56644 // in the original rounding, but that's fine because in these
56645 // contexts uniformity is not so important (if there's even
56646 // anything to be uniform with!)
56647
56648 // can we remove the whole time part?
56649 if(dateStr === '00:00:00' || dateStr === '00:00') {
56650 dateStr = headStr;
56651 headStr = '';
56652 } else if(dateStr.length === 8) {
56653 // strip off seconds if they're zero (zero fractional seconds
56654 // are already omitted)
56655 // but we never remove minutes and leave just hours
56656 dateStr = dateStr.replace(/:00$/, '');
56657 }
56658 }
56659
56660 if(headStr) {
56661 if(hover) {
56662 // hover puts it all on one line, so headPart works best up front
56663 // except for year headPart: turn this into "Jan 1, 2000" etc.
56664 if(tr === 'd') dateStr += ', ' + headStr;
56665 else dateStr = headStr + (dateStr ? ', ' + dateStr : '');
56666 } else if(!ax._inCalcTicks || (headStr !== ax._prevDateHead)) {
56667 dateStr += '<br>' + headStr;
56668 ax._prevDateHead = headStr;
56669 }
56670 }
56671
56672 out.text = dateStr;
56673}
56674
56675function formatLog(ax, out, hover, extraPrecision, hideexp) {
56676 var dtick = ax.dtick;
56677 var x = out.x;
56678 var tickformat = ax.tickformat;
56679 var dtChar0 = typeof dtick === 'string' && dtick.charAt(0);
56680
56681 if(hideexp === 'never') {
56682 // If this is a hover label, then we must *never* hide the exponent
56683 // for the sake of display, which could give the wrong value by
56684 // potentially many orders of magnitude. If hideexp was 'never', then
56685 // it's now succeeded by preventing the other condition from automating
56686 // this choice. Thus we can unset it so that the axis formatting takes
56687 // precedence.
56688 hideexp = '';
56689 }
56690
56691 if(extraPrecision && (dtChar0 !== 'L')) {
56692 dtick = 'L3';
56693 dtChar0 = 'L';
56694 }
56695
56696 if(tickformat || (dtChar0 === 'L')) {
56697 out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
56698 } else if(isNumeric(dtick) || ((dtChar0 === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
56699 var p = Math.round(x);
56700 var absP = Math.abs(p);
56701 var exponentFormat = ax.exponentformat;
56702 if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
56703 if(p === 0) out.text = 1;
56704 else if(p === 1) out.text = '10';
56705 else out.text = '10<sup>' + (p > 1 ? '' : MINUS_SIGN) + absP + '</sup>';
56706
56707 out.fontSize *= 1.25;
56708 } else if((exponentFormat === 'e' || exponentFormat === 'E') && absP > 2) {
56709 out.text = '1' + exponentFormat + (p > 0 ? '+' : MINUS_SIGN) + absP;
56710 } else {
56711 out.text = numFormat(Math.pow(10, x), ax, '', 'fakehover');
56712 if(dtick === 'D1' && ax._id.charAt(0) === 'y') {
56713 out.dy -= out.fontSize / 6;
56714 }
56715 }
56716 } else if(dtChar0 === 'D') {
56717 out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1))));
56718 out.fontSize *= 0.75;
56719 } else throw 'unrecognized dtick ' + String(dtick);
56720
56721 // if 9's are printed on log scale, move the 10's away a bit
56722 if(ax.dtick === 'D1') {
56723 var firstChar = String(out.text).charAt(0);
56724 if(firstChar === '0' || firstChar === '1') {
56725 if(ax._id.charAt(0) === 'y') {
56726 out.dx -= out.fontSize / 4;
56727 } else {
56728 out.dy += out.fontSize / 2;
56729 out.dx += (ax.range[1] > ax.range[0] ? 1 : -1) *
56730 out.fontSize * (x < 0 ? 0.5 : 0.25);
56731 }
56732 }
56733 }
56734}
56735
56736function formatCategory(ax, out) {
56737 var tt = ax._categories[Math.round(out.x)];
56738 if(tt === undefined) tt = '';
56739 out.text = String(tt);
56740}
56741
56742function formatMultiCategory(ax, out, hover) {
56743 var v = Math.round(out.x);
56744 var cats = ax._categories[v] || [];
56745 var tt = cats[1] === undefined ? '' : String(cats[1]);
56746 var tt2 = cats[0] === undefined ? '' : String(cats[0]);
56747
56748 if(hover) {
56749 // TODO is this what we want?
56750 out.text = tt2 + ' - ' + tt;
56751 } else {
56752 // setup for secondary labels
56753 out.text = tt;
56754 out.text2 = tt2;
56755 }
56756}
56757
56758function formatLinear(ax, out, hover, extraPrecision, hideexp) {
56759 if(hideexp === 'never') {
56760 // If this is a hover label, then we must *never* hide the exponent
56761 // for the sake of display, which could give the wrong value by
56762 // potentially many orders of magnitude. If hideexp was 'never', then
56763 // it's now succeeded by preventing the other condition from automating
56764 // this choice. Thus we can unset it so that the axis formatting takes
56765 // precedence.
56766 hideexp = '';
56767 } else if(ax.showexponent === 'all' && Math.abs(out.x / ax.dtick) < 1e-6) {
56768 // don't add an exponent to zero if we're showing all exponents
56769 // so the only reason you'd show an exponent on zero is if it's the
56770 // ONLY tick to get an exponent (first or last)
56771 hideexp = 'hide';
56772 }
56773 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
56774}
56775
56776function formatAngle(ax, out, hover, extraPrecision, hideexp) {
56777 if(ax.thetaunit === 'radians' && !hover) {
56778 var num = out.x / 180;
56779
56780 if(num === 0) {
56781 out.text = '0';
56782 } else {
56783 var frac = num2frac(num);
56784
56785 if(frac[1] >= 100) {
56786 out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision);
56787 } else {
56788 var isNeg = out.x < 0;
56789
56790 if(frac[1] === 1) {
56791 if(frac[0] === 1) out.text = 'π';
56792 else out.text = frac[0] + 'π';
56793 } else {
56794 out.text = [
56795 '<sup>', frac[0], '</sup>',
56796 '⁄',
56797 '<sub>', frac[1], '</sub>',
56798 'π'
56799 ].join('');
56800 }
56801
56802 if(isNeg) out.text = MINUS_SIGN + out.text;
56803 }
56804 }
56805 } else {
56806 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
56807 }
56808}
56809
56810// inspired by
56811// https://github.com/yisibl/num2fraction/blob/master/index.js
56812function num2frac(num) {
56813 function almostEq(a, b) {
56814 return Math.abs(a - b) <= 1e-6;
56815 }
56816
56817 function findGCD(a, b) {
56818 return almostEq(b, 0) ? a : findGCD(b, a % b);
56819 }
56820
56821 function findPrecision(n) {
56822 var e = 1;
56823 while(!almostEq(Math.round(n * e) / e, n)) {
56824 e *= 10;
56825 }
56826 return e;
56827 }
56828
56829 var precision = findPrecision(num);
56830 var number = num * precision;
56831 var gcd = Math.abs(findGCD(number, precision));
56832
56833 return [
56834 // numerator
56835 Math.round(number / gcd),
56836 // denominator
56837 Math.round(precision / gcd)
56838 ];
56839}
56840
56841// format a number (tick value) according to the axis settings
56842// new, more reliable procedure than d3.round or similar:
56843// add half the rounding increment, then stringify and truncate
56844// also automatically switch to sci. notation
56845var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
56846
56847function isSIFormat(exponentFormat) {
56848 return exponentFormat === 'SI' || exponentFormat === 'B';
56849}
56850
56851// are we beyond the range of common SI prefixes?
56852// 10^-16 -> 1x10^-16
56853// 10^-15 -> 1f
56854// ...
56855// 10^14 -> 100T
56856// 10^15 -> 1x10^15
56857// 10^16 -> 1x10^16
56858function beyondSI(exponent) {
56859 return exponent > 14 || exponent < -15;
56860}
56861
56862function numFormat(v, ax, fmtoverride, hover) {
56863 var isNeg = v < 0;
56864 // max number of digits past decimal point to show
56865 var tickRound = ax._tickround;
56866 var exponentFormat = fmtoverride || ax.exponentformat || 'B';
56867 var exponent = ax._tickexponent;
56868 var tickformat = axes.getTickFormat(ax);
56869 var separatethousands = ax.separatethousands;
56870
56871 // special case for hover: set exponent just for this value, and
56872 // add a couple more digits of precision over tick labels
56873 if(hover) {
56874 // make a dummy axis obj to get the auto rounding and exponent
56875 var ah = {
56876 exponentformat: exponentFormat,
56877 dtick: ax.showexponent === 'none' ? ax.dtick :
56878 (isNumeric(v) ? Math.abs(v) || 1 : 1),
56879 // if not showing any exponents, don't change the exponent
56880 // from what we calculate
56881 range: ax.showexponent === 'none' ? ax.range.map(ax.r2d) : [0, v || 1]
56882 };
56883 autoTickRound(ah);
56884 tickRound = (Number(ah._tickround) || 0) + 4;
56885 exponent = ah._tickexponent;
56886 if(ax.hoverformat) tickformat = ax.hoverformat;
56887 }
56888
56889 if(tickformat) return ax._numFormat(tickformat)(v).replace(/-/g, MINUS_SIGN);
56890
56891 // 'epsilon' - rounding increment
56892 var e = Math.pow(10, -tickRound) / 2;
56893
56894 // exponentFormat codes:
56895 // 'e' (1.2e+6, default)
56896 // 'E' (1.2E+6)
56897 // 'SI' (1.2M)
56898 // 'B' (same as SI except 10^9=B not G)
56899 // 'none' (1200000)
56900 // 'power' (1.2x10^6)
56901 // 'hide' (1.2, use 3rd argument=='hide' to eg
56902 // only show exponent on last tick)
56903 if(exponentFormat === 'none') exponent = 0;
56904
56905 // take the sign out, put it back manually at the end
56906 // - makes cases easier
56907 v = Math.abs(v);
56908 if(v < e) {
56909 // 0 is just 0, but may get exponent if it's the last tick
56910 v = '0';
56911 isNeg = false;
56912 } else {
56913 v += e;
56914 // take out a common exponent, if any
56915 if(exponent) {
56916 v *= Math.pow(10, -exponent);
56917 tickRound += exponent;
56918 }
56919 // round the mantissa
56920 if(tickRound === 0) v = String(Math.floor(v));
56921 else if(tickRound < 0) {
56922 v = String(Math.round(v));
56923 v = v.substr(0, v.length + tickRound);
56924 for(var i = tickRound; i < 0; i++) v += '0';
56925 } else {
56926 v = String(v);
56927 var dp = v.indexOf('.') + 1;
56928 if(dp) v = v.substr(0, dp + tickRound).replace(/\.?0+$/, '');
56929 }
56930 // insert appropriate decimal point and thousands separator
56931 v = Lib.numSeparate(v, ax._separators, separatethousands);
56932 }
56933
56934 // add exponent
56935 if(exponent && exponentFormat !== 'hide') {
56936 if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
56937
56938 var signedExponent;
56939 if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
56940 else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
56941 else signedExponent = String(exponent);
56942
56943 if(exponentFormat === 'e' || exponentFormat === 'E') {
56944 v += exponentFormat + signedExponent;
56945 } else if(exponentFormat === 'power') {
56946 v += '×10<sup>' + signedExponent + '</sup>';
56947 } else if(exponentFormat === 'B' && exponent === 9) {
56948 v += 'B';
56949 } else if(isSIFormat(exponentFormat)) {
56950 v += SIPREFIXES[exponent / 3 + 5];
56951 }
56952 }
56953
56954 // put sign back in and return
56955 // replace standard minus character (which is technically a hyphen)
56956 // with a true minus sign
56957 if(isNeg) return MINUS_SIGN + v;
56958 return v;
56959}
56960
56961axes.getTickFormat = function(ax) {
56962 var i;
56963
56964 function convertToMs(dtick) {
56965 return typeof dtick !== 'string' ? dtick : Number(dtick.replace('M', '')) * ONEAVGMONTH;
56966 }
56967
56968 function compareLogTicks(left, right) {
56969 var priority = ['L', 'D'];
56970 if(typeof left === typeof right) {
56971 if(typeof left === 'number') {
56972 return left - right;
56973 } else {
56974 var leftPriority = priority.indexOf(left.charAt(0));
56975 var rightPriority = priority.indexOf(right.charAt(0));
56976 if(leftPriority === rightPriority) {
56977 return Number(left.replace(/(L|D)/g, '')) - Number(right.replace(/(L|D)/g, ''));
56978 } else {
56979 return leftPriority - rightPriority;
56980 }
56981 }
56982 } else {
56983 return typeof left === 'number' ? 1 : -1;
56984 }
56985 }
56986
56987 function isProperStop(dtick, range, convert) {
56988 var convertFn = convert || function(x) { return x;};
56989 var leftDtick = range[0];
56990 var rightDtick = range[1];
56991 return ((!leftDtick && typeof leftDtick !== 'number') || convertFn(leftDtick) <= convertFn(dtick)) &&
56992 ((!rightDtick && typeof rightDtick !== 'number') || convertFn(rightDtick) >= convertFn(dtick));
56993 }
56994
56995 function isProperLogStop(dtick, range) {
56996 var isLeftDtickNull = range[0] === null;
56997 var isRightDtickNull = range[1] === null;
56998 var isDtickInRangeLeft = compareLogTicks(dtick, range[0]) >= 0;
56999 var isDtickInRangeRight = compareLogTicks(dtick, range[1]) <= 0;
57000 return (isLeftDtickNull || isDtickInRangeLeft) && (isRightDtickNull || isDtickInRangeRight);
57001 }
57002
57003 var tickstop, stopi;
57004 if(ax.tickformatstops && ax.tickformatstops.length > 0) {
57005 switch(ax.type) {
57006 case 'date':
57007 case 'linear': {
57008 for(i = 0; i < ax.tickformatstops.length; i++) {
57009 stopi = ax.tickformatstops[i];
57010 if(stopi.enabled && isProperStop(ax.dtick, stopi.dtickrange, convertToMs)) {
57011 tickstop = stopi;
57012 break;
57013 }
57014 }
57015 break;
57016 }
57017 case 'log': {
57018 for(i = 0; i < ax.tickformatstops.length; i++) {
57019 stopi = ax.tickformatstops[i];
57020 if(stopi.enabled && isProperLogStop(ax.dtick, stopi.dtickrange)) {
57021 tickstop = stopi;
57022 break;
57023 }
57024 }
57025 break;
57026 }
57027 default:
57028 }
57029 }
57030 return tickstop ? tickstop.value : ax.tickformat;
57031};
57032
57033// getSubplots - extract all subplot IDs we need
57034// as an array of items like 'xy', 'x2y', 'x2y2'...
57035// sorted by x (x,x2,x3...) then y
57036// optionally restrict to only subplots containing axis object ax
57037//
57038// NOTE: this is currently only used OUTSIDE plotly.js (toolpanel, webapp)
57039// ideally we get rid of it there (or just copy this there) and remove it here
57040axes.getSubplots = function(gd, ax) {
57041 var subplotObj = gd._fullLayout._subplots;
57042 var allSubplots = subplotObj.cartesian.concat(subplotObj.gl2d || []);
57043
57044 var out = ax ? axes.findSubplotsWithAxis(allSubplots, ax) : allSubplots;
57045
57046 out.sort(function(a, b) {
57047 var aParts = a.substr(1).split('y');
57048 var bParts = b.substr(1).split('y');
57049
57050 if(aParts[0] === bParts[0]) return +aParts[1] - +bParts[1];
57051 return +aParts[0] - +bParts[0];
57052 });
57053
57054 return out;
57055};
57056
57057// find all subplots with axis 'ax'
57058// NOTE: this is only used in axes.getSubplots (only used outside plotly.js) and
57059// gl2d/convert (where it restricts axis subplots to only those with gl2d)
57060axes.findSubplotsWithAxis = function(subplots, ax) {
57061 var axMatch = new RegExp(
57062 (ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
57063 );
57064 var subplotsWithAx = [];
57065
57066 for(var i = 0; i < subplots.length; i++) {
57067 var sp = subplots[i];
57068 if(axMatch.test(sp)) subplotsWithAx.push(sp);
57069 }
57070
57071 return subplotsWithAx;
57072};
57073
57074// makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings
57075axes.makeClipPaths = function(gd) {
57076 var fullLayout = gd._fullLayout;
57077
57078 // for more info: https://github.com/plotly/plotly.js/issues/2595
57079 if(fullLayout._hasOnlyLargeSploms) return;
57080
57081 var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''};
57082 var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''};
57083 var xaList = axes.list(gd, 'x', true);
57084 var yaList = axes.list(gd, 'y', true);
57085 var clipList = [];
57086 var i, j;
57087
57088 for(i = 0; i < xaList.length; i++) {
57089 clipList.push({x: xaList[i], y: fullHeight});
57090 for(j = 0; j < yaList.length; j++) {
57091 if(i === 0) clipList.push({x: fullWidth, y: yaList[j]});
57092 clipList.push({x: xaList[i], y: yaList[j]});
57093 }
57094 }
57095
57096 // selectors don't work right with camelCase tags,
57097 // have to use class instead
57098 // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
57099 var axClips = fullLayout._clips.selectAll('.axesclip')
57100 .data(clipList, function(d) { return d.x._id + d.y._id; });
57101
57102 axClips.enter().append('clipPath')
57103 .classed('axesclip', true)
57104 .attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; })
57105 .append('rect');
57106
57107 axClips.exit().remove();
57108
57109 axClips.each(function(d) {
57110 d3.select(this).select('rect').attr({
57111 x: d.x._offset || 0,
57112 y: d.y._offset || 0,
57113 width: d.x._length || 1,
57114 height: d.y._length || 1
57115 });
57116 });
57117};
57118
57119/**
57120 * Main multi-axis drawing routine!
57121 *
57122 * @param {DOM element} gd : graph div
57123 * @param {string or array of strings} arg : polymorphic argument
57124 * @param {object} opts:
57125 * - @param {boolean} skipTitle : optional flag to skip axis title draw/update
57126 *
57127 * Signature 1: Axes.draw(gd, 'redraw')
57128 * use this to clear and redraw all axes on graph
57129 *
57130 * Signature 2: Axes.draw(gd, '')
57131 * use this to draw all axes on graph w/o the selectAll().remove()
57132 * of the 'redraw' signature
57133 *
57134 * Signature 3: Axes.draw(gd, [axId, axId2, ...])
57135 * where the items are axis id string,
57136 * use this to update multiple axes in one call
57137 *
57138 * N.B draw updates:
57139 * - ax._r (stored range for use by zoom/pan)
57140 * - ax._rl (stored linearized range for use by zoom/pan)
57141 */
57142axes.draw = function(gd, arg, opts) {
57143 var fullLayout = gd._fullLayout;
57144
57145 if(arg === 'redraw') {
57146 fullLayout._paper.selectAll('g.subplot').each(function(d) {
57147 var id = d[0];
57148 var plotinfo = fullLayout._plots[id];
57149 var xa = plotinfo.xaxis;
57150 var ya = plotinfo.yaxis;
57151
57152 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
57153 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
57154 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove();
57155 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick2').remove();
57156 plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove();
57157 plotinfo.yaxislayer.selectAll('.' + ya._id + 'divider').remove();
57158
57159 if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
57160 if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
57161
57162 fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
57163 fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
57164 });
57165 }
57166
57167 var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;
57168
57169 return Lib.syncOrAsync(axList.map(function(axId) {
57170 return function() {
57171 if(!axId) return;
57172
57173 var ax = axes.getFromId(gd, axId);
57174 var axDone = axes.drawOne(gd, ax, opts);
57175
57176 ax._r = ax.range.slice();
57177 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
57178
57179 return axDone;
57180 };
57181 }));
57182};
57183
57184/**
57185 * Draw one cartesian axis
57186 *
57187 * @param {DOM element} gd
57188 * @param {object} ax (full) axis object
57189 * @param {object} opts
57190 * - @param {boolean} skipTitle (set to true to skip axis title draw call)
57191 *
57192 * Depends on:
57193 * - ax._mainSubplot (from linkSubplots)
57194 * - ax._mainAxis
57195 * - ax._anchorAxis
57196 * - ax._subplotsWith
57197 * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots)
57198 * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults)
57199 * - ax._mainLinePosition (from lsInner)
57200 * - ax._mainMirrorPosition
57201 * - ax._linepositions
57202 *
57203 * Fills in:
57204 * - ax._vals:
57205 * - ax._gridVals:
57206 * - ax._selections:
57207 * - ax._tickAngles:
57208 * - ax._depth (when required only):
57209 * - and calls ax.setScale
57210 */
57211axes.drawOne = function(gd, ax, opts) {
57212 opts = opts || {};
57213
57214 var i, sp, plotinfo;
57215
57216 ax.setScale();
57217
57218 var fullLayout = gd._fullLayout;
57219 var axId = ax._id;
57220 var axLetter = axId.charAt(0);
57221 var counterLetter = axes.counterLetter(axId);
57222 var mainPlotinfo = fullLayout._plots[ax._mainSubplot];
57223
57224 // this happens when updating matched group with 'missing' axes
57225 if(!mainPlotinfo) return;
57226
57227 var mainAxLayer = mainPlotinfo[axLetter + 'axislayer'];
57228 var mainLinePosition = ax._mainLinePosition;
57229 var mainMirrorPosition = ax._mainMirrorPosition;
57230
57231 var vals = ax._vals = axes.calcTicks(ax);
57232
57233 // Add a couple of axis properties that should cause us to recreate
57234 // elements. Used in d3 data function.
57235 var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_');
57236 for(i = 0; i < vals.length; i++) {
57237 vals[i].axInfo = axInfo;
57238 }
57239
57240 // stash selections to avoid DOM queries e.g.
57241 // - stash tickLabels selection, so that drawTitle can use it to scoot title
57242 ax._selections = {};
57243 // stash tick angle (including the computed 'auto' values) per tick-label class
57244 // linkup 'previous' tick angles on redraws
57245 if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles;
57246 ax._tickAngles = {};
57247 // measure [in px] between axis position and outward-most part of bounding box
57248 // (touching either the tick label or ticks)
57249 // depth can be expansive to compute, so we only do so when required
57250 ax._depth = null;
57251
57252 // calcLabelLevelBbox can be expensive,
57253 // so make sure to not call it twice during the same Axes.drawOne call
57254 // by stashing label-level bounding boxes per tick-label class
57255 var llbboxes = {};
57256 function getLabelLevelBbox(suffix) {
57257 var cls = axId + (suffix || 'tick');
57258 if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls);
57259 return llbboxes[cls];
57260 }
57261
57262 if(!ax.visible) return;
57263
57264 var transFn = axes.makeTransFn(ax);
57265 var tickVals;
57266 // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
57267 // The key case here is removing zero lines when the axis bound is zero
57268 var valsClipped;
57269
57270 if(ax.tickson === 'boundaries') {
57271 var boundaryVals = getBoundaryVals(ax, vals);
57272 valsClipped = axes.clipEnds(ax, boundaryVals);
57273 tickVals = ax.ticks === 'inside' ? valsClipped : boundaryVals;
57274 } else {
57275 valsClipped = axes.clipEnds(ax, vals);
57276 tickVals = ax.ticks === 'inside' ? valsClipped : vals;
57277 }
57278
57279 var gridVals = ax._gridVals = valsClipped;
57280 var dividerVals = getDividerVals(ax, vals);
57281
57282 if(!fullLayout._hasOnlyLargeSploms) {
57283 var subplotsWithAx = ax._subplotsWith;
57284
57285 // keep track of which subplots (by main counter axis) we've already
57286 // drawn grids for, so we don't overdraw overlaying subplots
57287 var finishedGrids = {};
57288
57289 for(i = 0; i < subplotsWithAx.length; i++) {
57290 sp = subplotsWithAx[i];
57291 plotinfo = fullLayout._plots[sp];
57292
57293 var counterAxis = plotinfo[counterLetter + 'axis'];
57294 var mainCounterID = counterAxis._mainAxis._id;
57295 if(finishedGrids[mainCounterID]) continue;
57296 finishedGrids[mainCounterID] = 1;
57297
57298 var gridPath = axLetter === 'x' ?
57299 'M0,' + counterAxis._offset + 'v' + counterAxis._length :
57300 'M' + counterAxis._offset + ',0h' + counterAxis._length;
57301
57302 axes.drawGrid(gd, ax, {
57303 vals: gridVals,
57304 counterAxis: counterAxis,
57305 layer: plotinfo.gridlayer.select('.' + axId),
57306 path: gridPath,
57307 transFn: transFn
57308 });
57309 axes.drawZeroLine(gd, ax, {
57310 counterAxis: counterAxis,
57311 layer: plotinfo.zerolinelayer,
57312 path: gridPath,
57313 transFn: transFn
57314 });
57315 }
57316 }
57317
57318 var tickSigns = axes.getTickSigns(ax);
57319 var tickSubplots = [];
57320
57321 if(ax.ticks) {
57322 var mainTickPath = axes.makeTickPath(ax, mainLinePosition, tickSigns[2]);
57323 var mirrorTickPath;
57324 var fullTickPath;
57325 if(ax._anchorAxis && ax.mirror && ax.mirror !== true) {
57326 mirrorTickPath = axes.makeTickPath(ax, mainMirrorPosition, tickSigns[3]);
57327 fullTickPath = mainTickPath + mirrorTickPath;
57328 } else {
57329 mirrorTickPath = '';
57330 fullTickPath = mainTickPath;
57331 }
57332
57333 var tickPath;
57334 if(ax.showdividers && ax.ticks === 'outside' && ax.tickson === 'boundaries') {
57335 var dividerLookup = {};
57336 for(i = 0; i < dividerVals.length; i++) {
57337 dividerLookup[dividerVals[i].x] = 1;
57338 }
57339 tickPath = function(d) {
57340 return dividerLookup[d.x] ? mirrorTickPath : fullTickPath;
57341 };
57342 } else {
57343 tickPath = fullTickPath;
57344 }
57345
57346 axes.drawTicks(gd, ax, {
57347 vals: tickVals,
57348 layer: mainAxLayer,
57349 path: tickPath,
57350 transFn: transFn
57351 });
57352
57353 if(ax.mirror === 'allticks') {
57354 tickSubplots = Object.keys(ax._linepositions || {});
57355 }
57356 }
57357
57358 for(i = 0; i < tickSubplots.length; i++) {
57359 sp = tickSubplots[i];
57360 plotinfo = fullLayout._plots[sp];
57361 // [bottom or left, top or right], free and main are handled above
57362 var linepositions = ax._linepositions[sp] || [];
57363 var spTickPath = axes.makeTickPath(ax, linepositions[0], tickSigns[0]) +
57364 axes.makeTickPath(ax, linepositions[1], tickSigns[1]);
57365
57366 axes.drawTicks(gd, ax, {
57367 vals: tickVals,
57368 layer: plotinfo[axLetter + 'axislayer'],
57369 path: spTickPath,
57370 transFn: transFn
57371 });
57372 }
57373
57374 var seq = [];
57375
57376 // tick labels - for now just the main labels.
57377 // TODO: mirror labels, esp for subplots
57378
57379 seq.push(function() {
57380 return axes.drawLabels(gd, ax, {
57381 vals: vals,
57382 layer: mainAxLayer,
57383 transFn: transFn,
57384 labelFns: axes.makeLabelFns(ax, mainLinePosition)
57385 });
57386 });
57387
57388 if(ax.type === 'multicategory') {
57389 var pad = {x: 2, y: 10}[axLetter];
57390
57391 seq.push(function() {
57392 var bboxKey = {x: 'height', y: 'width'}[axLetter];
57393 var standoff = getLabelLevelBbox()[bboxKey] + pad +
57394 (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0);
57395
57396 return axes.drawLabels(gd, ax, {
57397 vals: getSecondaryLabelVals(ax, vals),
57398 layer: mainAxLayer,
57399 cls: axId + 'tick2',
57400 repositionOnUpdate: true,
57401 secondary: true,
57402 transFn: transFn,
57403 labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4])
57404 });
57405 });
57406
57407 seq.push(function() {
57408 ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition);
57409
57410 return drawDividers(gd, ax, {
57411 vals: dividerVals,
57412 layer: mainAxLayer,
57413 path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth),
57414 transFn: transFn
57415 });
57416 });
57417 } else if(ax.title.hasOwnProperty('standoff')) {
57418 seq.push(function() {
57419 ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition);
57420 });
57421 }
57422
57423 var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax);
57424
57425 seq.push(function() {
57426 var s = ax.side.charAt(0);
57427 var sMirror = OPPOSITE_SIDE[ax.side].charAt(0);
57428 var pos = axes.getPxPosition(gd, ax);
57429 var outsideTickLen = ax.ticks === 'outside' ? ax.ticklen : 0;
57430 var llbbox;
57431
57432 var push;
57433 var mirrorPush;
57434 var rangeSliderPush;
57435
57436 if(ax.automargin || hasRangeSlider) {
57437 if(ax.type === 'multicategory') {
57438 llbbox = getLabelLevelBbox('tick2');
57439 } else {
57440 llbbox = getLabelLevelBbox();
57441 if(axLetter === 'x' && s === 'b') {
57442 ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen);
57443 }
57444 }
57445 }
57446
57447 if(ax.automargin) {
57448 push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
57449 var domainIndices = [0, 1];
57450
57451 if(axLetter === 'x') {
57452 if(s === 'b') {
57453 push[s] = ax._depth;
57454 } else {
57455 push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen);
57456 domainIndices.reverse();
57457 }
57458
57459 if(llbbox.width > 0) {
57460 var rExtra = llbbox.right - (ax._offset + ax._length);
57461 if(rExtra > 0) {
57462 push.xr = 1;
57463 push.r = rExtra;
57464 }
57465 var lExtra = ax._offset - llbbox.left;
57466 if(lExtra > 0) {
57467 push.xl = 0;
57468 push.l = lExtra;
57469 }
57470 }
57471 } else {
57472 if(s === 'l') {
57473 push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen);
57474 } else {
57475 push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen);
57476 domainIndices.reverse();
57477 }
57478
57479 if(llbbox.height > 0) {
57480 var bExtra = llbbox.bottom - (ax._offset + ax._length);
57481 if(bExtra > 0) {
57482 push.yb = 0;
57483 push.b = bExtra;
57484 }
57485 var tExtra = ax._offset - llbbox.top;
57486 if(tExtra > 0) {
57487 push.yt = 1;
57488 push.t = tExtra;
57489 }
57490 }
57491 }
57492
57493 push[counterLetter] = ax.anchor === 'free' ?
57494 ax.position :
57495 ax._anchorAxis.domain[domainIndices[0]];
57496
57497 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
57498 push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0);
57499 }
57500
57501 if(ax.mirror && ax.anchor !== 'free') {
57502 mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
57503
57504 mirrorPush[sMirror] = ax.linewidth;
57505 if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen;
57506
57507 if(ax.mirror === true || ax.mirror === 'ticks') {
57508 mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]];
57509 } else if(ax.mirror === 'all' || ax.mirror === 'allticks') {
57510 mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]];
57511 }
57512 }
57513 }
57514
57515 if(hasRangeSlider) {
57516 rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
57517 }
57518
57519 Plots.autoMargin(gd, axAutoMarginID(ax), push);
57520 Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush);
57521 Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush);
57522 });
57523
57524 if(!opts.skipTitle &&
57525 !(hasRangeSlider && ax.side === 'bottom')
57526 ) {
57527 seq.push(function() { return drawTitle(gd, ax); });
57528 }
57529
57530 return Lib.syncOrAsync(seq);
57531};
57532
57533function getBoundaryVals(ax, vals) {
57534 var out = [];
57535 var i;
57536
57537 // boundaryVals are never used for labels;
57538 // no need to worry about the other tickTextObj keys
57539 var _push = function(d, bndIndex) {
57540 var xb = d.xbnd[bndIndex];
57541 if(xb !== null) {
57542 out.push(Lib.extendFlat({}, d, {x: xb}));
57543 }
57544 };
57545
57546 if(vals.length) {
57547 for(i = 0; i < vals.length; i++) {
57548 _push(vals[i], 0);
57549 }
57550 _push(vals[i - 1], 1);
57551 }
57552
57553 return out;
57554}
57555
57556function getSecondaryLabelVals(ax, vals) {
57557 var out = [];
57558 var lookup = {};
57559
57560 for(var i = 0; i < vals.length; i++) {
57561 var d = vals[i];
57562 if(lookup[d.text2]) {
57563 lookup[d.text2].push(d.x);
57564 } else {
57565 lookup[d.text2] = [d.x];
57566 }
57567 }
57568
57569 for(var k in lookup) {
57570 out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k));
57571 }
57572
57573 return out;
57574}
57575
57576function getDividerVals(ax, vals) {
57577 var out = [];
57578 var i, current;
57579
57580 // never used for labels;
57581 // no need to worry about the other tickTextObj keys
57582 var _push = function(d, bndIndex) {
57583 var xb = d.xbnd[bndIndex];
57584 if(xb !== null) {
57585 out.push(Lib.extendFlat({}, d, {x: xb}));
57586 }
57587 };
57588
57589 if(ax.showdividers && vals.length) {
57590 for(i = 0; i < vals.length; i++) {
57591 var d = vals[i];
57592 if(d.text2 !== current) {
57593 _push(d, 0);
57594 }
57595 current = d.text2;
57596 }
57597 _push(vals[i - 1], 1);
57598 }
57599
57600 return out;
57601}
57602
57603function calcLabelLevelBbox(ax, cls) {
57604 var top, bottom;
57605 var left, right;
57606
57607 if(ax._selections[cls].size()) {
57608 top = Infinity;
57609 bottom = -Infinity;
57610 left = Infinity;
57611 right = -Infinity;
57612 ax._selections[cls].each(function() {
57613 var thisLabel = selectTickLabel(this);
57614 // Use parent node <g.(x|y)tick>, to make Drawing.bBox
57615 // retrieve a bbox computed with transform info
57616 //
57617 // To improve perf, it would be nice to use `thisLabel.node()`
57618 // (like in fixLabelOverlaps) instead and use Axes.getPxPosition
57619 // together with the makeLabelFns outputs and `tickangle`
57620 // to compute one bbox per (tick value x tick style)
57621 var bb = Drawing.bBox(thisLabel.node().parentNode);
57622 top = Math.min(top, bb.top);
57623 bottom = Math.max(bottom, bb.bottom);
57624 left = Math.min(left, bb.left);
57625 right = Math.max(right, bb.right);
57626 });
57627 } else {
57628 top = 0;
57629 bottom = 0;
57630 left = 0;
57631 right = 0;
57632 }
57633
57634 return {
57635 top: top,
57636 bottom: bottom,
57637 left: left,
57638 right: right,
57639 height: bottom - top,
57640 width: right - left
57641 };
57642}
57643
57644/**
57645 * Which direction do the 'ax.side' values, and free ticks go?
57646 *
57647 * @param {object} ax (full) axis object
57648 * - {string} _id (starting with 'x' or 'y')
57649 * - {string} side
57650 * - {string} ticks
57651 * @return {array} all entries are either -1 or 1
57652 * - [0]: sign for top/right ticks (i.e. negative SVG direction)
57653 * - [1]: sign for bottom/left ticks (i.e. positive SVG direction)
57654 * - [2]: sign for ticks corresponding to 'ax.side'
57655 * - [3]: sign for ticks mirroring 'ax.side'
57656 * - [4]: sign of arrow starting at axis pointing towards margin
57657 */
57658axes.getTickSigns = function(ax) {
57659 var axLetter = ax._id.charAt(0);
57660 var sideOpposite = {x: 'top', y: 'right'}[axLetter];
57661 var main = ax.side === sideOpposite ? 1 : -1;
57662 var out = [-1, 1, main, -main];
57663 // then we flip if outside XOR y axis
57664 if((ax.ticks !== 'inside') === (axLetter === 'x')) {
57665 out = out.map(function(v) { return -v; });
57666 }
57667 // independent of `ticks`; do not flip this one
57668 if(ax.side) {
57669 out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]);
57670 }
57671 return out;
57672};
57673
57674/**
57675 * Make axis translate transform function
57676 *
57677 * @param {object} ax (full) axis object
57678 * - {string} _id
57679 * - {number} _offset
57680 * - {fn} l2p
57681 * @return {fn} function of calcTicks items
57682 */
57683axes.makeTransFn = function(ax) {
57684 var axLetter = ax._id.charAt(0);
57685 var offset = ax._offset;
57686 return axLetter === 'x' ?
57687 function(d) { return 'translate(' + (offset + ax.l2p(d.x)) + ',0)'; } :
57688 function(d) { return 'translate(0,' + (offset + ax.l2p(d.x)) + ')'; };
57689};
57690
57691/**
57692 * Make axis tick path string
57693 *
57694 * @param {object} ax (full) axis object
57695 * - {string} _id
57696 * - {number} ticklen
57697 * - {number} linewidth
57698 * @param {number} shift along direction of ticklen
57699 * @param {1 or -1} sgn tick sign
57700 * @param {number (optional)} len tick length
57701 * @return {string}
57702 */
57703axes.makeTickPath = function(ax, shift, sgn, len) {
57704 len = len !== undefined ? len : ax.ticklen;
57705
57706 var axLetter = ax._id.charAt(0);
57707 var pad = (ax.linewidth || 1) / 2;
57708
57709 return axLetter === 'x' ?
57710 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) :
57711 'M' + (shift + pad * sgn) + ',0h' + (len * sgn);
57712};
57713
57714/**
57715 * Make axis tick label x, y and anchor functions
57716 *
57717 * @param {object} ax (full) axis object
57718 * - {string} _id
57719 * - {string} ticks
57720 * - {number} ticklen
57721 * - {string} side
57722 * - {number} linewidth
57723 * - {number} tickfont.size
57724 * - {boolean} showline
57725 * @param {number} shift
57726 * @param {number} angle [in degrees] ...
57727 * @return {object}
57728 * - {fn} xFn
57729 * - {fn} yFn
57730 * - {fn} anchorFn
57731 * - {fn} heightFn
57732 * - {number} labelStandoff (gap parallel to ticks)
57733 * - {number} labelShift (gap perpendicular to ticks)
57734 */
57735axes.makeLabelFns = function(ax, shift, angle) {
57736 var axLetter = ax._id.charAt(0);
57737 var ticksOnOutsideLabels = ax.tickson !== 'boundaries' && ax.ticks === 'outside';
57738
57739 var labelStandoff = 0;
57740 var labelShift = 0;
57741
57742 if(ticksOnOutsideLabels) {
57743 labelStandoff += ax.ticklen;
57744 }
57745 if(angle && ax.ticks === 'outside') {
57746 var rad = Lib.deg2rad(angle);
57747 labelStandoff = ax.ticklen * Math.cos(rad) + 1;
57748 labelShift = ax.ticklen * Math.sin(rad);
57749 }
57750 if(ax.showticklabels && (ticksOnOutsideLabels || ax.showline)) {
57751 labelStandoff += 0.2 * ax.tickfont.size;
57752 }
57753 labelStandoff += (ax.linewidth || 1) / 2;
57754
57755 var out = {
57756 labelStandoff: labelStandoff,
57757 labelShift: labelShift
57758 };
57759
57760 var x0, y0, ff, flipIt;
57761
57762 if(axLetter === 'x') {
57763 flipIt = ax.side === 'bottom' ? 1 : -1;
57764 x0 = labelShift * flipIt;
57765 y0 = shift + labelStandoff * flipIt;
57766 ff = ax.side === 'bottom' ? 1 : -0.2;
57767
57768 out.xFn = function(d) { return d.dx + x0; };
57769 out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; };
57770 out.anchorFn = function(d, a) {
57771 if(!isNumeric(a) || a === 0 || a === 180) {
57772 return 'middle';
57773 }
57774 return (a * flipIt < 0) ? 'end' : 'start';
57775 };
57776 out.heightFn = function(d, a, h) {
57777 return (a < -60 || a > 60) ? -0.5 * h :
57778 ax.side === 'top' ? -h :
57779 0;
57780 };
57781 } else if(axLetter === 'y') {
57782 flipIt = ax.side === 'right' ? 1 : -1;
57783 x0 = labelStandoff;
57784 y0 = -labelShift * flipIt;
57785 ff = Math.abs(ax.tickangle) === 90 ? 0.5 : 0;
57786
57787 out.xFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; };
57788 out.yFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; };
57789 out.anchorFn = function(d, a) {
57790 if(isNumeric(a) && Math.abs(a) === 90) {
57791 return 'middle';
57792 }
57793 return ax.side === 'right' ? 'start' : 'end';
57794 };
57795 out.heightFn = function(d, a, h) {
57796 a *= ax.side === 'left' ? 1 : -1;
57797 return a < -30 ? -h :
57798 a < 30 ? -0.5 * h :
57799 0;
57800 };
57801 }
57802
57803 return out;
57804};
57805
57806function tickDataFn(d) {
57807 return [d.text, d.x, d.axInfo, d.font, d.fontSize, d.fontColor].join('_');
57808}
57809
57810/**
57811 * Draw axis ticks
57812 *
57813 * @param {DOM element} gd
57814 * @param {object} ax (full) axis object
57815 * - {string} _id
57816 * - {string} ticks
57817 * - {number} linewidth
57818 * - {string} tickcolor
57819 * @param {object} opts
57820 * - {array of object} vals (calcTicks output-like)
57821 * - {d3 selection} layer
57822 * - {string or fn} path
57823 * - {fn} transFn
57824 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
57825 */
57826axes.drawTicks = function(gd, ax, opts) {
57827 opts = opts || {};
57828
57829 var cls = ax._id + 'tick';
57830
57831 var ticks = opts.layer.selectAll('path.' + cls)
57832 .data(ax.ticks ? opts.vals : [], tickDataFn);
57833
57834 ticks.exit().remove();
57835
57836 ticks.enter().append('path')
57837 .classed(cls, 1)
57838 .classed('ticks', 1)
57839 .classed('crisp', opts.crisp !== false)
57840 .call(Color.stroke, ax.tickcolor)
57841 .style('stroke-width', Drawing.crispRound(gd, ax.tickwidth, 1) + 'px')
57842 .attr('d', opts.path);
57843
57844 ticks.attr('transform', opts.transFn);
57845};
57846
57847/**
57848 * Draw axis grid
57849 *
57850 * @param {DOM element} gd
57851 * @param {object} ax (full) axis object
57852 * - {string} _id
57853 * - {boolean} showgrid
57854 * - {string} gridcolor
57855 * - {string} gridwidth
57856 * - {boolean} zeroline
57857 * - {string} type
57858 * - {string} dtick
57859 * @param {object} opts
57860 * - {array of object} vals (calcTicks output-like)
57861 * - {d3 selection} layer
57862 * - {object} counterAxis (full axis object corresponding to counter axis)
57863 * optional - only required if this axis supports zero lines
57864 * - {string or fn} path
57865 * - {fn} transFn
57866 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
57867 */
57868axes.drawGrid = function(gd, ax, opts) {
57869 opts = opts || {};
57870
57871 var cls = ax._id + 'grid';
57872 var vals = opts.vals;
57873 var counterAx = opts.counterAxis;
57874 if(ax.showgrid === false) {
57875 vals = [];
57876 } else if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
57877 var isArrayMode = ax.tickmode === 'array';
57878 for(var i = 0; i < vals.length; i++) {
57879 var xi = vals[i].x;
57880 if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
57881 vals = vals.slice(0, i).concat(vals.slice(i + 1));
57882 // In array mode you can in principle have multiple
57883 // ticks at 0, so test them all. Otherwise once we found
57884 // one we can stop.
57885 if(isArrayMode) i--;
57886 else break;
57887 }
57888 }
57889 }
57890
57891 var grid = opts.layer.selectAll('path.' + cls)
57892 .data(vals, tickDataFn);
57893
57894 grid.exit().remove();
57895
57896 grid.enter().append('path')
57897 .classed(cls, 1)
57898 .classed('crisp', opts.crisp !== false);
57899
57900 ax._gw = Drawing.crispRound(gd, ax.gridwidth, 1);
57901
57902 grid.attr('transform', opts.transFn)
57903 .attr('d', opts.path)
57904 .call(Color.stroke, ax.gridcolor || '#ddd')
57905 .style('stroke-width', ax._gw + 'px');
57906
57907 if(typeof opts.path === 'function') grid.attr('d', opts.path);
57908};
57909
57910/**
57911 * Draw axis zero-line
57912 *
57913 * @param {DOM element} gd
57914 * @param {object} ax (full) axis object
57915 * - {string} _id
57916 * - {boolean} zeroline
57917 * - {number} zerolinewidth
57918 * - {string} zerolinecolor
57919 * - {number (optional)} _gridWidthCrispRound
57920 * @param {object} opts
57921 * - {d3 selection} layer
57922 * - {object} counterAxis (full axis object corresponding to counter axis)
57923 * - {string or fn} path
57924 * - {fn} transFn
57925 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
57926 */
57927axes.drawZeroLine = function(gd, ax, opts) {
57928 opts = opts || opts;
57929
57930 var cls = ax._id + 'zl';
57931 var show = axes.shouldShowZeroLine(gd, ax, opts.counterAxis);
57932
57933 var zl = opts.layer.selectAll('path.' + cls)
57934 .data(show ? [{x: 0, id: ax._id}] : []);
57935
57936 zl.exit().remove();
57937
57938 zl.enter().append('path')
57939 .classed(cls, 1)
57940 .classed('zl', 1)
57941 .classed('crisp', opts.crisp !== false)
57942 .each(function() {
57943 // use the fact that only one element can enter to trigger a sort.
57944 // If several zerolines enter at the same time we will sort once per,
57945 // but generally this should be a minimal overhead.
57946 opts.layer.selectAll('path').sort(function(da, db) {
57947 return axisIds.idSort(da.id, db.id);
57948 });
57949 });
57950
57951 zl.attr('transform', opts.transFn)
57952 .attr('d', opts.path)
57953 .call(Color.stroke, ax.zerolinecolor || Color.defaultLine)
57954 .style('stroke-width', Drawing.crispRound(gd, ax.zerolinewidth, ax._gw || 1) + 'px');
57955};
57956
57957/**
57958 * Draw axis tick labels
57959 *
57960 * @param {DOM element} gd
57961 * @param {object} ax (full) axis object
57962 * - {string} _id
57963 * - {boolean} showticklabels
57964 * - {number} tickangle
57965 * - {object (optional)} _selections
57966 * - {object} (optional)} _tickAngles
57967 * - {object} (optional)} _prevTickAngles
57968 * @param {object} opts
57969 * - {array of object} vals (calcTicks output-like)
57970 * - {d3 selection} layer
57971 * - {string (optional)} cls (node className)
57972 * - {boolean} repositionOnUpdate (set to true to reposition update selection)
57973 * - {boolean} secondary
57974 * - {fn} transFn
57975 * - {object} labelFns
57976 * + {fn} xFn
57977 * + {fn} yFn
57978 * + {fn} anchorFn
57979 * + {fn} heightFn
57980 */
57981axes.drawLabels = function(gd, ax, opts) {
57982 opts = opts || {};
57983
57984 var fullLayout = gd._fullLayout;
57985 var axId = ax._id;
57986 var axLetter = axId.charAt(0);
57987 var cls = opts.cls || axId + 'tick';
57988 var vals = opts.vals;
57989 var labelFns = opts.labelFns;
57990 var tickAngle = opts.secondary ? 0 : ax.tickangle;
57991 var prevAngle = (ax._prevTickAngles || {})[cls];
57992
57993 var tickLabels = opts.layer.selectAll('g.' + cls)
57994 .data(ax.showticklabels ? vals : [], tickDataFn);
57995
57996 var labelsReady = [];
57997
57998 tickLabels.enter().append('g')
57999 .classed(cls, 1)
58000 .append('text')
58001 // only so tex has predictable alignment that we can
58002 // alter later
58003 .attr('text-anchor', 'middle')
58004 .each(function(d) {
58005 var thisLabel = d3.select(this);
58006 var newPromise = gd._promises.length;
58007
58008 thisLabel
58009 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d))
58010 .call(Drawing.font, d.font, d.fontSize, d.fontColor)
58011 .text(d.text)
58012 .call(svgTextUtils.convertToTspans, gd);
58013
58014 if(gd._promises[newPromise]) {
58015 // if we have an async label, we'll deal with that
58016 // all here so take it out of gd._promises and
58017 // instead position the label and promise this in
58018 // labelsReady
58019 labelsReady.push(gd._promises.pop().then(function() {
58020 positionLabels(thisLabel, tickAngle);
58021 }));
58022 } else {
58023 // sync label: just position it now.
58024 positionLabels(thisLabel, tickAngle);
58025 }
58026 });
58027
58028 tickLabels.exit().remove();
58029
58030 if(opts.repositionOnUpdate) {
58031 tickLabels.each(function(d) {
58032 d3.select(this).select('text')
58033 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d));
58034 });
58035 }
58036
58037 function positionLabels(s, angle) {
58038 s.each(function(d) {
58039 var thisLabel = d3.select(this);
58040 var mathjaxGroup = thisLabel.select('.text-math-group');
58041 var anchor = labelFns.anchorFn(d, angle);
58042
58043 var transform = opts.transFn.call(thisLabel.node(), d) +
58044 ((isNumeric(angle) && +angle !== 0) ?
58045 (' rotate(' + angle + ',' + labelFns.xFn(d) + ',' +
58046 (labelFns.yFn(d) - d.fontSize / 2) + ')') :
58047 '');
58048
58049 // how much to shift a multi-line label to center it vertically.
58050 var nLines = svgTextUtils.lineCount(thisLabel);
58051 var lineHeight = LINE_SPACING * d.fontSize;
58052 var anchorHeight = labelFns.heightFn(d, isNumeric(angle) ? +angle : 0, (nLines - 1) * lineHeight);
58053
58054 if(anchorHeight) {
58055 transform += ' translate(0, ' + anchorHeight + ')';
58056 }
58057
58058 if(mathjaxGroup.empty()) {
58059 thisLabel.select('text').attr({
58060 transform: transform,
58061 'text-anchor': anchor
58062 });
58063 } else {
58064 var mjWidth = Drawing.bBox(mathjaxGroup.node()).width;
58065 var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor];
58066 mathjaxGroup.attr('transform', transform + (mjShift ? 'translate(' + mjShift + ',0)' : ''));
58067 }
58068 });
58069 }
58070
58071 // make sure all labels are correctly positioned at their base angle
58072 // the positionLabels call above is only for newly drawn labels.
58073 // do this without waiting, using the last calculated angle to
58074 // minimize flicker, then do it again when we know all labels are
58075 // there, putting back the prescribed angle to check for overlaps.
58076 positionLabels(tickLabels, (prevAngle + 1) ? prevAngle : tickAngle);
58077
58078 function allLabelsReady() {
58079 return labelsReady.length && Promise.all(labelsReady);
58080 }
58081
58082 var autoangle = null;
58083
58084 function fixLabelOverlaps() {
58085 positionLabels(tickLabels, tickAngle);
58086
58087 // check for auto-angling if x labels overlap
58088 // don't auto-angle at all for log axes with
58089 // base and digit format
58090 if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) &&
58091 (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D')
58092 ) {
58093 autoangle = 0;
58094
58095 var maxFontSize = 0;
58096 var lbbArray = [];
58097 var i;
58098
58099 tickLabels.each(function(d) {
58100 maxFontSize = Math.max(maxFontSize, d.fontSize);
58101
58102 var x = ax.l2p(d.x);
58103 var thisLabel = selectTickLabel(this);
58104 var bb = Drawing.bBox(thisLabel.node());
58105
58106 lbbArray.push({
58107 // ignore about y, just deal with x overlaps
58108 top: 0,
58109 bottom: 10,
58110 height: 10,
58111 left: x - bb.width / 2,
58112 // impose a 2px gap
58113 right: x + bb.width / 2 + 2,
58114 width: bb.width + 2
58115 });
58116 });
58117
58118 if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
58119 var gap = 2;
58120 if(ax.ticks) gap += ax.tickwidth / 2;
58121
58122 // TODO should secondary labels also fall into this fix-overlap regime?
58123
58124 for(i = 0; i < lbbArray.length; i++) {
58125 var xbnd = vals[i].xbnd;
58126 var lbb = lbbArray[i];
58127 if(
58128 (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
58129 (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
58130 ) {
58131 autoangle = 90;
58132 break;
58133 }
58134 }
58135 } else {
58136 var vLen = vals.length;
58137 var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
58138 var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory';
58139
58140 // any overlap at all - set 30 degrees or 90 degrees
58141 for(i = 0; i < lbbArray.length - 1; i++) {
58142 if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1])) {
58143 autoangle = rotate90 ? 90 : 30;
58144 break;
58145 }
58146 }
58147 }
58148
58149 if(autoangle) {
58150 positionLabels(tickLabels, autoangle);
58151 }
58152 }
58153 }
58154
58155 if(ax._selections) {
58156 ax._selections[cls] = tickLabels;
58157 }
58158
58159 var seq = [allLabelsReady];
58160
58161 // N.B. during auto-margin redraws, if the axis fixed its label overlaps
58162 // by rotating 90 degrees, do not attempt to re-fix its label overlaps
58163 // as this can lead to infinite redraw loops!
58164 if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) {
58165 autoangle = 90;
58166 seq.push(function() {
58167 positionLabels(tickLabels, prevAngle);
58168 });
58169 } else {
58170 seq.push(fixLabelOverlaps);
58171 }
58172
58173 // save current tick angle for future redraws
58174 if(ax._tickAngles) {
58175 seq.push(function() {
58176 ax._tickAngles[cls] = autoangle === null ?
58177 (isNumeric(tickAngle) ? tickAngle : 0) :
58178 autoangle;
58179 });
58180 }
58181
58182 var done = Lib.syncOrAsync(seq);
58183 if(done && done.then) gd._promises.push(done);
58184 return done;
58185};
58186
58187/**
58188 * Draw axis dividers
58189 *
58190 * @param {DOM element} gd
58191 * @param {object} ax (full) axis object
58192 * - {string} _id
58193 * - {string} showdividers
58194 * - {number} dividerwidth
58195 * - {string} dividercolor
58196 * @param {object} opts
58197 * - {array of object} vals (calcTicks output-like)
58198 * - {d3 selection} layer
58199 * - {fn} path
58200 * - {fn} transFn
58201 */
58202function drawDividers(gd, ax, opts) {
58203 var cls = ax._id + 'divider';
58204 var vals = opts.vals;
58205
58206 var dividers = opts.layer.selectAll('path.' + cls)
58207 .data(vals, tickDataFn);
58208
58209 dividers.exit().remove();
58210
58211 dividers.enter().insert('path', ':first-child')
58212 .classed(cls, 1)
58213 .classed('crisp', 1)
58214 .call(Color.stroke, ax.dividercolor)
58215 .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px');
58216
58217 dividers
58218 .attr('transform', opts.transFn)
58219 .attr('d', opts.path);
58220}
58221
58222/**
58223 * Get axis position in px, that is the distance for the graph's
58224 * top (left) edge for x (y) axes.
58225 *
58226 * @param {DOM element} gd
58227 * @param {object} ax (full) axis object
58228 * - {string} _id
58229 * - {string} side
58230 * if anchored:
58231 * - {object} _anchorAxis
58232 * Otherwise:
58233 * - {number} position
58234 * @return {number}
58235 */
58236axes.getPxPosition = function(gd, ax) {
58237 var gs = gd._fullLayout._size;
58238 var axLetter = ax._id.charAt(0);
58239 var side = ax.side;
58240 var anchorAxis;
58241
58242 if(ax.anchor !== 'free') {
58243 anchorAxis = ax._anchorAxis;
58244 } else if(axLetter === 'x') {
58245 anchorAxis = {
58246 _offset: gs.t + (1 - (ax.position || 0)) * gs.h,
58247 _length: 0
58248 };
58249 } else if(axLetter === 'y') {
58250 anchorAxis = {
58251 _offset: gs.l + (ax.position || 0) * gs.w,
58252 _length: 0
58253 };
58254 }
58255
58256 if(side === 'top' || side === 'left') {
58257 return anchorAxis._offset;
58258 } else if(side === 'bottom' || side === 'right') {
58259 return anchorAxis._offset + anchorAxis._length;
58260 }
58261};
58262
58263/**
58264 * Approximate axis title depth (w/o computing its bounding box)
58265 *
58266 * @param {object} ax (full) axis object
58267 * - {string} title.text
58268 * - {number} title.font.size
58269 * - {number} title.standoff
58270 * @return {number} (in px)
58271 */
58272function approxTitleDepth(ax) {
58273 var fontSize = ax.title.font.size;
58274 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
58275 if(ax.title.hasOwnProperty('standoff')) {
58276 return extraLines ?
58277 fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) :
58278 fontSize * CAP_SHIFT;
58279 } else {
58280 return extraLines ?
58281 fontSize * (extraLines + 1) * LINE_SPACING :
58282 fontSize;
58283 }
58284}
58285
58286/**
58287 * Draw axis title, compute default standoff if necessary
58288 *
58289 * @param {DOM element} gd
58290 * @param {object} ax (full) axis object
58291 * - {string} _id
58292 * - {string} _name
58293 * - {string} side
58294 * - {number} title.font.size
58295 * - {object} _selections
58296 *
58297 * - {number} _depth
58298 * - {number} title.standoff
58299 * OR
58300 * - {number} linewidth
58301 * - {boolean} showticklabels
58302 */
58303function drawTitle(gd, ax) {
58304 var fullLayout = gd._fullLayout;
58305 var axId = ax._id;
58306 var axLetter = axId.charAt(0);
58307 var fontSize = ax.title.font.size;
58308
58309 var titleStandoff;
58310
58311 if(ax.title.hasOwnProperty('standoff')) {
58312 titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax);
58313 } else {
58314 if(ax.type === 'multicategory') {
58315 titleStandoff = ax._depth;
58316 } else {
58317 var offsetBase = 1.5;
58318 titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0);
58319 }
58320
58321 if(axLetter === 'x') {
58322 titleStandoff += ax.side === 'top' ?
58323 fontSize * (ax.showticklabels ? 1 : 0) :
58324 fontSize * (ax.showticklabels ? 1.5 : 0.5);
58325 } else {
58326 titleStandoff += ax.side === 'right' ?
58327 fontSize * (ax.showticklabels ? 1 : 0.5) :
58328 fontSize * (ax.showticklabels ? 0.5 : 0);
58329 }
58330 }
58331
58332 var pos = axes.getPxPosition(gd, ax);
58333 var transform, x, y;
58334
58335 if(axLetter === 'x') {
58336 x = ax._offset + ax._length / 2;
58337 y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff;
58338 } else {
58339 y = ax._offset + ax._length / 2;
58340 x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff;
58341 transform = {rotate: '-90', offset: 0};
58342 }
58343
58344 var avoid;
58345
58346 if(ax.type !== 'multicategory') {
58347 var tickLabels = ax._selections[ax._id + 'tick'];
58348
58349 avoid = {
58350 selection: tickLabels,
58351 side: ax.side
58352 };
58353
58354 if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) {
58355 var translation = Drawing.getTranslate(tickLabels.node().parentNode);
58356 avoid.offsetLeft = translation.x;
58357 avoid.offsetTop = translation.y;
58358 }
58359
58360 if(ax.title.hasOwnProperty('standoff')) {
58361 avoid.pad = 0;
58362 }
58363 }
58364
58365 return Titles.draw(gd, axId + 'title', {
58366 propContainer: ax,
58367 propName: ax._name + '.title.text',
58368 placeholder: fullLayout._dfltTitle[axLetter],
58369 avoid: avoid,
58370 transform: transform,
58371 attributes: {x: x, y: y, 'text-anchor': 'middle'}
58372 });
58373}
58374
58375axes.shouldShowZeroLine = function(gd, ax, counterAxis) {
58376 var rng = Lib.simpleMap(ax.range, ax.r2l);
58377 return (
58378 (rng[0] * rng[1] <= 0) &&
58379 ax.zeroline &&
58380 (ax.type === 'linear' || ax.type === '-') &&
58381 !(ax.rangebreaks && ax.maskBreaks(0) === BADNUM) &&
58382 (
58383 clipEnds(ax, 0) ||
58384 !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) ||
58385 hasBarsOrFill(gd, ax)
58386 )
58387 );
58388};
58389
58390axes.clipEnds = function(ax, vals) {
58391 return vals.filter(function(d) { return clipEnds(ax, d.x); });
58392};
58393
58394function clipEnds(ax, l) {
58395 var p = ax.l2p(l);
58396 return (p > 1 && p < ax._length - 1);
58397}
58398
58399function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) {
58400 var mainCounterAxis = counterAxis._mainAxis;
58401 if(!mainCounterAxis) return;
58402
58403 var fullLayout = gd._fullLayout;
58404 var axLetter = ax._id.charAt(0);
58405 var counterLetter = axes.counterLetter(ax._id);
58406
58407 var zeroPosition = ax._offset + (
58408 ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ?
58409 0 : ax._length
58410 );
58411
58412 function lineNearZero(ax2) {
58413 if(!ax2.showline || !ax2.linewidth) return false;
58414 var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1);
58415
58416 function closeEnough(pos2) {
58417 return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance;
58418 }
58419
58420 if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) {
58421 return true;
58422 }
58423 var linePositions = ax2._linepositions || {};
58424 for(var k in linePositions) {
58425 if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) {
58426 return true;
58427 }
58428 }
58429 }
58430
58431 var plotinfo = fullLayout._plots[counterAxis._mainSubplot];
58432 if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) {
58433 return lineNearZero(counterAxis, zeroPosition);
58434 }
58435
58436 var counterLetterAxes = axes.list(gd, counterLetter);
58437 for(var i = 0; i < counterLetterAxes.length; i++) {
58438 var counterAxis2 = counterLetterAxes[i];
58439 if(
58440 counterAxis2._mainAxis === mainCounterAxis &&
58441 lineNearZero(counterAxis2, zeroPosition)
58442 ) {
58443 return true;
58444 }
58445 }
58446}
58447
58448function hasBarsOrFill(gd, ax) {
58449 var fullData = gd._fullData;
58450 var subplot = ax._mainSubplot;
58451 var axLetter = ax._id.charAt(0);
58452
58453 for(var i = 0; i < fullData.length; i++) {
58454 var trace = fullData[i];
58455
58456 if(trace.visible === true && (trace.xaxis + trace.yaxis) === subplot) {
58457 if(
58458 Registry.traceIs(trace, 'bar-like') &&
58459 trace.orientation === {x: 'h', y: 'v'}[axLetter]
58460 ) return true;
58461
58462 if(
58463 trace.fill &&
58464 trace.fill.charAt(trace.fill.length - 1) === axLetter
58465 ) return true;
58466 }
58467 }
58468 return false;
58469}
58470
58471function selectTickLabel(gTick) {
58472 var s = d3.select(gTick);
58473 var mj = s.select('.text-math-group');
58474 return mj.empty() ? s.select('text') : mj;
58475}
58476
58477/**
58478 * Find all margin pushers for 2D axes and reserve them for later use
58479 * Both label and rangeslider automargin calculations happen later so
58480 * we need to explicitly allow their ids in order to not delete them.
58481 *
58482 * TODO: can we pull the actual automargin calls forward to avoid this hack?
58483 * We're probably also doing multiple redraws in this case, would be faster
58484 * if we can just do the whole calculation ahead of time and draw once.
58485 */
58486axes.allowAutoMargin = function(gd) {
58487 var axList = axes.list(gd, '', true);
58488 for(var i = 0; i < axList.length; i++) {
58489 var ax = axList[i];
58490 if(ax.automargin) {
58491 Plots.allowAutoMargin(gd, axAutoMarginID(ax));
58492 if(ax.mirror) {
58493 Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax));
58494 }
58495 }
58496 if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) {
58497 Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax));
58498 }
58499 }
58500};
58501
58502function axAutoMarginID(ax) { return ax._id + '.automargin'; }
58503function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; }
58504function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; }
58505
58506// swap all the presentation attributes of the axes showing these traces
58507axes.swap = function(gd, traces) {
58508 var axGroups = makeAxisGroups(gd, traces);
58509
58510 for(var i = 0; i < axGroups.length; i++) {
58511 swapAxisGroup(gd, axGroups[i].x, axGroups[i].y);
58512 }
58513};
58514
58515function makeAxisGroups(gd, traces) {
58516 var groups = [];
58517 var i, j;
58518
58519 for(i = 0; i < traces.length; i++) {
58520 var groupsi = [];
58521 var xi = gd._fullData[traces[i]].xaxis;
58522 var yi = gd._fullData[traces[i]].yaxis;
58523 if(!xi || !yi) continue; // not a 2D cartesian trace?
58524
58525 for(j = 0; j < groups.length; j++) {
58526 if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) {
58527 groupsi.push(j);
58528 }
58529 }
58530
58531 if(!groupsi.length) {
58532 groups.push({x: [xi], y: [yi]});
58533 continue;
58534 }
58535
58536 var group0 = groups[groupsi[0]];
58537 var groupj;
58538
58539 if(groupsi.length > 1) {
58540 for(j = 1; j < groupsi.length; j++) {
58541 groupj = groups[groupsi[j]];
58542 mergeAxisGroups(group0.x, groupj.x);
58543 mergeAxisGroups(group0.y, groupj.y);
58544 }
58545 }
58546 mergeAxisGroups(group0.x, [xi]);
58547 mergeAxisGroups(group0.y, [yi]);
58548 }
58549
58550 return groups;
58551}
58552
58553function mergeAxisGroups(intoSet, fromSet) {
58554 for(var i = 0; i < fromSet.length; i++) {
58555 if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]);
58556 }
58557}
58558
58559function swapAxisGroup(gd, xIds, yIds) {
58560 var xFullAxes = [];
58561 var yFullAxes = [];
58562 var layout = gd.layout;
58563 var i, j;
58564
58565 for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i]));
58566 for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i]));
58567
58568 var allAxKeys = Object.keys(axAttrs);
58569
58570 var noSwapAttrs = [
58571 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType'
58572 ];
58573 var numericTypes = ['linear', 'log'];
58574
58575 for(i = 0; i < allAxKeys.length; i++) {
58576 var keyi = allAxKeys[i];
58577 var xVal = xFullAxes[0][keyi];
58578 var yVal = yFullAxes[0][keyi];
58579 var allEqual = true;
58580 var coerceLinearX = false;
58581 var coerceLinearY = false;
58582 if(keyi.charAt(0) === '_' || typeof xVal === 'function' ||
58583 noSwapAttrs.indexOf(keyi) !== -1) {
58584 continue;
58585 }
58586 for(j = 1; j < xFullAxes.length && allEqual; j++) {
58587 var xVali = xFullAxes[j][keyi];
58588 if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 &&
58589 numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) {
58590 // type is special - if we find a mixture of linear and log,
58591 // coerce them all to linear on flipping
58592 coerceLinearX = true;
58593 } else if(xVali !== xVal) allEqual = false;
58594 }
58595 for(j = 1; j < yFullAxes.length && allEqual; j++) {
58596 var yVali = yFullAxes[j][keyi];
58597 if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 &&
58598 numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) {
58599 // type is special - if we find a mixture of linear and log,
58600 // coerce them all to linear on flipping
58601 coerceLinearY = true;
58602 } else if(yFullAxes[j][keyi] !== yVal) allEqual = false;
58603 }
58604 if(allEqual) {
58605 if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear';
58606 if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear';
58607 swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle);
58608 }
58609 }
58610
58611 // now swap x&y for any annotations anchored to these x & y
58612 for(i = 0; i < gd._fullLayout.annotations.length; i++) {
58613 var ann = gd._fullLayout.annotations[i];
58614 if(xIds.indexOf(ann.xref) !== -1 &&
58615 yIds.indexOf(ann.yref) !== -1) {
58616 Lib.swapAttrs(layout.annotations[i], ['?']);
58617 }
58618 }
58619}
58620
58621function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) {
58622 // in case the value is the default for either axis,
58623 // look at the first axis in each list and see if
58624 // this key's value is undefined
58625 var np = Lib.nestedProperty;
58626 var xVal = np(layout[xFullAxes[0]._name], key).get();
58627 var yVal = np(layout[yFullAxes[0]._name], key).get();
58628 var i;
58629
58630 if(key === 'title') {
58631 // special handling of placeholder titles
58632 if(xVal && xVal.text === dfltTitle.x) {
58633 xVal.text = dfltTitle.y;
58634 }
58635 if(yVal && yVal.text === dfltTitle.y) {
58636 yVal.text = dfltTitle.x;
58637 }
58638 }
58639
58640 for(i = 0; i < xFullAxes.length; i++) {
58641 np(layout, xFullAxes[i]._name + '.' + key).set(yVal);
58642 }
58643 for(i = 0; i < yFullAxes.length; i++) {
58644 np(layout, yFullAxes[i]._name + '.' + key).set(xVal);
58645 }
58646}
58647
58648function isAngular(ax) {
58649 return ax._id === 'angularaxis';
58650}
58651
58652function moveOutsideBreak(v, ax) {
58653 var len = ax._rangebreaks.length;
58654 for(var k = 0; k < len; k++) {
58655 var brk = ax._rangebreaks[k];
58656 if(v >= brk.min && v < brk.max) {
58657 return brk.max;
58658 }
58659 }
58660 return v;
58661}
58662
58663},{"../../components/color":50,"../../components/drawing":72,"../../components/titles":145,"../../constants/alignment":152,"../../constants/numerical":155,"../../lib":177,"../../lib/svg_text_utils":198,"../../plots/plots":263,"../../registry":272,"./autorange":221,"./axis_autotype":223,"./axis_ids":225,"./clean_ticks":227,"./layout_attributes":236,"./set_convert":242,"d3":13,"fast-isnumeric":15}],223:[function(_dereq_,module,exports){
58664/**
58665* Copyright 2012-2020, Plotly, Inc.
58666* All rights reserved.
58667*
58668* This source code is licensed under the MIT license found in the
58669* LICENSE file in the root directory of this source tree.
58670*/
58671
58672
58673'use strict';
58674
58675var isNumeric = _dereq_('fast-isnumeric');
58676
58677var Lib = _dereq_('../../lib');
58678var BADNUM = _dereq_('../../constants/numerical').BADNUM;
58679
58680module.exports = function autoType(array, calendar, opts) {
58681 opts = opts || {};
58682
58683 if(!opts.noMultiCategory && multiCategory(array)) return 'multicategory';
58684 if(moreDates(array, calendar)) return 'date';
58685 if(category(array)) return 'category';
58686 if(linearOK(array)) return 'linear';
58687 else return '-';
58688};
58689
58690// is there at least one number in array? If not, we should leave
58691// ax.type empty so it can be autoset later
58692function linearOK(array) {
58693 if(!array) return false;
58694
58695 for(var i = 0; i < array.length; i++) {
58696 if(isNumeric(array[i])) return true;
58697 }
58698
58699 return false;
58700}
58701
58702// does the array a have mostly dates rather than numbers?
58703// note: some values can be neither (such as blanks, text)
58704// 2- or 4-digit integers can be both, so require twice as many
58705// dates as non-dates, to exclude cases with mostly 2 & 4 digit
58706// numbers and a few dates
58707// as with categories, consider DISTINCT values only.
58708function moreDates(a, calendar) {
58709 // test at most 1000 points, evenly spaced
58710 var inc = Math.max(1, (a.length - 1) / 1000);
58711 var dcnt = 0;
58712 var ncnt = 0;
58713 var seen = {};
58714
58715 for(var i = 0; i < a.length; i += inc) {
58716 var ai = a[Math.round(i)];
58717 var stri = String(ai);
58718 if(seen[stri]) continue;
58719 seen[stri] = 1;
58720
58721 if(Lib.isDateTime(ai, calendar)) dcnt += 1;
58722 if(isNumeric(ai)) ncnt += 1;
58723 }
58724
58725 return (dcnt > ncnt * 2);
58726}
58727
58728// are the (x,y)-values in gd.data mostly text?
58729// require twice as many DISTINCT categories as distinct numbers
58730function category(a) {
58731 // test at most 1000 points
58732 var inc = Math.max(1, (a.length - 1) / 1000);
58733 var curvenums = 0;
58734 var curvecats = 0;
58735 var seen = {};
58736
58737 for(var i = 0; i < a.length; i += inc) {
58738 var ai = a[Math.round(i)];
58739 var stri = String(ai);
58740 if(seen[stri]) continue;
58741 seen[stri] = 1;
58742
58743 if(typeof ai === 'boolean') curvecats++;
58744 else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
58745 else if(typeof ai === 'string') curvecats++;
58746 }
58747
58748 return curvecats > curvenums * 2;
58749}
58750
58751// very-loose requirements for multicategory,
58752// trace modules that should never auto-type to multicategory
58753// should be declared with 'noMultiCategory'
58754function multiCategory(a) {
58755 return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]);
58756}
58757
58758},{"../../constants/numerical":155,"../../lib":177,"fast-isnumeric":15}],224:[function(_dereq_,module,exports){
58759/**
58760* Copyright 2012-2020, Plotly, Inc.
58761* All rights reserved.
58762*
58763* This source code is licensed under the MIT license found in the
58764* LICENSE file in the root directory of this source tree.
58765*/
58766
58767'use strict';
58768
58769var isNumeric = _dereq_('fast-isnumeric');
58770
58771var Registry = _dereq_('../../registry');
58772var Lib = _dereq_('../../lib');
58773
58774var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
58775
58776var layoutAttributes = _dereq_('./layout_attributes');
58777var handleTickValueDefaults = _dereq_('./tick_value_defaults');
58778var handleTickMarkDefaults = _dereq_('./tick_mark_defaults');
58779var handleTickLabelDefaults = _dereq_('./tick_label_defaults');
58780var handleCategoryOrderDefaults = _dereq_('./category_order_defaults');
58781var handleLineGridDefaults = _dereq_('./line_grid_defaults');
58782var setConvert = _dereq_('./set_convert');
58783
58784var DAY_OF_WEEK = _dereq_('./constants').WEEKDAY_PATTERN;
58785var HOUR = _dereq_('./constants').HOUR_PATTERN;
58786
58787/**
58788 * options: object containing:
58789 *
58790 * letter: 'x' or 'y'
58791 * title: name of the axis (ie 'Colorbar') to go in default title
58792 * font: the default font to inherit
58793 * outerTicks: boolean, should ticks default to outside?
58794 * showGrid: boolean, should gridlines be shown by default?
58795 * noHover: boolean, this axis doesn't support hover effects?
58796 * noTickson: boolean, this axis doesn't support 'tickson'
58797 * data: the plot data, used to manage categories
58798 * bgColor: the plot background color, to calculate default gridline colors
58799 * calendar:
58800 * splomStash:
58801 * visibleDflt: boolean
58802 * reverseDflt: boolean
58803 * automargin: boolean
58804 */
58805module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
58806 var letter = options.letter;
58807 var font = options.font || {};
58808 var splomStash = options.splomStash || {};
58809
58810 var visible = coerce('visible', !options.visibleDflt);
58811
58812 var axTemplate = containerOut._template || {};
58813 var axType = containerOut.type || axTemplate.type || '-';
58814
58815 if(axType === 'date') {
58816 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
58817 handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar);
58818 }
58819
58820 setConvert(containerOut, layoutOut);
58821
58822 var autorangeDflt = !containerOut.isValidRange(containerIn.range);
58823 if(autorangeDflt && options.reverseDflt) autorangeDflt = 'reversed';
58824 var autoRange = coerce('autorange', autorangeDflt);
58825 if(autoRange && (axType === 'linear' || axType === '-')) coerce('rangemode');
58826
58827 coerce('range');
58828 containerOut.cleanRange();
58829
58830 handleCategoryOrderDefaults(containerIn, containerOut, coerce, options);
58831
58832 if(axType !== 'category' && !options.noHover) coerce('hoverformat');
58833
58834 var dfltColor = coerce('color');
58835 // if axis.color was provided, use it for fonts too; otherwise,
58836 // inherit from global font color in case that was provided.
58837 // Compare to dflt rather than to containerIn, so we can provide color via
58838 // template too.
58839 var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
58840 // try to get default title from splom trace, fallback to graph-wide value
58841 var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];
58842
58843 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 1});
58844 if(!visible) return containerOut;
58845
58846 coerce('title.text', dfltTitle);
58847 Lib.coerceFont(coerce, 'title.font', {
58848 family: font.family,
58849 size: Math.round(font.size * 1.2),
58850 color: dfltFontColor
58851 });
58852
58853 handleTickValueDefaults(containerIn, containerOut, coerce, axType);
58854 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 2});
58855 handleTickMarkDefaults(containerIn, containerOut, coerce, options);
58856 handleLineGridDefaults(containerIn, containerOut, coerce, {
58857 dfltColor: dfltColor,
58858 bgColor: options.bgColor,
58859 showGrid: options.showGrid,
58860 attributes: layoutAttributes
58861 });
58862
58863 if(containerOut.showline || containerOut.ticks) coerce('mirror');
58864
58865 if(options.automargin) coerce('automargin');
58866
58867 var isMultiCategory = axType === 'multicategory';
58868
58869 if(!options.noTickson &&
58870 (axType === 'category' || isMultiCategory) &&
58871 (containerOut.ticks || containerOut.showgrid)
58872 ) {
58873 var ticksonDflt;
58874 if(isMultiCategory) ticksonDflt = 'boundaries';
58875 coerce('tickson', ticksonDflt);
58876 }
58877
58878 if(isMultiCategory) {
58879 var showDividers = coerce('showdividers');
58880 if(showDividers) {
58881 coerce('dividercolor');
58882 coerce('dividerwidth');
58883 }
58884 }
58885
58886 if(axType === 'date') {
58887 handleArrayContainerDefaults(containerIn, containerOut, {
58888 name: 'rangebreaks',
58889 inclusionAttr: 'enabled',
58890 handleItemDefaults: rangebreaksDefaults
58891 });
58892
58893 if(!containerOut.rangebreaks.length) {
58894 delete containerOut.rangebreaks;
58895 } else {
58896 for(var k = 0; k < containerOut.rangebreaks.length; k++) {
58897 if(containerOut.rangebreaks[k].pattern === DAY_OF_WEEK) {
58898 containerOut._hasDayOfWeekBreaks = true;
58899 break;
58900 }
58901 }
58902
58903 setConvert(containerOut, layoutOut);
58904
58905 if(layoutOut._has('scattergl') || layoutOut._has('splom')) {
58906 for(var i = 0; i < options.data.length; i++) {
58907 var trace = options.data[i];
58908 if(trace.type === 'scattergl' || trace.type === 'splom') {
58909 trace.visible = false;
58910 Lib.warn(trace.type +
58911 ' traces do not work on axes with rangebreaks.' +
58912 ' Setting trace ' + trace.index + ' to `visible: false`.');
58913 }
58914 }
58915 }
58916 }
58917 }
58918
58919 return containerOut;
58920};
58921
58922function rangebreaksDefaults(itemIn, itemOut, containerOut) {
58923 function coerce(attr, dflt) {
58924 return Lib.coerce(itemIn, itemOut, layoutAttributes.rangebreaks, attr, dflt);
58925 }
58926
58927 var enabled = coerce('enabled');
58928
58929 if(enabled) {
58930 var bnds = coerce('bounds');
58931 if(bnds && bnds.length >= 2) {
58932 var dfltPattern = '';
58933 var i, q;
58934 if(bnds.length === 2) {
58935 for(i = 0; i < 2; i++) {
58936 q = indexOfDay(bnds[i]);
58937 if(q) {
58938 dfltPattern = DAY_OF_WEEK;
58939 break;
58940 }
58941 }
58942 }
58943 var pattern = coerce('pattern', dfltPattern);
58944 if(pattern === DAY_OF_WEEK) {
58945 for(i = 0; i < 2; i++) {
58946 q = indexOfDay(bnds[i]);
58947 if(q) {
58948 // convert to integers i.e 'Sunday' --> 0
58949 itemOut.bounds[i] = bnds[i] = q - 1;
58950 }
58951 }
58952 }
58953 if(pattern) {
58954 // ensure types and ranges
58955 for(i = 0; i < 2; i++) {
58956 q = bnds[i];
58957 switch(pattern) {
58958 case DAY_OF_WEEK :
58959 if(!isNumeric(q)) {
58960 itemOut.enabled = false;
58961 return;
58962 }
58963 q = +q;
58964
58965 if(
58966 q !== Math.floor(q) || // don't accept fractional days for mow
58967 q < 0 || q >= 7
58968 ) {
58969 itemOut.enabled = false;
58970 return;
58971 }
58972 // use number
58973 itemOut.bounds[i] = bnds[i] = q;
58974 break;
58975
58976 case HOUR :
58977 if(!isNumeric(q)) {
58978 itemOut.enabled = false;
58979 return;
58980 }
58981 q = +q;
58982
58983 if(q < 0 || q > 24) { // accept 24
58984 itemOut.enabled = false;
58985 return;
58986 }
58987 // use number
58988 itemOut.bounds[i] = bnds[i] = q;
58989 break;
58990 }
58991 }
58992 }
58993
58994 if(containerOut.autorange === false) {
58995 var rng = containerOut.range;
58996
58997 // if bounds are bigger than the (set) range, disable break
58998 if(rng[0] < rng[1]) {
58999 if(bnds[0] < rng[0] && bnds[1] > rng[1]) {
59000 itemOut.enabled = false;
59001 return;
59002 }
59003 } else if(bnds[0] > rng[0] && bnds[1] < rng[1]) {
59004 itemOut.enabled = false;
59005 return;
59006 }
59007 }
59008 } else {
59009 var values = coerce('values');
59010
59011 if(values && values.length) {
59012 coerce('dvalue');
59013 } else {
59014 itemOut.enabled = false;
59015 return;
59016 }
59017 }
59018 }
59019}
59020
59021// these numbers are one more than what bounds would be mapped to
59022var dayStrToNum = {
59023 sun: 1,
59024 mon: 2,
59025 tue: 3,
59026 wed: 4,
59027 thu: 5,
59028 fri: 6,
59029 sat: 7
59030};
59031
59032function indexOfDay(v) {
59033 if(typeof v !== 'string') return;
59034 return dayStrToNum[
59035 v.substr(0, 3).toLowerCase()
59036 ];
59037}
59038
59039},{"../../lib":177,"../../registry":272,"../array_container_defaults":218,"./category_order_defaults":226,"./constants":228,"./layout_attributes":236,"./line_grid_defaults":238,"./set_convert":242,"./tick_label_defaults":243,"./tick_mark_defaults":244,"./tick_value_defaults":245,"fast-isnumeric":15}],225:[function(_dereq_,module,exports){
59040/**
59041* Copyright 2012-2020, Plotly, Inc.
59042* All rights reserved.
59043*
59044* This source code is licensed under the MIT license found in the
59045* LICENSE file in the root directory of this source tree.
59046*/
59047
59048'use strict';
59049
59050var Registry = _dereq_('../../registry');
59051
59052var constants = _dereq_('./constants');
59053
59054
59055// convert between axis names (xaxis, xaxis2, etc, elements of gd.layout)
59056// and axis id's (x, x2, etc). Would probably have ditched 'xaxis'
59057// completely in favor of just 'x' if it weren't ingrained in the API etc.
59058exports.id2name = function id2name(id) {
59059 if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
59060 var axNum = id.substr(1);
59061 if(axNum === '1') axNum = '';
59062 return id.charAt(0) + 'axis' + axNum;
59063};
59064
59065exports.name2id = function name2id(name) {
59066 if(!name.match(constants.AX_NAME_PATTERN)) return;
59067 var axNum = name.substr(5);
59068 if(axNum === '1') axNum = '';
59069 return name.charAt(0) + axNum;
59070};
59071
59072exports.cleanId = function cleanId(id, axLetter) {
59073 if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
59074 if(axLetter && id.charAt(0) !== axLetter) return;
59075
59076 var axNum = id.substr(1).replace(/^0+/, '');
59077 if(axNum === '1') axNum = '';
59078 return id.charAt(0) + axNum;
59079};
59080
59081// get all axis objects, as restricted in listNames
59082exports.list = function(gd, axLetter, only2d) {
59083 var fullLayout = gd._fullLayout;
59084 if(!fullLayout) return [];
59085
59086 var idList = exports.listIds(gd, axLetter);
59087 var out = new Array(idList.length);
59088 var i;
59089
59090 for(i = 0; i < idList.length; i++) {
59091 var idi = idList[i];
59092 out[i] = fullLayout[idi.charAt(0) + 'axis' + idi.substr(1)];
59093 }
59094
59095 if(!only2d) {
59096 var sceneIds3D = fullLayout._subplots.gl3d || [];
59097
59098 for(i = 0; i < sceneIds3D.length; i++) {
59099 var scene = fullLayout[sceneIds3D[i]];
59100
59101 if(axLetter) out.push(scene[axLetter + 'axis']);
59102 else out.push(scene.xaxis, scene.yaxis, scene.zaxis);
59103 }
59104 }
59105
59106 return out;
59107};
59108
59109// get all axis ids, optionally restricted by letter
59110// this only makes sense for 2d axes
59111exports.listIds = function(gd, axLetter) {
59112 var fullLayout = gd._fullLayout;
59113 if(!fullLayout) return [];
59114
59115 var subplotLists = fullLayout._subplots;
59116 if(axLetter) return subplotLists[axLetter + 'axis'];
59117 return subplotLists.xaxis.concat(subplotLists.yaxis);
59118};
59119
59120// get an axis object from its id 'x','x2' etc
59121// optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it
59122exports.getFromId = function(gd, id, type) {
59123 var fullLayout = gd._fullLayout;
59124
59125 if(type === 'x') id = id.replace(/y[0-9]*/, '');
59126 else if(type === 'y') id = id.replace(/x[0-9]*/, '');
59127
59128 return fullLayout[exports.id2name(id)];
59129};
59130
59131// get an axis object of specified type from the containing trace
59132exports.getFromTrace = function(gd, fullTrace, type) {
59133 var fullLayout = gd._fullLayout;
59134 var ax = null;
59135
59136 if(Registry.traceIs(fullTrace, 'gl3d')) {
59137 var scene = fullTrace.scene;
59138 if(scene.substr(0, 5) === 'scene') {
59139 ax = fullLayout[scene][type + 'axis'];
59140 }
59141 } else {
59142 ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
59143 }
59144
59145 return ax;
59146};
59147
59148// sort x, x2, x10, y, y2, y10...
59149exports.idSort = function(id1, id2) {
59150 var letter1 = id1.charAt(0);
59151 var letter2 = id2.charAt(0);
59152 if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1;
59153 return +(id1.substr(1) || 1) - +(id2.substr(1) || 1);
59154};
59155
59156exports.getAxisGroup = function getAxisGroup(fullLayout, axId) {
59157 var matchGroups = fullLayout._axisMatchGroups;
59158
59159 for(var i = 0; i < matchGroups.length; i++) {
59160 var group = matchGroups[i];
59161 if(group[axId]) return 'g' + i;
59162 }
59163 return axId;
59164};
59165
59166},{"../../registry":272,"./constants":228}],226:[function(_dereq_,module,exports){
59167/**
59168* Copyright 2012-2020, Plotly, Inc.
59169* All rights reserved.
59170*
59171* This source code is licensed under the MIT license found in the
59172* LICENSE file in the root directory of this source tree.
59173*/
59174
59175'use strict';
59176
59177function findCategories(ax, opts) {
59178 var dataAttr = opts.dataAttr || ax._id.charAt(0);
59179 var lookup = {};
59180 var axData;
59181 var i, j;
59182
59183 if(opts.axData) {
59184 // non-x/y case
59185 axData = opts.axData;
59186 } else {
59187 // x/y case
59188 axData = [];
59189 for(i = 0; i < opts.data.length; i++) {
59190 var trace = opts.data[i];
59191 if(trace[dataAttr + 'axis'] === ax._id) {
59192 axData.push(trace);
59193 }
59194 }
59195 }
59196
59197 for(i = 0; i < axData.length; i++) {
59198 var vals = axData[i][dataAttr];
59199 for(j = 0; j < vals.length; j++) {
59200 var v = vals[j];
59201 if(v !== null && v !== undefined) {
59202 lookup[v] = 1;
59203 }
59204 }
59205 }
59206
59207 return Object.keys(lookup);
59208}
59209
59210/**
59211 * Fills in category* default and initial categories.
59212 *
59213 * @param {object} containerIn : input axis object
59214 * @param {object} containerOut : full axis object
59215 * @param {function} coerce : Lib.coerce fn wrapper
59216 * @param {object} opts :
59217 * - data {array} : (full) data trace
59218 * OR
59219 * - axData {array} : (full) data associated with axis being coerced here
59220 * - dataAttr {string} : attribute name corresponding to coordinate array
59221 */
59222module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce, opts) {
59223 if(containerOut.type !== 'category') return;
59224
59225 var arrayIn = containerIn.categoryarray;
59226 var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
59227
59228 // override default 'categoryorder' value when non-empty array is supplied
59229 var orderDefault;
59230 if(isValidArray) orderDefault = 'array';
59231
59232 var order = coerce('categoryorder', orderDefault);
59233 var array;
59234
59235 // coerce 'categoryarray' only in array order case
59236 if(order === 'array') {
59237 array = coerce('categoryarray');
59238 }
59239
59240 // cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
59241 if(!isValidArray && order === 'array') {
59242 order = containerOut.categoryorder = 'trace';
59243 }
59244
59245 // set up things for makeCalcdata
59246 if(order === 'trace') {
59247 containerOut._initialCategories = [];
59248 } else if(order === 'array') {
59249 containerOut._initialCategories = array.slice();
59250 } else {
59251 array = findCategories(containerOut, opts).sort();
59252 if(order === 'category ascending') {
59253 containerOut._initialCategories = array;
59254 } else if(order === 'category descending') {
59255 containerOut._initialCategories = array.reverse();
59256 }
59257 }
59258};
59259
59260},{}],227:[function(_dereq_,module,exports){
59261/**
59262* Copyright 2012-2020, Plotly, Inc.
59263* All rights reserved.
59264*
59265* This source code is licensed under the MIT license found in the
59266* LICENSE file in the root directory of this source tree.
59267*/
59268
59269'use strict';
59270
59271var isNumeric = _dereq_('fast-isnumeric');
59272var Lib = _dereq_('../../lib');
59273var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
59274
59275/**
59276 * Return a validated dtick value for this axis
59277 *
59278 * @param {any} dtick: the candidate dtick. valid values are numbers and strings,
59279 * and further constrained depending on the axis type.
59280 * @param {string} axType: the axis type
59281 */
59282exports.dtick = function(dtick, axType) {
59283 var isLog = axType === 'log';
59284 var isDate = axType === 'date';
59285 var isCat = axType === 'category';
59286 var dtickDflt = isDate ? ONEDAY : 1;
59287
59288 if(!dtick) return dtickDflt;
59289
59290 if(isNumeric(dtick)) {
59291 dtick = Number(dtick);
59292 if(dtick <= 0) return dtickDflt;
59293 if(isCat) {
59294 // category dtick must be positive integers
59295 return Math.max(1, Math.round(dtick));
59296 }
59297 if(isDate) {
59298 // date dtick must be at least 0.1ms (our current precision)
59299 return Math.max(0.1, dtick);
59300 }
59301 return dtick;
59302 }
59303
59304 if(typeof dtick !== 'string' || !(isDate || isLog)) {
59305 return dtickDflt;
59306 }
59307
59308 var prefix = dtick.charAt(0);
59309 var dtickNum = dtick.substr(1);
59310 dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0;
59311
59312 if((dtickNum <= 0) || !(
59313 // "M<n>" gives ticks every (integer) n months
59314 (isDate && prefix === 'M' && dtickNum === Math.round(dtickNum)) ||
59315 // "L<f>" gives ticks linearly spaced in data (not in position) every (float) f
59316 (isLog && prefix === 'L') ||
59317 // "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5
59318 (isLog && prefix === 'D' && (dtickNum === 1 || dtickNum === 2))
59319 )) {
59320 return dtickDflt;
59321 }
59322
59323 return dtick;
59324};
59325
59326/**
59327 * Return a validated tick0 for this axis
59328 *
59329 * @param {any} tick0: the candidate tick0. Valid values are numbers and strings,
59330 * further constrained depending on the axis type
59331 * @param {string} axType: the axis type
59332 * @param {string} calendar: for date axes, the calendar to validate/convert with
59333 * @param {any} dtick: an already valid dtick. Only used for D1 and D2 log dticks,
59334 * which do not support tick0 at all.
59335 */
59336exports.tick0 = function(tick0, axType, calendar, dtick) {
59337 if(axType === 'date') {
59338 return Lib.cleanDate(tick0, Lib.dateTick0(calendar));
59339 }
59340 if(dtick === 'D1' || dtick === 'D2') {
59341 // D1 and D2 modes ignore tick0 entirely
59342 return undefined;
59343 }
59344 // Aside from date axes, tick0 must be numeric
59345 return isNumeric(tick0) ? Number(tick0) : 0;
59346};
59347
59348},{"../../constants/numerical":155,"../../lib":177,"fast-isnumeric":15}],228:[function(_dereq_,module,exports){
59349/**
59350* Copyright 2012-2020, Plotly, Inc.
59351* All rights reserved.
59352*
59353* This source code is licensed under the MIT license found in the
59354* LICENSE file in the root directory of this source tree.
59355*/
59356
59357'use strict';
59358
59359var counterRegex = _dereq_('../../lib/regex').counter;
59360
59361module.exports = {
59362 idRegex: {
59363 x: counterRegex('x'),
59364 y: counterRegex('y')
59365 },
59366
59367 attrRegex: counterRegex('[xy]axis'),
59368
59369 // axis match regular expression
59370 xAxisMatch: counterRegex('xaxis'),
59371 yAxisMatch: counterRegex('yaxis'),
59372
59373 // pattern matching axis ids and names
59374 // note that this is more permissive than counterRegex, as
59375 // id2name, name2id, and cleanId accept "x1" etc
59376 AX_ID_PATTERN: /^[xyz][0-9]*$/,
59377 AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/,
59378
59379 // and for 2D subplots
59380 SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/,
59381
59382 HOUR_PATTERN: 'hour',
59383 WEEKDAY_PATTERN: 'day of week',
59384
59385 // pixels to move mouse before you stop clamping to starting point
59386 MINDRAG: 8,
59387
59388 // smallest dimension allowed for a select box
59389 MINSELECT: 12,
59390
59391 // smallest dimension allowed for a zoombox
59392 MINZOOM: 20,
59393
59394 // width of axis drag regions
59395 DRAGGERSIZE: 20,
59396
59397 // max pixels off straight before a lasso select line counts as bent
59398 BENDPX: 1.5,
59399
59400 // delay before a redraw (relayout) after smooth panning and zooming
59401 REDRAWDELAY: 50,
59402
59403 // throttling limit (ms) for selectPoints calls
59404 SELECTDELAY: 100,
59405
59406 // cache ID suffix for throttle
59407 SELECTID: '-select',
59408
59409 // last resort axis ranges for x and y axes if we have no data
59410 DFLTRANGEX: [-1, 6],
59411 DFLTRANGEY: [-1, 4],
59412
59413 // Layers to keep trace types in the right order
59414 // N.B. each 'unique' plot method must have its own layer
59415 traceLayerClasses: [
59416 'imagelayer',
59417 'heatmaplayer',
59418 'contourcarpetlayer', 'contourlayer',
59419 'funnellayer', 'waterfalllayer', 'barlayer',
59420 'carpetlayer',
59421 'violinlayer',
59422 'boxlayer',
59423 'ohlclayer',
59424 'scattercarpetlayer', 'scatterlayer'
59425 ],
59426
59427 clipOnAxisFalseQuery: [
59428 '.scatterlayer',
59429 '.barlayer',
59430 '.funnellayer',
59431 '.waterfalllayer'
59432 ],
59433
59434 layerValue2layerClass: {
59435 'above traces': 'above',
59436 'below traces': 'below'
59437 }
59438};
59439
59440},{"../../lib/regex":192}],229:[function(_dereq_,module,exports){
59441/**
59442* Copyright 2012-2020, Plotly, Inc.
59443* All rights reserved.
59444*
59445* This source code is licensed under the MIT license found in the
59446* LICENSE file in the root directory of this source tree.
59447*/
59448
59449'use strict';
59450
59451var Lib = _dereq_('../../lib');
59452var id2name = _dereq_('./axis_ids').id2name;
59453var scaleZoom = _dereq_('./scale_zoom');
59454var makePadFn = _dereq_('./autorange').makePadFn;
59455var concatExtremes = _dereq_('./autorange').concatExtremes;
59456
59457var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL;
59458var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
59459
59460exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, opts) {
59461 var allAxisIds = opts.allAxisIds;
59462 var layoutOut = opts.layoutOut;
59463 var scaleanchorDflt = opts.scaleanchorDflt;
59464 var constrainDflt = opts.constrainDflt;
59465 var constraintGroups = layoutOut._axisConstraintGroups;
59466 var matchGroups = layoutOut._axisMatchGroups;
59467 var axId = containerOut._id;
59468 var axLetter = axId.charAt(0);
59469 var splomStash = ((layoutOut._splomAxes || {})[axLetter] || {})[axId] || {};
59470 var thisID = containerOut._id;
59471 var letter = thisID.charAt(0);
59472
59473 // coerce the constraint mechanics even if this axis has no scaleanchor
59474 // because it may be the anchor of another axis.
59475 var constrain = coerce('constrain', constrainDflt);
59476 Lib.coerce(containerIn, containerOut, {
59477 constraintoward: {
59478 valType: 'enumerated',
59479 values: letter === 'x' ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'],
59480 dflt: letter === 'x' ? 'center' : 'middle'
59481 }
59482 }, 'constraintoward');
59483
59484 var matches, matchOpts;
59485
59486 if((containerIn.matches || splomStash.matches) && !containerOut.fixedrange) {
59487 matchOpts = getConstraintOpts(matchGroups, thisID, allAxisIds, layoutOut);
59488 matches = Lib.coerce(containerIn, containerOut, {
59489 matches: {
59490 valType: 'enumerated',
59491 values: matchOpts.linkableAxes || [],
59492 dflt: splomStash.matches
59493 }
59494 }, 'matches');
59495 }
59496
59497 // 'matches' wins over 'scaleanchor' (for now)
59498 var scaleanchor, scaleOpts;
59499
59500 if(!matches &&
59501 !(containerOut.fixedrange && constrain !== 'domain') &&
59502 (containerIn.scaleanchor || scaleanchorDflt)
59503 ) {
59504 scaleOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut, constrain);
59505 scaleanchor = Lib.coerce(containerIn, containerOut, {
59506 scaleanchor: {
59507 valType: 'enumerated',
59508 values: scaleOpts.linkableAxes || []
59509 }
59510 }, 'scaleanchor', scaleanchorDflt);
59511 }
59512
59513 if(matches) {
59514 delete containerOut.constrain;
59515 updateConstraintGroups(matchGroups, matchOpts.thisGroup, thisID, matches, 1);
59516 } else if(allAxisIds.indexOf(containerIn.matches) !== -1) {
59517 Lib.warn('ignored ' + containerOut._name + '.matches: "' +
59518 containerIn.matches + '" to avoid either an infinite loop ' +
59519 'or because the target axis has fixed range.');
59520 }
59521
59522 if(scaleanchor) {
59523 var scaleratio = coerce('scaleratio');
59524
59525 // TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
59526 // but that seems hacky. Better way to say "must be a positive number"?
59527 // Of course if you use several super-tiny values you could eventually
59528 // force a product of these to zero and all hell would break loose...
59529 // Likewise with super-huge values.
59530 if(!scaleratio) scaleratio = containerOut.scaleratio = 1;
59531
59532 updateConstraintGroups(constraintGroups, scaleOpts.thisGroup, thisID, scaleanchor, scaleratio);
59533 } else if(allAxisIds.indexOf(containerIn.scaleanchor) !== -1) {
59534 Lib.warn('ignored ' + containerOut._name + '.scaleanchor: "' +
59535 containerIn.scaleanchor + '" to avoid either an infinite loop ' +
59536 'and possibly inconsistent scaleratios, or because the target ' +
59537 'axis has fixed range or this axis declares a *matches* constraint.');
59538 }
59539};
59540
59541// If this axis is already part of a constraint group, we can't
59542// scaleanchor any other axis in that group, or we'd make a loop.
59543// Filter allAxisIds to enforce this, also matching axis types.
59544function getConstraintOpts(groups, thisID, allAxisIds, layoutOut, constrain) {
59545 var doesNotConstrainRange = constrain !== 'range';
59546 var thisType = layoutOut[id2name(thisID)].type;
59547 var i, j, idj, axj;
59548
59549 var linkableAxes = [];
59550 for(j = 0; j < allAxisIds.length; j++) {
59551 idj = allAxisIds[j];
59552 if(idj === thisID) continue;
59553
59554 axj = layoutOut[id2name(idj)];
59555 if(axj.type === thisType) {
59556 if(!axj.fixedrange) {
59557 linkableAxes.push(idj);
59558 } else if(doesNotConstrainRange && axj.anchor) {
59559 // allow domain constraints on subplots where
59560 // BOTH axes have fixedrange:true and constrain:domain
59561 var counterAxj = layoutOut[id2name(axj.anchor)];
59562 if(counterAxj.fixedrange) {
59563 linkableAxes.push(idj);
59564 }
59565 }
59566 }
59567 }
59568
59569 for(i = 0; i < groups.length; i++) {
59570 if(groups[i][thisID]) {
59571 var thisGroup = groups[i];
59572
59573 var linkableAxesNoLoops = [];
59574 for(j = 0; j < linkableAxes.length; j++) {
59575 idj = linkableAxes[j];
59576 if(!thisGroup[idj]) linkableAxesNoLoops.push(idj);
59577 }
59578 return {linkableAxes: linkableAxesNoLoops, thisGroup: thisGroup};
59579 }
59580 }
59581
59582 return {linkableAxes: linkableAxes, thisGroup: null};
59583}
59584
59585/*
59586 * Add this axis to the axis constraint groups, which is the collection
59587 * of axes that are all constrained together on scale.
59588 *
59589 * constraintGroups: a list of objects. each object is
59590 * {axis_id: scale_within_group}, where scale_within_group is
59591 * only important relative to the rest of the group, and defines
59592 * the relative scales between all axes in the group
59593 *
59594 * thisGroup: the group the current axis is already in
59595 * thisID: the id if the current axis
59596 * scaleanchor: the id of the axis to scale it with
59597 * scaleratio: the ratio of this axis to the scaleanchor axis
59598 */
59599function updateConstraintGroups(constraintGroups, thisGroup, thisID, scaleanchor, scaleratio) {
59600 var i, j, groupi, keyj, thisGroupIndex;
59601
59602 if(thisGroup === null) {
59603 thisGroup = {};
59604 thisGroup[thisID] = 1;
59605 thisGroupIndex = constraintGroups.length;
59606 constraintGroups.push(thisGroup);
59607 } else {
59608 thisGroupIndex = constraintGroups.indexOf(thisGroup);
59609 }
59610
59611 var thisGroupKeys = Object.keys(thisGroup);
59612
59613 // we know that this axis isn't in any other groups, but we don't know
59614 // about the scaleanchor axis. If it is, we need to merge the groups.
59615 for(i = 0; i < constraintGroups.length; i++) {
59616 groupi = constraintGroups[i];
59617 if(i !== thisGroupIndex && groupi[scaleanchor]) {
59618 var baseScale = groupi[scaleanchor];
59619 for(j = 0; j < thisGroupKeys.length; j++) {
59620 keyj = thisGroupKeys[j];
59621 groupi[keyj] = baseScale * scaleratio * thisGroup[keyj];
59622 }
59623 constraintGroups.splice(thisGroupIndex, 1);
59624 return;
59625 }
59626 }
59627
59628 // otherwise, we insert the new scaleanchor axis as the base scale (1)
59629 // in its group, and scale the rest of the group to it
59630 if(scaleratio !== 1) {
59631 for(j = 0; j < thisGroupKeys.length; j++) {
59632 thisGroup[thisGroupKeys[j]] *= scaleratio;
59633 }
59634 }
59635 thisGroup[scaleanchor] = 1;
59636}
59637
59638exports.enforce = function enforce(gd) {
59639 var fullLayout = gd._fullLayout;
59640 var constraintGroups = fullLayout._axisConstraintGroups || [];
59641
59642 var i, j, axisID, ax, normScale, mode, factor;
59643
59644 for(i = 0; i < constraintGroups.length; i++) {
59645 var group = constraintGroups[i];
59646 var axisIDs = Object.keys(group);
59647
59648 var minScale = Infinity;
59649 var maxScale = 0;
59650 // mostly matchScale will be the same as minScale
59651 // ie we expand axis ranges to encompass *everything*
59652 // that's currently in any of their ranges, but during
59653 // autorange of a subset of axes we will ignore other
59654 // axes for this purpose.
59655 var matchScale = Infinity;
59656 var normScales = {};
59657 var axes = {};
59658 var hasAnyDomainConstraint = false;
59659
59660 // find the (normalized) scale of each axis in the group
59661 for(j = 0; j < axisIDs.length; j++) {
59662 axisID = axisIDs[j];
59663 axes[axisID] = ax = fullLayout[id2name(axisID)];
59664
59665 if(ax._inputDomain) ax.domain = ax._inputDomain.slice();
59666 else ax._inputDomain = ax.domain.slice();
59667
59668 if(!ax._inputRange) ax._inputRange = ax.range.slice();
59669
59670 // set axis scale here so we can use _m rather than
59671 // having to calculate it from length and range
59672 ax.setScale();
59673
59674 // abs: inverted scales still satisfy the constraint
59675 normScales[axisID] = normScale = Math.abs(ax._m) / group[axisID];
59676 minScale = Math.min(minScale, normScale);
59677 if(ax.constrain === 'domain' || !ax._constraintShrinkable) {
59678 matchScale = Math.min(matchScale, normScale);
59679 }
59680
59681 // this has served its purpose, so remove it
59682 delete ax._constraintShrinkable;
59683 maxScale = Math.max(maxScale, normScale);
59684
59685 if(ax.constrain === 'domain') hasAnyDomainConstraint = true;
59686 }
59687
59688 // Do we have a constraint mismatch? Give a small buffer for rounding errors
59689 if(minScale > ALMOST_EQUAL * maxScale && !hasAnyDomainConstraint) continue;
59690
59691 // now increase any ranges we need to until all normalized scales are equal
59692 for(j = 0; j < axisIDs.length; j++) {
59693 axisID = axisIDs[j];
59694 normScale = normScales[axisID];
59695 ax = axes[axisID];
59696 mode = ax.constrain;
59697
59698 // even if the scale didn't change, if we're shrinking domain
59699 // we need to recalculate in case `constraintoward` changed
59700 if(normScale !== matchScale || mode === 'domain') {
59701 factor = normScale / matchScale;
59702
59703 if(mode === 'range') {
59704 scaleZoom(ax, factor);
59705 } else {
59706 // mode === 'domain'
59707
59708 var inputDomain = ax._inputDomain;
59709 var domainShrunk = (ax.domain[1] - ax.domain[0]) /
59710 (inputDomain[1] - inputDomain[0]);
59711 var rangeShrunk = (ax.r2l(ax.range[1]) - ax.r2l(ax.range[0])) /
59712 (ax.r2l(ax._inputRange[1]) - ax.r2l(ax._inputRange[0]));
59713
59714 factor /= domainShrunk;
59715
59716 if(factor * rangeShrunk < 1) {
59717 // we've asked to magnify the axis more than we can just by
59718 // enlarging the domain - so we need to constrict range
59719 ax.domain = ax._input.domain = inputDomain.slice();
59720 scaleZoom(ax, factor);
59721 continue;
59722 }
59723
59724 if(rangeShrunk < 1) {
59725 // the range has previously been constricted by ^^, but we've
59726 // switched to the domain-constricted regime, so reset range
59727 ax.range = ax._input.range = ax._inputRange.slice();
59728 factor *= rangeShrunk;
59729 }
59730
59731 if(ax.autorange) {
59732 /*
59733 * range & factor may need to change because range was
59734 * calculated for the larger scaling, so some pixel
59735 * paddings may get cut off when we reduce the domain.
59736 *
59737 * This is easier than the regular autorange calculation
59738 * because we already know the scaling `m`, but we still
59739 * need to cut out impossible constraints (like
59740 * annotations with super-long arrows). That's what
59741 * outerMin/Max are for - if the expansion was going to
59742 * go beyond the original domain, it must be impossible
59743 */
59744 var rl0 = ax.r2l(ax.range[0]);
59745 var rl1 = ax.r2l(ax.range[1]);
59746 var rangeCenter = (rl0 + rl1) / 2;
59747 var rangeMin = rangeCenter;
59748 var rangeMax = rangeCenter;
59749 var halfRange = Math.abs(rl1 - rangeCenter);
59750 // extra tiny bit for rounding errors, in case we actually
59751 // *are* expanding to the full domain
59752 var outerMin = rangeCenter - halfRange * factor * 1.0001;
59753 var outerMax = rangeCenter + halfRange * factor * 1.0001;
59754 var getPad = makePadFn(ax);
59755
59756 updateDomain(ax, factor);
59757 var m = Math.abs(ax._m);
59758 var extremes = concatExtremes(gd, ax);
59759 var minArray = extremes.min;
59760 var maxArray = extremes.max;
59761 var newVal;
59762 var k;
59763
59764 for(k = 0; k < minArray.length; k++) {
59765 newVal = minArray[k].val - getPad(minArray[k]) / m;
59766 if(newVal > outerMin && newVal < rangeMin) {
59767 rangeMin = newVal;
59768 }
59769 }
59770
59771 for(k = 0; k < maxArray.length; k++) {
59772 newVal = maxArray[k].val + getPad(maxArray[k]) / m;
59773 if(newVal < outerMax && newVal > rangeMax) {
59774 rangeMax = newVal;
59775 }
59776 }
59777
59778 var domainExpand = (rangeMax - rangeMin) / (2 * halfRange);
59779 factor /= domainExpand;
59780
59781 rangeMin = ax.l2r(rangeMin);
59782 rangeMax = ax.l2r(rangeMax);
59783 ax.range = ax._input.range = (rl0 < rl1) ?
59784 [rangeMin, rangeMax] : [rangeMax, rangeMin];
59785 }
59786
59787 updateDomain(ax, factor);
59788 }
59789 }
59790 }
59791 }
59792};
59793
59794// For use before autoranging, check if this axis was previously constrained
59795// by domain but no longer is
59796exports.clean = function clean(gd, ax) {
59797 if(ax._inputDomain) {
59798 var isConstrained = false;
59799 var axId = ax._id;
59800 var constraintGroups = gd._fullLayout._axisConstraintGroups;
59801 for(var j = 0; j < constraintGroups.length; j++) {
59802 if(constraintGroups[j][axId]) {
59803 isConstrained = true;
59804 break;
59805 }
59806 }
59807 if(!isConstrained || ax.constrain !== 'domain') {
59808 ax._input.domain = ax.domain = ax._inputDomain;
59809 delete ax._inputDomain;
59810 }
59811 }
59812};
59813
59814function updateDomain(ax, factor) {
59815 var inputDomain = ax._inputDomain;
59816 var centerFraction = FROM_BL[ax.constraintoward];
59817 var center = inputDomain[0] + (inputDomain[1] - inputDomain[0]) * centerFraction;
59818
59819 ax.domain = ax._input.domain = [
59820 center + (inputDomain[0] - center) / factor,
59821 center + (inputDomain[1] - center) / factor
59822 ];
59823 ax.setScale();
59824}
59825
59826},{"../../constants/alignment":152,"../../constants/numerical":155,"../../lib":177,"./autorange":221,"./axis_ids":225,"./scale_zoom":240}],230:[function(_dereq_,module,exports){
59827/**
59828* Copyright 2012-2020, Plotly, Inc.
59829* All rights reserved.
59830*
59831* This source code is licensed under the MIT license found in the
59832* LICENSE file in the root directory of this source tree.
59833*/
59834
59835'use strict';
59836
59837var d3 = _dereq_('d3');
59838var tinycolor = _dereq_('tinycolor2');
59839var supportsPassive = _dereq_('has-passive-events');
59840
59841var Registry = _dereq_('../../registry');
59842var Lib = _dereq_('../../lib');
59843var svgTextUtils = _dereq_('../../lib/svg_text_utils');
59844var Color = _dereq_('../../components/color');
59845var Drawing = _dereq_('../../components/drawing');
59846var Fx = _dereq_('../../components/fx');
59847var Axes = _dereq_('./axes');
59848var setCursor = _dereq_('../../lib/setcursor');
59849var dragElement = _dereq_('../../components/dragelement');
59850var helpers = _dereq_('../../components/dragelement/helpers');
59851var selectingOrDrawing = helpers.selectingOrDrawing;
59852var freeMode = helpers.freeMode;
59853
59854var FROM_TL = _dereq_('../../constants/alignment').FROM_TL;
59855var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
59856var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
59857
59858var Plots = _dereq_('../plots');
59859
59860var getFromId = _dereq_('./axis_ids').getFromId;
59861var prepSelect = _dereq_('./select').prepSelect;
59862var clearSelect = _dereq_('./select').clearSelect;
59863var selectOnClick = _dereq_('./select').selectOnClick;
59864var scaleZoom = _dereq_('./scale_zoom');
59865
59866var constants = _dereq_('./constants');
59867var MINDRAG = constants.MINDRAG;
59868var MINZOOM = constants.MINZOOM;
59869
59870// flag for showing "doubleclick to zoom out" only at the beginning
59871var SHOWZOOMOUTTIP = true;
59872
59873// dragBox: create an element to drag one or more axis ends
59874// inputs:
59875// plotinfo - which subplot are we making dragboxes on?
59876// x,y,w,h - left, top, width, height of the box
59877// ns - how does this drag the vertical axis?
59878// 'n' - top only
59879// 's' - bottom only
59880// 'ns' - top and bottom together, difference unchanged
59881// ew - same for horizontal axis
59882function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
59883 // mouseDown stores ms of first mousedown event in the last
59884 // `gd._context.doubleClickDelay` ms on the drag bars
59885 // numClicks stores how many mousedowns have been seen
59886 // within `gd._context.doubleClickDelay` so we can check for click or doubleclick events
59887 // dragged stores whether a drag has occurred, so we don't have to
59888 // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px
59889 var zoomlayer = gd._fullLayout._zoomlayer;
59890 var isMainDrag = (ns + ew === 'nsew');
59891 var singleEnd = (ns + ew).length === 1;
59892
59893 // main subplot x and y (i.e. found in plotinfo - the main ones)
59894 var xa0, ya0;
59895 // {ax._id: ax} hash objects
59896 var xaHash, yaHash;
59897 // xaHash/yaHash values (arrays)
59898 var xaxes, yaxes;
59899 // main axis offsets
59900 var xs, ys;
59901 // main axis lengths
59902 var pw, ph;
59903 // contains keys 'xaHash', 'yaHash', 'xaxes', and 'yaxes'
59904 // which are the x/y {ax._id: ax} hash objects and their values
59905 // for linked axis relative to this subplot
59906 var links;
59907 // similar to `links` but for matching axes
59908 var matches;
59909 // set to ew/ns val when active, set to '' when inactive
59910 var xActive, yActive;
59911 // are all axes in this subplot are fixed?
59912 var allFixedRanges;
59913 // do we need to edit x/y ranges?
59914 var editX, editY;
59915 // graph-wide optimization flags
59916 var hasScatterGl, hasSplom, hasSVG;
59917 // collected changes to be made to the plot by relayout at the end
59918 var updates;
59919
59920 function recomputeAxisLists() {
59921 xa0 = plotinfo.xaxis;
59922 ya0 = plotinfo.yaxis;
59923 pw = xa0._length;
59924 ph = ya0._length;
59925 xs = xa0._offset;
59926 ys = ya0._offset;
59927
59928 xaHash = {};
59929 xaHash[xa0._id] = xa0;
59930 yaHash = {};
59931 yaHash[ya0._id] = ya0;
59932
59933 // if we're dragging two axes at once, also drag overlays
59934 if(ns && ew) {
59935 var overlays = plotinfo.overlays;
59936 for(var i = 0; i < overlays.length; i++) {
59937 var xa = overlays[i].xaxis;
59938 xaHash[xa._id] = xa;
59939 var ya = overlays[i].yaxis;
59940 yaHash[ya._id] = ya;
59941 }
59942 }
59943
59944 xaxes = hashValues(xaHash);
59945 yaxes = hashValues(yaHash);
59946 xActive = isDirectionActive(xaxes, ew);
59947 yActive = isDirectionActive(yaxes, ns);
59948 allFixedRanges = !yActive && !xActive;
59949
59950 links = calcLinks(gd, gd._fullLayout._axisConstraintGroups, xaHash, yaHash);
59951 matches = calcLinks(gd, gd._fullLayout._axisMatchGroups, xaHash, yaHash);
59952 editX = ew || links.isSubplotConstrained || matches.isSubplotConstrained;
59953 editY = ns || links.isSubplotConstrained || matches.isSubplotConstrained;
59954
59955 var fullLayout = gd._fullLayout;
59956 hasScatterGl = fullLayout._has('scattergl');
59957 hasSplom = fullLayout._has('splom');
59958 hasSVG = fullLayout._has('svg');
59959 }
59960
59961 recomputeAxisLists();
59962
59963 var cursor = getDragCursor(yActive + xActive, gd._fullLayout.dragmode, isMainDrag);
59964 var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h);
59965
59966 // still need to make the element if the axes are disabled
59967 // but nuke its events (except for maindrag which needs them for hover)
59968 // and stop there
59969 if(allFixedRanges && !isMainDrag) {
59970 dragger.onmousedown = null;
59971 dragger.style.pointerEvents = 'none';
59972 return dragger;
59973 }
59974
59975 var dragOptions = {
59976 element: dragger,
59977 gd: gd,
59978 plotinfo: plotinfo
59979 };
59980
59981 dragOptions.prepFn = function(e, startX, startY) {
59982 var dragModePrev = dragOptions.dragmode;
59983 var dragModeNow = gd._fullLayout.dragmode;
59984 if(dragModeNow !== dragModePrev) {
59985 dragOptions.dragmode = dragModeNow;
59986 }
59987
59988 recomputeAxisLists();
59989
59990 if(!allFixedRanges) {
59991 if(isMainDrag) {
59992 // main dragger handles all drag modes, and changes
59993 // to pan (or to zoom if it already is pan) on shift
59994 if(e.shiftKey) {
59995 if(dragModeNow === 'pan') dragModeNow = 'zoom';
59996 else if(!selectingOrDrawing(dragModeNow)) dragModeNow = 'pan';
59997 } else if(e.ctrlKey) {
59998 dragModeNow = 'pan';
59999 }
60000 } else {
60001 // all other draggers just pan
60002 dragModeNow = 'pan';
60003 }
60004 }
60005
60006 if(freeMode(dragModeNow)) dragOptions.minDrag = 1;
60007 else dragOptions.minDrag = undefined;
60008
60009 if(selectingOrDrawing(dragModeNow)) {
60010 dragOptions.xaxes = xaxes;
60011 dragOptions.yaxes = yaxes;
60012 // this attaches moveFn, clickFn, doneFn on dragOptions
60013 prepSelect(e, startX, startY, dragOptions, dragModeNow);
60014 } else {
60015 dragOptions.clickFn = clickFn;
60016 if(selectingOrDrawing(dragModePrev)) {
60017 // TODO Fix potential bug
60018 // Note: clearing / resetting selection state only happens, when user
60019 // triggers at least one interaction in pan/zoom mode. Otherwise, the
60020 // select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection
60021 // cache isn't cleared. So when the user switches back to select/lasso and
60022 // 'adds to a selection' with Shift, the "old", seemingly removed outlines
60023 // are redrawn again because the selection cache still holds their coordinates.
60024 // However, this isn't easily solved, since plots.js would need
60025 // to have a reference to the dragOptions object (which holds the
60026 // selection cache).
60027 clearAndResetSelect();
60028 }
60029
60030 if(!allFixedRanges) {
60031 if(dragModeNow === 'zoom') {
60032 dragOptions.moveFn = zoomMove;
60033 dragOptions.doneFn = zoomDone;
60034
60035 // zoomMove takes care of the threshold, but we need to
60036 // minimize this so that constrained zoom boxes will flip
60037 // orientation at the right place
60038 dragOptions.minDrag = 1;
60039
60040 zoomPrep(e, startX, startY);
60041 } else if(dragModeNow === 'pan') {
60042 dragOptions.moveFn = plotDrag;
60043 dragOptions.doneFn = dragTail;
60044 }
60045 }
60046 }
60047
60048 gd._fullLayout._redrag = function() {
60049 var dragDataNow = gd._dragdata;
60050
60051 if(dragDataNow && dragDataNow.element === dragger) {
60052 var dragModeNow = gd._fullLayout.dragmode;
60053
60054 if(!selectingOrDrawing(dragModeNow)) {
60055 recomputeAxisLists();
60056 updateSubplots([0, 0, pw, ph]);
60057 dragOptions.moveFn(dragDataNow.dx, dragDataNow.dy);
60058 }
60059
60060 // TODO should we try to "re-select" under select/lasso modes?
60061 // probably best to wait for https://github.com/plotly/plotly.js/issues/1851
60062 }
60063 };
60064 };
60065
60066 function clearAndResetSelect() {
60067 // clear selection polygon cache (if any)
60068 dragOptions.plotinfo.selection = false;
60069 // clear selection outlines
60070 clearSelect(gd);
60071 }
60072
60073 function clickFn(numClicks, evt) {
60074 var gd = dragOptions.gd;
60075 if(gd._fullLayout._activeShapeIndex >= 0) {
60076 gd._fullLayout._deactivateShape(gd);
60077 return;
60078 }
60079
60080 var clickmode = gd._fullLayout.clickmode;
60081
60082 removeZoombox(gd);
60083
60084 if(numClicks === 2 && !singleEnd) doubleClick();
60085
60086 if(isMainDrag) {
60087 if(clickmode.indexOf('select') > -1) {
60088 selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions);
60089 }
60090
60091 if(clickmode.indexOf('event') > -1) {
60092 Fx.click(gd, evt, plotinfo.id);
60093 }
60094 } else if(numClicks === 1 && singleEnd) {
60095 var ax = ns ? ya0 : xa0;
60096 var end = (ns === 's' || ew === 'w') ? 0 : 1;
60097 var attrStr = ax._name + '.range[' + end + ']';
60098 var initialText = getEndText(ax, end);
60099 var hAlign = 'left';
60100 var vAlign = 'middle';
60101
60102 if(ax.fixedrange) return;
60103
60104 if(ns) {
60105 vAlign = (ns === 'n') ? 'top' : 'bottom';
60106 if(ax.side === 'right') hAlign = 'right';
60107 } else if(ew === 'e') hAlign = 'right';
60108
60109 if(gd._context.showAxisRangeEntryBoxes) {
60110 d3.select(dragger)
60111 .call(svgTextUtils.makeEditable, {
60112 gd: gd,
60113 immediate: true,
60114 background: gd._fullLayout.paper_bgcolor,
60115 text: String(initialText),
60116 fill: ax.tickfont ? ax.tickfont.color : '#444',
60117 horizontalAlign: hAlign,
60118 verticalAlign: vAlign
60119 })
60120 .on('edit', function(text) {
60121 var v = ax.d2r(text);
60122 if(v !== undefined) {
60123 Registry.call('_guiRelayout', gd, attrStr, v);
60124 }
60125 });
60126 }
60127 }
60128 }
60129
60130 dragElement.init(dragOptions);
60131
60132 // x/y px position at start of drag
60133 var x0, y0;
60134 // bbox object of the zoombox
60135 var box;
60136 // luminance of bg behind zoombox
60137 var lum;
60138 // zoombox path outline
60139 var path0;
60140 // is zoombox dimmed (during drag)
60141 var dimmed;
60142 // 'x'-only, 'y' or 'xy' zooming
60143 var zoomMode;
60144 // zoombox d3 selection
60145 var zb;
60146 // zoombox corner d3 selection
60147 var corners;
60148 // zoom takes over minDrag, so it also has to take over gd._dragged
60149 var zoomDragged;
60150
60151 function zoomPrep(e, startX, startY) {
60152 var dragBBox = dragger.getBoundingClientRect();
60153 x0 = startX - dragBBox.left;
60154 y0 = startY - dragBBox.top;
60155 box = {l: x0, r: x0, w: 0, t: y0, b: y0, h: 0};
60156 lum = gd._hmpixcount ?
60157 (gd._hmlumcount / gd._hmpixcount) :
60158 tinycolor(gd._fullLayout.plot_bgcolor).getLuminance();
60159 path0 = 'M0,0H' + pw + 'V' + ph + 'H0V0';
60160 dimmed = false;
60161 zoomMode = 'xy';
60162 zoomDragged = false;
60163 zb = makeZoombox(zoomlayer, lum, xs, ys, path0);
60164 corners = makeCorners(zoomlayer, xs, ys);
60165 }
60166
60167 function zoomMove(dx0, dy0) {
60168 if(gd._transitioningWithDuration) {
60169 return false;
60170 }
60171
60172 var x1 = Math.max(0, Math.min(pw, dx0 + x0));
60173 var y1 = Math.max(0, Math.min(ph, dy0 + y0));
60174 var dx = Math.abs(x1 - x0);
60175 var dy = Math.abs(y1 - y0);
60176
60177 box.l = Math.min(x0, x1);
60178 box.r = Math.max(x0, x1);
60179 box.t = Math.min(y0, y1);
60180 box.b = Math.max(y0, y1);
60181
60182 function noZoom() {
60183 zoomMode = '';
60184 box.r = box.l;
60185 box.t = box.b;
60186 corners.attr('d', 'M0,0Z');
60187 }
60188
60189 if(links.isSubplotConstrained) {
60190 if(dx > MINZOOM || dy > MINZOOM) {
60191 zoomMode = 'xy';
60192 if(dx / pw > dy / ph) {
60193 dy = dx * ph / pw;
60194 if(y0 > y1) box.t = y0 - dy;
60195 else box.b = y0 + dy;
60196 } else {
60197 dx = dy * pw / ph;
60198 if(x0 > x1) box.l = x0 - dx;
60199 else box.r = x0 + dx;
60200 }
60201 corners.attr('d', xyCorners(box));
60202 } else {
60203 noZoom();
60204 }
60205 } else if(matches.isSubplotConstrained) {
60206 if(dx > MINZOOM || dy > MINZOOM) {
60207 zoomMode = 'xy';
60208
60209 var r0 = Math.min(box.l / pw, (ph - box.b) / ph);
60210 var r1 = Math.max(box.r / pw, (ph - box.t) / ph);
60211
60212 box.l = r0 * pw;
60213 box.r = r1 * pw;
60214 box.b = (1 - r0) * ph;
60215 box.t = (1 - r1) * ph;
60216 corners.attr('d', xyCorners(box));
60217 } else {
60218 noZoom();
60219 }
60220 } else if(!yActive || dy < Math.min(Math.max(dx * 0.6, MINDRAG), MINZOOM)) {
60221 // look for small drags in one direction or the other,
60222 // and only drag the other axis
60223
60224 if(dx < MINDRAG || !xActive) {
60225 noZoom();
60226 } else {
60227 box.t = 0;
60228 box.b = ph;
60229 zoomMode = 'x';
60230 corners.attr('d', xCorners(box, y0));
60231 }
60232 } else if(!xActive || dx < Math.min(dy * 0.6, MINZOOM)) {
60233 box.l = 0;
60234 box.r = pw;
60235 zoomMode = 'y';
60236 corners.attr('d', yCorners(box, x0));
60237 } else {
60238 zoomMode = 'xy';
60239 corners.attr('d', xyCorners(box));
60240 }
60241 box.w = box.r - box.l;
60242 box.h = box.b - box.t;
60243
60244 if(zoomMode) zoomDragged = true;
60245 gd._dragged = zoomDragged;
60246
60247 updateZoombox(zb, corners, box, path0, dimmed, lum);
60248 computeZoomUpdates();
60249 gd.emit('plotly_relayouting', updates);
60250 dimmed = true;
60251 }
60252
60253 function computeZoomUpdates() {
60254 updates = {};
60255
60256 // TODO: edit linked axes in zoomAxRanges and in dragTail
60257 if(zoomMode === 'xy' || zoomMode === 'x') {
60258 zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes);
60259 updateMatchedAxRange('x', updates);
60260 }
60261 if(zoomMode === 'xy' || zoomMode === 'y') {
60262 zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes);
60263 updateMatchedAxRange('y', updates);
60264 }
60265 }
60266
60267 function zoomDone() {
60268 computeZoomUpdates();
60269 removeZoombox(gd);
60270 dragTail();
60271 showDoubleClickNotifier(gd);
60272 }
60273
60274 // scroll zoom, on all draggers except corners
60275 var scrollViewBox = [0, 0, pw, ph];
60276 // wait a little after scrolling before redrawing
60277 var redrawTimer = null;
60278 var REDRAWDELAY = constants.REDRAWDELAY;
60279 var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo;
60280
60281 function zoomWheel(e) {
60282 // deactivate mousewheel scrolling on embedded graphs
60283 // devs can override this with layout._enablescrollzoom,
60284 // but _ ensures this setting won't leave their page
60285 if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
60286 return;
60287 }
60288
60289 clearAndResetSelect();
60290
60291 // If a transition is in progress, then disable any behavior:
60292 if(gd._transitioningWithDuration) {
60293 e.preventDefault();
60294 e.stopPropagation();
60295 return;
60296 }
60297
60298 recomputeAxisLists();
60299
60300 clearTimeout(redrawTimer);
60301
60302 var wheelDelta = -e.deltaY;
60303 if(!isFinite(wheelDelta)) wheelDelta = e.wheelDelta / 10;
60304 if(!isFinite(wheelDelta)) {
60305 Lib.log('Did not find wheel motion attributes: ', e);
60306 return;
60307 }
60308
60309 var zoom = Math.exp(-Math.min(Math.max(wheelDelta, -20), 20) / 200);
60310 var gbb = mainplot.draglayer.select('.nsewdrag').node().getBoundingClientRect();
60311 var xfrac = (e.clientX - gbb.left) / gbb.width;
60312 var yfrac = (gbb.bottom - e.clientY) / gbb.height;
60313 var i;
60314
60315 function zoomWheelOneAxis(ax, centerFraction, zoom) {
60316 if(ax.fixedrange) return;
60317
60318 var axRange = Lib.simpleMap(ax.range, ax.r2l);
60319 var v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction;
60320 function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); }
60321 ax.range = axRange.map(doZoom);
60322 }
60323
60324 if(editX) {
60325 // if we're only zooming this axis because of constraints,
60326 // zoom it about the center
60327 if(!ew) xfrac = 0.5;
60328
60329 for(i = 0; i < xaxes.length; i++) {
60330 zoomWheelOneAxis(xaxes[i], xfrac, zoom);
60331 }
60332 updateMatchedAxRange('x');
60333
60334 scrollViewBox[2] *= zoom;
60335 scrollViewBox[0] += scrollViewBox[2] * xfrac * (1 / zoom - 1);
60336 }
60337 if(editY) {
60338 if(!ns) yfrac = 0.5;
60339
60340 for(i = 0; i < yaxes.length; i++) {
60341 zoomWheelOneAxis(yaxes[i], yfrac, zoom);
60342 }
60343 updateMatchedAxRange('y');
60344
60345 scrollViewBox[3] *= zoom;
60346 scrollViewBox[1] += scrollViewBox[3] * (1 - yfrac) * (1 / zoom - 1);
60347 }
60348
60349 // viewbox redraw at first
60350 updateSubplots(scrollViewBox);
60351 ticksAndAnnotations();
60352
60353 gd.emit('plotly_relayouting', updates);
60354
60355 // then replot after a delay to make sure
60356 // no more scrolling is coming
60357 redrawTimer = setTimeout(function() {
60358 scrollViewBox = [0, 0, pw, ph];
60359 dragTail();
60360 }, REDRAWDELAY);
60361
60362 e.preventDefault();
60363 return;
60364 }
60365
60366 // everything but the corners gets wheel zoom
60367 if(ns.length * ew.length !== 1) {
60368 attachWheelEventHandler(dragger, zoomWheel);
60369 }
60370
60371 // plotDrag: move the plot in response to a drag
60372 function plotDrag(dx, dy) {
60373 // If a transition is in progress, then disable any behavior:
60374 if(gd._transitioningWithDuration) {
60375 return;
60376 }
60377
60378 // prevent axis drawing from monkeying with margins until we're done
60379 gd._fullLayout._replotting = true;
60380
60381 if(xActive === 'ew' || yActive === 'ns') {
60382 if(xActive) {
60383 dragAxList(xaxes, dx);
60384 updateMatchedAxRange('x');
60385 }
60386 if(yActive) {
60387 dragAxList(yaxes, dy);
60388 updateMatchedAxRange('y');
60389 }
60390 updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]);
60391 ticksAndAnnotations();
60392 gd.emit('plotly_relayouting', updates);
60393 return;
60394 }
60395
60396 // dz: set a new value for one end (0 or 1) of an axis array axArray,
60397 // and return a pixel shift for that end for the viewbox
60398 // based on pixel drag distance d
60399 // TODO: this makes (generally non-fatal) errors when you get
60400 // near floating point limits
60401 function dz(axArray, end, d) {
60402 var otherEnd = 1 - end;
60403 var movedAx;
60404 var newLinearizedEnd;
60405 for(var i = 0; i < axArray.length; i++) {
60406 var axi = axArray[i];
60407 if(axi.fixedrange) continue;
60408 movedAx = axi;
60409 newLinearizedEnd = axi._rl[otherEnd] +
60410 (axi._rl[end] - axi._rl[otherEnd]) / dZoom(d / axi._length);
60411 var newEnd = axi.l2r(newLinearizedEnd);
60412
60413 // if l2r comes back false or undefined, it means we've dragged off
60414 // the end of valid ranges - so stop.
60415 if(newEnd !== false && newEnd !== undefined) axi.range[end] = newEnd;
60416 }
60417 return movedAx._length * (movedAx._rl[end] - newLinearizedEnd) /
60418 (movedAx._rl[end] - movedAx._rl[otherEnd]);
60419 }
60420
60421 if(links.isSubplotConstrained && xActive && yActive) {
60422 // dragging a corner of a constrained subplot:
60423 // respect the fixed corner, but harmonize dx and dy
60424 var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1;
60425 var dxyFraction = (dx / pw + dxySign * dy / ph) / 2;
60426 dx = dxyFraction * pw;
60427 dy = dxySign * dxyFraction * ph;
60428 }
60429
60430 if(xActive === 'w') dx = dz(xaxes, 0, dx);
60431 else if(xActive === 'e') dx = dz(xaxes, 1, -dx);
60432 else if(!xActive) dx = 0;
60433
60434 if(yActive === 'n') dy = dz(yaxes, 1, dy);
60435 else if(yActive === 's') dy = dz(yaxes, 0, -dy);
60436 else if(!yActive) dy = 0;
60437
60438 var xStart = (xActive === 'w') ? dx : 0;
60439 var yStart = (yActive === 'n') ? dy : 0;
60440
60441 if(links.isSubplotConstrained) {
60442 var i;
60443 if(!xActive && yActive.length === 1) {
60444 // dragging one end of the y axis of a constrained subplot
60445 // scale the other axis the same about its middle
60446 for(i = 0; i < xaxes.length; i++) {
60447 xaxes[i].range = xaxes[i]._r.slice();
60448 scaleZoom(xaxes[i], 1 - dy / ph);
60449 }
60450 dx = dy * pw / ph;
60451 xStart = dx / 2;
60452 }
60453 if(!yActive && xActive.length === 1) {
60454 for(i = 0; i < yaxes.length; i++) {
60455 yaxes[i].range = yaxes[i]._r.slice();
60456 scaleZoom(yaxes[i], 1 - dx / pw);
60457 }
60458 dy = dx * ph / pw;
60459 yStart = dy / 2;
60460 }
60461 }
60462
60463 updateMatchedAxRange('x');
60464 updateMatchedAxRange('y');
60465 updateSubplots([xStart, yStart, pw - dx, ph - dy]);
60466 ticksAndAnnotations();
60467 gd.emit('plotly_relayouting', updates);
60468 }
60469
60470 function updateMatchedAxRange(axLetter, out) {
60471 var matchedAxes = matches.isSubplotConstrained ?
60472 {x: yaxes, y: xaxes}[axLetter] :
60473 matches[axLetter + 'axes'];
60474
60475 var constrainedAxes = matches.isSubplotConstrained ?
60476 {x: xaxes, y: yaxes}[axLetter] :
60477 [];
60478
60479 for(var i = 0; i < matchedAxes.length; i++) {
60480 var ax = matchedAxes[i];
60481 var axId = ax._id;
60482 var axId2 = matches.xLinks[axId] || matches.yLinks[axId];
60483 var ax2 = constrainedAxes[0] || xaHash[axId2] || yaHash[axId2];
60484
60485 if(ax2) {
60486 if(out) {
60487 // zoombox case - don't mutate 'range', just add keys in 'updates'
60488 out[ax._name + '.range[0]'] = out[ax2._name + '.range[0]'];
60489 out[ax._name + '.range[1]'] = out[ax2._name + '.range[1]'];
60490 } else {
60491 ax.range = ax2.range.slice();
60492 }
60493 }
60494 }
60495 }
60496
60497 // Draw ticks and annotations (and other components) when ranges change.
60498 // Also records the ranges that have changed for use by update at the end.
60499 function ticksAndAnnotations() {
60500 var activeAxIds = [];
60501 var i;
60502
60503 function pushActiveAxIds(axList) {
60504 for(i = 0; i < axList.length; i++) {
60505 if(!axList[i].fixedrange) activeAxIds.push(axList[i]._id);
60506 }
60507 }
60508
60509 if(editX) {
60510 pushActiveAxIds(xaxes);
60511 pushActiveAxIds(links.xaxes);
60512 pushActiveAxIds(matches.xaxes);
60513 }
60514 if(editY) {
60515 pushActiveAxIds(yaxes);
60516 pushActiveAxIds(links.yaxes);
60517 pushActiveAxIds(matches.yaxes);
60518 }
60519
60520 updates = {};
60521 for(i = 0; i < activeAxIds.length; i++) {
60522 var axId = activeAxIds[i];
60523 var ax = getFromId(gd, axId);
60524 Axes.drawOne(gd, ax, {skipTitle: true});
60525 updates[ax._name + '.range[0]'] = ax.range[0];
60526 updates[ax._name + '.range[1]'] = ax.range[1];
60527 }
60528
60529 Axes.redrawComponents(gd, activeAxIds);
60530 }
60531
60532 function doubleClick() {
60533 if(gd._transitioningWithDuration) return;
60534
60535 var doubleClickConfig = gd._context.doubleClick;
60536
60537 var axList = [];
60538 if(xActive) axList = axList.concat(xaxes);
60539 if(yActive) axList = axList.concat(yaxes);
60540 if(matches.xaxes) axList = axList.concat(matches.xaxes);
60541 if(matches.yaxes) axList = axList.concat(matches.yaxes);
60542
60543 var attrs = {};
60544 var ax, i, rangeInitial;
60545
60546 // For reset+autosize mode:
60547 // If *any* of the main axes is not at its initial range
60548 // (or autoranged, if we have no initial range, to match the logic in
60549 // doubleClickConfig === 'reset' below), we reset.
60550 // If they are *all* at their initial ranges, then we autosize.
60551 if(doubleClickConfig === 'reset+autosize') {
60552 doubleClickConfig = 'autosize';
60553
60554 for(i = 0; i < axList.length; i++) {
60555 ax = axList[i];
60556 if((ax._rangeInitial && (
60557 ax.range[0] !== ax._rangeInitial[0] ||
60558 ax.range[1] !== ax._rangeInitial[1]
60559 )) ||
60560 (!ax._rangeInitial && !ax.autorange)
60561 ) {
60562 doubleClickConfig = 'reset';
60563 break;
60564 }
60565 }
60566 }
60567
60568 if(doubleClickConfig === 'autosize') {
60569 // don't set the linked axes here, so relayout marks them as shrinkable
60570 // and we autosize just to the requested axis/axes
60571 for(i = 0; i < axList.length; i++) {
60572 ax = axList[i];
60573 if(!ax.fixedrange) attrs[ax._name + '.autorange'] = true;
60574 }
60575 } else if(doubleClickConfig === 'reset') {
60576 // when we're resetting, reset all linked axes too, so we get back
60577 // to the fully-auto-with-constraints situation
60578 if(xActive || links.isSubplotConstrained) axList = axList.concat(links.xaxes);
60579 if(yActive && !links.isSubplotConstrained) axList = axList.concat(links.yaxes);
60580
60581 if(links.isSubplotConstrained) {
60582 if(!xActive) axList = axList.concat(xaxes);
60583 else if(!yActive) axList = axList.concat(yaxes);
60584 }
60585
60586 for(i = 0; i < axList.length; i++) {
60587 ax = axList[i];
60588
60589 if(!ax.fixedrange) {
60590 if(!ax._rangeInitial) {
60591 attrs[ax._name + '.autorange'] = true;
60592 } else {
60593 rangeInitial = ax._rangeInitial;
60594 attrs[ax._name + '.range[0]'] = rangeInitial[0];
60595 attrs[ax._name + '.range[1]'] = rangeInitial[1];
60596 }
60597 }
60598 }
60599 }
60600
60601 gd.emit('plotly_doubleclick', null);
60602 Registry.call('_guiRelayout', gd, attrs);
60603 }
60604
60605 // dragTail - finish a drag event with a redraw
60606 function dragTail() {
60607 // put the subplot viewboxes back to default (Because we're going to)
60608 // be repositioning the data in the relayout. But DON'T call
60609 // ticksAndAnnotations again - it's unnecessary and would overwrite `updates`
60610 updateSubplots([0, 0, pw, ph]);
60611
60612 // since we may have been redrawing some things during the drag, we may have
60613 // accumulated MathJax promises - wait for them before we relayout.
60614 Lib.syncOrAsync([
60615 Plots.previousPromises,
60616 function() {
60617 gd._fullLayout._replotting = false;
60618 Registry.call('_guiRelayout', gd, updates);
60619 }
60620 ], gd);
60621 }
60622
60623 // updateSubplots - find all plot viewboxes that should be
60624 // affected by this drag, and update them. look for all plots
60625 // sharing an affected axis (including the one being dragged),
60626 // includes also scattergl and splom logic.
60627 function updateSubplots(viewBox) {
60628 var fullLayout = gd._fullLayout;
60629 var plotinfos = fullLayout._plots;
60630 var subplots = fullLayout._subplots.cartesian;
60631 var i, sp, xa, ya;
60632
60633 if(hasSplom) {
60634 Registry.subplotsRegistry.splom.drag(gd);
60635 }
60636
60637 if(hasScatterGl) {
60638 for(i = 0; i < subplots.length; i++) {
60639 sp = plotinfos[subplots[i]];
60640 xa = sp.xaxis;
60641 ya = sp.yaxis;
60642
60643 if(sp._scene) {
60644 var xrng = Lib.simpleMap(xa.range, xa.r2l);
60645 var yrng = Lib.simpleMap(ya.range, ya.r2l);
60646 sp._scene.update({range: [xrng[0], yrng[0], xrng[1], yrng[1]]});
60647 }
60648 }
60649 }
60650
60651 if(hasSplom || hasScatterGl) {
60652 clearGlCanvases(gd);
60653 redrawReglTraces(gd);
60654 }
60655
60656 if(hasSVG) {
60657 var xScaleFactor = viewBox[2] / xa0._length;
60658 var yScaleFactor = viewBox[3] / ya0._length;
60659
60660 for(i = 0; i < subplots.length; i++) {
60661 sp = plotinfos[subplots[i]];
60662 xa = sp.xaxis;
60663 ya = sp.yaxis;
60664
60665 var editX2 = editX && !xa.fixedrange && xaHash[xa._id];
60666 var editY2 = editY && !ya.fixedrange && yaHash[ya._id];
60667
60668 var xScaleFactor2, yScaleFactor2;
60669 var clipDx, clipDy;
60670
60671 if(editX2) {
60672 xScaleFactor2 = xScaleFactor;
60673 clipDx = ew ? viewBox[0] : getShift(xa, xScaleFactor2);
60674 } else if(matches.xaHash[xa._id]) {
60675 xScaleFactor2 = xScaleFactor;
60676 clipDx = viewBox[0] * xa._length / xa0._length;
60677 } else if(matches.yaHash[xa._id]) {
60678 xScaleFactor2 = yScaleFactor;
60679 clipDx = yActive === 'ns' ?
60680 -viewBox[1] * xa._length / ya0._length :
60681 getShift(xa, xScaleFactor2, {n: 'top', s: 'bottom'}[yActive]);
60682 } else {
60683 xScaleFactor2 = getLinkedScaleFactor(xa, xScaleFactor, yScaleFactor);
60684 clipDx = scaleAndGetShift(xa, xScaleFactor2);
60685 }
60686
60687 if(editY2) {
60688 yScaleFactor2 = yScaleFactor;
60689 clipDy = ns ? viewBox[1] : getShift(ya, yScaleFactor2);
60690 } else if(matches.yaHash[ya._id]) {
60691 yScaleFactor2 = yScaleFactor;
60692 clipDy = viewBox[1] * ya._length / ya0._length;
60693 } else if(matches.xaHash[ya._id]) {
60694 yScaleFactor2 = xScaleFactor;
60695 clipDy = xActive === 'ew' ?
60696 -viewBox[0] * ya._length / xa0._length :
60697 getShift(ya, yScaleFactor2, {e: 'right', w: 'left'}[xActive]);
60698 } else {
60699 yScaleFactor2 = getLinkedScaleFactor(ya, xScaleFactor, yScaleFactor);
60700 clipDy = scaleAndGetShift(ya, yScaleFactor2);
60701 }
60702
60703 // don't scale at all if neither axis is scalable here
60704 if(!xScaleFactor2 && !yScaleFactor2) {
60705 continue;
60706 }
60707
60708 // but if only one is, reset the other axis scaling
60709 if(!xScaleFactor2) xScaleFactor2 = 1;
60710 if(!yScaleFactor2) yScaleFactor2 = 1;
60711
60712 var plotDx = xa._offset - clipDx / xScaleFactor2;
60713 var plotDy = ya._offset - clipDy / yScaleFactor2;
60714
60715 // TODO could be more efficient here:
60716 // setTranslate and setScale do a lot of extra work
60717 // when working independently, should perhaps combine
60718 // them into a single routine.
60719 sp.clipRect
60720 .call(Drawing.setTranslate, clipDx, clipDy)
60721 .call(Drawing.setScale, xScaleFactor2, yScaleFactor2);
60722
60723 sp.plot
60724 .call(Drawing.setTranslate, plotDx, plotDy)
60725 .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
60726
60727 // apply an inverse scale to individual points to counteract
60728 // the scale of the trace group.
60729 // apply only when scale changes, as adjusting the scale of
60730 // all the points can be expansive.
60731 if(xScaleFactor2 !== sp.xScaleFactor || yScaleFactor2 !== sp.yScaleFactor) {
60732 Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2);
60733 Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2);
60734 }
60735
60736 Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp);
60737
60738 // update x/y scaleFactor stash
60739 sp.xScaleFactor = xScaleFactor2;
60740 sp.yScaleFactor = yScaleFactor2;
60741 }
60742 }
60743 }
60744
60745 // Find the appropriate scaling for this axis, if it's linked to the
60746 // dragged axes by constraints. 0 is special, it means this axis shouldn't
60747 // ever be scaled (will be converted to 1 if the other axis is scaled)
60748 function getLinkedScaleFactor(ax, xScaleFactor, yScaleFactor) {
60749 if(ax.fixedrange) return 0;
60750
60751 if(editX && links.xaHash[ax._id]) {
60752 return xScaleFactor;
60753 }
60754 if(editY && (links.isSubplotConstrained ? links.xaHash : links.yaHash)[ax._id]) {
60755 return yScaleFactor;
60756 }
60757 return 0;
60758 }
60759
60760 function scaleAndGetShift(ax, scaleFactor) {
60761 if(scaleFactor) {
60762 ax.range = ax._r.slice();
60763 scaleZoom(ax, scaleFactor);
60764 return getShift(ax, scaleFactor);
60765 }
60766 return 0;
60767 }
60768
60769 function getShift(ax, scaleFactor, from) {
60770 return ax._length * (1 - scaleFactor) * FROM_TL[from || ax.constraintoward || 'middle'];
60771 }
60772
60773 return dragger;
60774}
60775
60776function makeDragger(plotinfo, nodeName, dragClass, cursor) {
60777 var dragger3 = Lib.ensureSingle(plotinfo.draglayer, nodeName, dragClass, function(s) {
60778 s.classed('drag', true)
60779 .style({fill: 'transparent', 'stroke-width': 0})
60780 .attr('data-subplot', plotinfo.id);
60781 });
60782
60783 dragger3.call(setCursor, cursor);
60784
60785 return dragger3.node();
60786}
60787
60788function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) {
60789 var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor);
60790 d3.select(dragger).call(Drawing.setRect, x, y, w, h);
60791 return dragger;
60792}
60793
60794function isDirectionActive(axList, activeVal) {
60795 for(var i = 0; i < axList.length; i++) {
60796 if(!axList[i].fixedrange) return activeVal;
60797 }
60798 return '';
60799}
60800
60801function getEndText(ax, end) {
60802 var initialVal = ax.range[end];
60803 var diff = Math.abs(initialVal - ax.range[1 - end]);
60804 var dig;
60805
60806 // TODO: this should basically be ax.r2d but we're doing extra
60807 // rounding here... can we clean up at all?
60808 if(ax.type === 'date') {
60809 return initialVal;
60810 } else if(ax.type === 'log') {
60811 dig = Math.ceil(Math.max(0, -Math.log(diff) / Math.LN10)) + 3;
60812 return d3.format('.' + dig + 'g')(Math.pow(10, initialVal));
60813 } else { // linear numeric (or category... but just show numbers here)
60814 dig = Math.floor(Math.log(Math.abs(initialVal)) / Math.LN10) -
60815 Math.floor(Math.log(diff) / Math.LN10) + 4;
60816 return d3.format('.' + String(dig) + 'g')(initialVal);
60817 }
60818}
60819
60820function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
60821 for(var i = 0; i < axList.length; i++) {
60822 var axi = axList[i];
60823 if(axi.fixedrange) continue;
60824
60825 if(axi.rangebreaks) {
60826 var isY = axi._id.charAt(0) === 'y';
60827 var r0F = isY ? (1 - r0Fraction) : r0Fraction;
60828 var r1F = isY ? (1 - r1Fraction) : r1Fraction;
60829
60830 updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0F * axi._length));
60831 updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1F * axi._length));
60832 } else {
60833 var axRangeLinear0 = axi._rl[0];
60834 var axRangeLinearSpan = axi._rl[1] - axRangeLinear0;
60835 updates[axi._name + '.range[0]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction);
60836 updates[axi._name + '.range[1]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction);
60837 }
60838 }
60839
60840 // zoom linked axes about their centers
60841 if(linkedAxes && linkedAxes.length) {
60842 var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;
60843 zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, []);
60844 }
60845}
60846
60847function dragAxList(axList, pix) {
60848 for(var i = 0; i < axList.length; i++) {
60849 var axi = axList[i];
60850 if(!axi.fixedrange) {
60851 if(axi.rangebreaks) {
60852 var p0 = 0;
60853 var p1 = axi._length;
60854 var d0 = axi.p2l(p0 + pix) - axi.p2l(p0);
60855 var d1 = axi.p2l(p1 + pix) - axi.p2l(p1);
60856 var delta = (d0 + d1) / 2;
60857
60858 axi.range = [
60859 axi.l2r(axi._rl[0] - delta),
60860 axi.l2r(axi._rl[1] - delta)
60861 ];
60862 } else {
60863 axi.range = [
60864 axi.l2r(axi._rl[0] - pix / axi._m),
60865 axi.l2r(axi._rl[1] - pix / axi._m)
60866 ];
60867 }
60868 }
60869 }
60870}
60871
60872// common transform for dragging one end of an axis
60873// d>0 is compressing scale (cursor is over the plot,
60874// the axis end should move with the cursor)
60875// d<0 is expanding (cursor is off the plot, axis end moves
60876// nonlinearly so you can expand far)
60877function dZoom(d) {
60878 return 1 - ((d >= 0) ? Math.min(d, 0.9) :
60879 1 / (1 / Math.max(d, -0.3) + 3.222));
60880}
60881
60882function getDragCursor(nsew, dragmode, isMainDrag) {
60883 if(!nsew) return 'pointer';
60884 if(nsew === 'nsew') {
60885 // in this case here, clear cursor and
60886 // use the cursor style set on <g .draglayer>
60887 if(isMainDrag) return '';
60888 if(dragmode === 'pan') return 'move';
60889 return 'crosshair';
60890 }
60891 return nsew.toLowerCase() + '-resize';
60892}
60893
60894function makeZoombox(zoomlayer, lum, xs, ys, path0) {
60895 return zoomlayer.append('path')
60896 .attr('class', 'zoombox')
60897 .style({
60898 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
60899 'stroke-width': 0
60900 })
60901 .attr('transform', 'translate(' + xs + ', ' + ys + ')')
60902 .attr('d', path0 + 'Z');
60903}
60904
60905function makeCorners(zoomlayer, xs, ys) {
60906 return zoomlayer.append('path')
60907 .attr('class', 'zoombox-corners')
60908 .style({
60909 fill: Color.background,
60910 stroke: Color.defaultLine,
60911 'stroke-width': 1,
60912 opacity: 0
60913 })
60914 .attr('transform', 'translate(' + xs + ', ' + ys + ')')
60915 .attr('d', 'M0,0Z');
60916}
60917
60918function updateZoombox(zb, corners, box, path0, dimmed, lum) {
60919 zb.attr('d',
60920 path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) +
60921 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z');
60922 transitionZoombox(zb, corners, dimmed, lum);
60923}
60924
60925function transitionZoombox(zb, corners, dimmed, lum) {
60926 if(!dimmed) {
60927 zb.transition()
60928 .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
60929 'rgba(255,255,255,0.3)')
60930 .duration(200);
60931 corners.transition()
60932 .style('opacity', 1)
60933 .duration(200);
60934 }
60935}
60936
60937function removeZoombox(gd) {
60938 d3.select(gd)
60939 .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
60940 .remove();
60941}
60942
60943function showDoubleClickNotifier(gd) {
60944 if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
60945 Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long');
60946 SHOWZOOMOUTTIP = false;
60947 }
60948}
60949
60950function xCorners(box, y0) {
60951 return 'M' +
60952 (box.l - 0.5) + ',' + (y0 - MINZOOM - 0.5) +
60953 'h-3v' + (2 * MINZOOM + 1) + 'h3ZM' +
60954 (box.r + 0.5) + ',' + (y0 - MINZOOM - 0.5) +
60955 'h3v' + (2 * MINZOOM + 1) + 'h-3Z';
60956}
60957
60958function yCorners(box, x0) {
60959 return 'M' +
60960 (x0 - MINZOOM - 0.5) + ',' + (box.t - 0.5) +
60961 'v-3h' + (2 * MINZOOM + 1) + 'v3ZM' +
60962 (x0 - MINZOOM - 0.5) + ',' + (box.b + 0.5) +
60963 'v3h' + (2 * MINZOOM + 1) + 'v-3Z';
60964}
60965
60966function xyCorners(box) {
60967 var clen = Math.floor(Math.min(box.b - box.t, box.r - box.l, MINZOOM) / 2);
60968 return 'M' +
60969 (box.l - 3.5) + ',' + (box.t - 0.5 + clen) + 'h3v' + (-clen) +
60970 'h' + clen + 'v-3h-' + (clen + 3) + 'ZM' +
60971 (box.r + 3.5) + ',' + (box.t - 0.5 + clen) + 'h-3v' + (-clen) +
60972 'h' + (-clen) + 'v-3h' + (clen + 3) + 'ZM' +
60973 (box.r + 3.5) + ',' + (box.b + 0.5 - clen) + 'h-3v' + clen +
60974 'h' + (-clen) + 'v3h' + (clen + 3) + 'ZM' +
60975 (box.l - 3.5) + ',' + (box.b + 0.5 - clen) + 'h3v' + clen +
60976 'h' + clen + 'v3h-' + (clen + 3) + 'Z';
60977}
60978
60979function calcLinks(gd, groups, xaHash, yaHash) {
60980 var isSubplotConstrained = false;
60981 var xLinks = {};
60982 var yLinks = {};
60983 var xID, yID, xLinkID, yLinkID;
60984
60985 for(var i = 0; i < groups.length; i++) {
60986 var group = groups[i];
60987 // check if any of the x axes we're dragging is in this constraint group
60988 for(xID in xaHash) {
60989 if(group[xID]) {
60990 // put the rest of these axes into xLinks, if we're not already
60991 // dragging them, so we know to scale these axes automatically too
60992 // to match the changes in the dragged x axes
60993 for(xLinkID in group) {
60994 if(!(xLinkID.charAt(0) === 'x' ? xaHash : yaHash)[xLinkID]) {
60995 xLinks[xLinkID] = xID;
60996 }
60997 }
60998
60999 // check if the x and y axes of THIS drag are linked
61000 for(yID in yaHash) {
61001 if(group[yID]) isSubplotConstrained = true;
61002 }
61003 }
61004 }
61005
61006 // now check if any of the y axes we're dragging is in this constraint group
61007 // only look for outside links, as we've already checked for links within the dragger
61008 for(yID in yaHash) {
61009 if(group[yID]) {
61010 for(yLinkID in group) {
61011 if(!(yLinkID.charAt(0) === 'x' ? xaHash : yaHash)[yLinkID]) {
61012 yLinks[yLinkID] = yID;
61013 }
61014 }
61015 }
61016 }
61017 }
61018
61019 if(isSubplotConstrained) {
61020 // merge xLinks and yLinks if the subplot is constrained,
61021 // since we'll always apply both anyway and the two will contain
61022 // duplicates
61023 Lib.extendFlat(xLinks, yLinks);
61024 yLinks = {};
61025 }
61026
61027 var xaHashLinked = {};
61028 var xaxesLinked = [];
61029 for(xLinkID in xLinks) {
61030 var xa = getFromId(gd, xLinkID);
61031 xaxesLinked.push(xa);
61032 xaHashLinked[xa._id] = xa;
61033 }
61034
61035 var yaHashLinked = {};
61036 var yaxesLinked = [];
61037 for(yLinkID in yLinks) {
61038 var ya = getFromId(gd, yLinkID);
61039 yaxesLinked.push(ya);
61040 yaHashLinked[ya._id] = ya;
61041 }
61042
61043 return {
61044 xaHash: xaHashLinked,
61045 yaHash: yaHashLinked,
61046 xaxes: xaxesLinked,
61047 yaxes: yaxesLinked,
61048 xLinks: xLinks,
61049 yLinks: yLinks,
61050 isSubplotConstrained: isSubplotConstrained
61051 };
61052}
61053
61054// still seems to be some confusion about onwheel vs onmousewheel...
61055function attachWheelEventHandler(element, handler) {
61056 if(!supportsPassive) {
61057 if(element.onwheel !== undefined) element.onwheel = handler;
61058 else if(element.onmousewheel !== undefined) element.onmousewheel = handler;
61059 else if(!element.isAddedWheelEvent) {
61060 element.isAddedWheelEvent = true;
61061 element.addEventListener('wheel', handler, {passive: false});
61062 }
61063 } else {
61064 var wheelEventName = element.onwheel !== undefined ? 'wheel' : 'mousewheel';
61065
61066 if(element._onwheel) {
61067 element.removeEventListener(wheelEventName, element._onwheel);
61068 }
61069 element._onwheel = handler;
61070
61071 element.addEventListener(wheelEventName, handler, {passive: false});
61072 }
61073}
61074
61075function hashValues(hash) {
61076 var out = [];
61077 for(var k in hash) out.push(hash[k]);
61078 return out;
61079}
61080
61081module.exports = {
61082 makeDragBox: makeDragBox,
61083
61084 makeDragger: makeDragger,
61085 makeRectDragger: makeRectDragger,
61086 makeZoombox: makeZoombox,
61087 makeCorners: makeCorners,
61088
61089 updateZoombox: updateZoombox,
61090 xyCorners: xyCorners,
61091 transitionZoombox: transitionZoombox,
61092 removeZoombox: removeZoombox,
61093 showDoubleClickNotifier: showDoubleClickNotifier,
61094
61095 attachWheelEventHandler: attachWheelEventHandler
61096};
61097
61098},{"../../components/color":50,"../../components/dragelement":69,"../../components/dragelement/helpers":68,"../../components/drawing":72,"../../components/fx":90,"../../constants/alignment":152,"../../lib":177,"../../lib/clear_gl_canvases":164,"../../lib/setcursor":196,"../../lib/svg_text_utils":198,"../../plot_api/subroutines":213,"../../registry":272,"../plots":263,"./axes":222,"./axis_ids":225,"./constants":228,"./scale_zoom":240,"./select":241,"d3":13,"has-passive-events":18,"tinycolor2":32}],231:[function(_dereq_,module,exports){
61099/**
61100* Copyright 2012-2020, Plotly, Inc.
61101* All rights reserved.
61102*
61103* This source code is licensed under the MIT license found in the
61104* LICENSE file in the root directory of this source tree.
61105*/
61106
61107
61108'use strict';
61109
61110var d3 = _dereq_('d3');
61111
61112var Fx = _dereq_('../../components/fx');
61113var dragElement = _dereq_('../../components/dragelement');
61114var setCursor = _dereq_('../../lib/setcursor');
61115
61116var makeDragBox = _dereq_('./dragbox').makeDragBox;
61117var DRAGGERSIZE = _dereq_('./constants').DRAGGERSIZE;
61118
61119exports.initInteractions = function initInteractions(gd) {
61120 var fullLayout = gd._fullLayout;
61121
61122 if(gd._context.staticPlot) {
61123 // this sweeps up more than just cartesian drag elements...
61124 d3.select(gd).selectAll('.drag').remove();
61125 return;
61126 }
61127
61128 if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return;
61129
61130 var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
61131 // sort overlays last, then by x axis number, then y axis number
61132 if((fullLayout._plots[a].mainplot && true) ===
61133 (fullLayout._plots[b].mainplot && true)) {
61134 var aParts = a.split('y');
61135 var bParts = b.split('y');
61136 return (aParts[0] === bParts[0]) ?
61137 (Number(aParts[1] || 1) - Number(bParts[1] || 1)) :
61138 (Number(aParts[0] || 1) - Number(bParts[0] || 1));
61139 }
61140 return fullLayout._plots[a].mainplot ? 1 : -1;
61141 });
61142
61143 subplots.forEach(function(subplot) {
61144 var plotinfo = fullLayout._plots[subplot];
61145 var xa = plotinfo.xaxis;
61146 var ya = plotinfo.yaxis;
61147
61148 // main and corner draggers need not be repeated for
61149 // overlaid subplots - these draggers drag them all
61150 if(!plotinfo.mainplot) {
61151 // main dragger goes over the grids and data, so we use its
61152 // mousemove events for all data hover effects
61153 var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset,
61154 xa._length, ya._length, 'ns', 'ew');
61155
61156 maindrag.onmousemove = function(evt) {
61157 // This is on `gd._fullLayout`, *not* fullLayout because the reference
61158 // changes by the time this is called again.
61159 gd._fullLayout._rehover = function() {
61160 if((gd._fullLayout._hoversubplot === subplot) && gd._fullLayout._plots[subplot]) {
61161 Fx.hover(gd, evt, subplot);
61162 }
61163 };
61164
61165 Fx.hover(gd, evt, subplot);
61166
61167 // Note that we have *not* used the cached fullLayout variable here
61168 // since that may be outdated when this is called as a callback later on
61169 gd._fullLayout._lasthover = maindrag;
61170 gd._fullLayout._hoversubplot = subplot;
61171 };
61172
61173 /*
61174 * IMPORTANT:
61175 * We must check for the presence of the drag cover here.
61176 * If we don't, a 'mouseout' event is triggered on the
61177 * maindrag before each 'click' event, which has the effect
61178 * of clearing the hoverdata; thus, cancelling the click event.
61179 */
61180 maindrag.onmouseout = function(evt) {
61181 if(gd._dragging) return;
61182
61183 // When the mouse leaves this maindrag, unset the hovered subplot.
61184 // This may cause problems if it leaves the subplot directly *onto*
61185 // another subplot, but that's a tiny corner case at the moment.
61186 gd._fullLayout._hoversubplot = null;
61187
61188 dragElement.unhover(gd, evt);
61189 };
61190
61191 // corner draggers
61192 if(gd._context.showAxisDragHandles) {
61193 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE,
61194 DRAGGERSIZE, DRAGGERSIZE, 'n', 'w');
61195 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE,
61196 DRAGGERSIZE, DRAGGERSIZE, 'n', 'e');
61197 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length,
61198 DRAGGERSIZE, DRAGGERSIZE, 's', 'w');
61199 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length,
61200 DRAGGERSIZE, DRAGGERSIZE, 's', 'e');
61201 }
61202 }
61203 if(gd._context.showAxisDragHandles) {
61204 // x axis draggers - if you have overlaid plots,
61205 // these drag each axis separately
61206 if(subplot === xa._mainSubplot) {
61207 // the y position of the main x axis line
61208 var y0 = xa._mainLinePosition;
61209 if(xa.side === 'top') y0 -= DRAGGERSIZE;
61210 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0,
61211 xa._length * 0.8, DRAGGERSIZE, '', 'ew');
61212 makeDragBox(gd, plotinfo, xa._offset, y0,
61213 xa._length * 0.1, DRAGGERSIZE, '', 'w');
61214 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0,
61215 xa._length * 0.1, DRAGGERSIZE, '', 'e');
61216 }
61217 // y axis draggers
61218 if(subplot === ya._mainSubplot) {
61219 // the x position of the main y axis line
61220 var x0 = ya._mainLinePosition;
61221 if(ya.side !== 'right') x0 -= DRAGGERSIZE;
61222 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1,
61223 DRAGGERSIZE, ya._length * 0.8, 'ns', '');
61224 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9,
61225 DRAGGERSIZE, ya._length * 0.1, 's', '');
61226 makeDragBox(gd, plotinfo, x0, ya._offset,
61227 DRAGGERSIZE, ya._length * 0.1, 'n', '');
61228 }
61229 }
61230 });
61231
61232 // In case you mousemove over some hovertext, send it to Fx.hover too
61233 // we do this so that we can put the hover text in front of everything,
61234 // but still be able to interact with everything as if it isn't there
61235 var hoverLayer = fullLayout._hoverlayer.node();
61236
61237 hoverLayer.onmousemove = function(evt) {
61238 evt.target = gd._fullLayout._lasthover;
61239 Fx.hover(gd, evt, fullLayout._hoversubplot);
61240 };
61241
61242 hoverLayer.onclick = function(evt) {
61243 evt.target = gd._fullLayout._lasthover;
61244 Fx.click(gd, evt);
61245 };
61246
61247 // also delegate mousedowns... TODO: does this actually work?
61248 hoverLayer.onmousedown = function(evt) {
61249 gd._fullLayout._lasthover.onmousedown(evt);
61250 };
61251
61252 exports.updateFx(gd);
61253};
61254
61255// Minimal set of update needed on 'modebar' edits.
61256// We only need to update the <g .draglayer> cursor style.
61257//
61258// Note that changing the axis configuration and/or the fixedrange attribute
61259// should trigger a full initInteractions.
61260exports.updateFx = function(gd) {
61261 var fullLayout = gd._fullLayout;
61262 var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair';
61263 setCursor(fullLayout._draggers, cursor);
61264};
61265
61266},{"../../components/dragelement":69,"../../components/fx":90,"../../lib/setcursor":196,"./constants":228,"./dragbox":230,"d3":13}],232:[function(_dereq_,module,exports){
61267/**
61268* Copyright 2012-2020, Plotly, Inc.
61269* All rights reserved.
61270*
61271* This source code is licensed under the MIT license found in the
61272* LICENSE file in the root directory of this source tree.
61273*/
61274
61275
61276'use strict';
61277
61278function clearOutlineControllers(gd) {
61279 var zoomLayer = gd._fullLayout._zoomlayer;
61280 if(zoomLayer) {
61281 zoomLayer.selectAll('.outline-controllers').remove();
61282 }
61283}
61284
61285function clearSelect(gd) {
61286 var zoomLayer = gd._fullLayout._zoomlayer;
61287 if(zoomLayer) {
61288 // until we get around to persistent selections, remove the outline
61289 // here. The selection itself will be removed when the plot redraws
61290 // at the end.
61291 zoomLayer.selectAll('.select-outline').remove();
61292 }
61293
61294 gd._fullLayout._drawing = false;
61295}
61296
61297module.exports = {
61298 clearOutlineControllers: clearOutlineControllers,
61299 clearSelect: clearSelect
61300};
61301
61302},{}],233:[function(_dereq_,module,exports){
61303/**
61304* Copyright 2012-2020, Plotly, Inc.
61305* All rights reserved.
61306*
61307* This source code is licensed under the MIT license found in the
61308* LICENSE file in the root directory of this source tree.
61309*/
61310
61311
61312'use strict';
61313
61314// in v2 (once log ranges are fixed),
61315// we'll be able to p2r here for all axis types
61316function p2r(ax, v) {
61317 switch(ax.type) {
61318 case 'log':
61319 return ax.p2d(v);
61320 case 'date':
61321 return ax.p2r(v, 0, ax.calendar);
61322 default:
61323 return ax.p2r(v);
61324 }
61325}
61326
61327function r2p(ax, v) {
61328 switch(ax.type) {
61329 case 'log':
61330 return ax.d2p(v);
61331 case 'date':
61332 return ax.r2p(v, 0, ax.calendar);
61333 default:
61334 return ax.r2p(v);
61335 }
61336}
61337
61338function axValue(ax) {
61339 var index = (ax._id.charAt(0) === 'y') ? 1 : 0;
61340 return function(v) { return p2r(ax, v[index]); };
61341}
61342
61343function getTransform(plotinfo) {
61344 return 'translate(' +
61345 plotinfo.xaxis._offset + ',' +
61346 plotinfo.yaxis._offset + ')';
61347}
61348
61349module.exports = {
61350 p2r: p2r,
61351 r2p: r2p,
61352 axValue: axValue,
61353 getTransform: getTransform
61354};
61355
61356},{}],234:[function(_dereq_,module,exports){
61357/**
61358* Copyright 2012-2020, Plotly, Inc.
61359* All rights reserved.
61360*
61361* This source code is licensed under the MIT license found in the
61362* LICENSE file in the root directory of this source tree.
61363*/
61364
61365
61366'use strict';
61367
61368var Registry = _dereq_('../../registry');
61369var Lib = _dereq_('../../lib');
61370
61371/**
61372 * Factory function for checking component arrays for subplot references.
61373 *
61374 * @param {string} containerArrayName: the top-level array in gd.layout to check
61375 * If an item in this container is found that references a cartesian x and/or y axis,
61376 * ensure cartesian is marked as a base plot module and record the axes (and subplot
61377 * if both refs are axes) in gd._fullLayout
61378 *
61379 * @return {function}: with args layoutIn (gd.layout) and layoutOut (gd._fullLayout)
61380 * as expected of a component includeBasePlot method
61381 */
61382module.exports = function makeIncludeComponents(containerArrayName) {
61383 return function includeComponents(layoutIn, layoutOut) {
61384 var array = layoutIn[containerArrayName];
61385 if(!Array.isArray(array)) return;
61386
61387 var Cartesian = Registry.subplotsRegistry.cartesian;
61388 var idRegex = Cartesian.idRegex;
61389 var subplots = layoutOut._subplots;
61390 var xaList = subplots.xaxis;
61391 var yaList = subplots.yaxis;
61392 var cartesianList = subplots.cartesian;
61393 var hasCartesianOrGL2D = layoutOut._has('cartesian') || layoutOut._has('gl2d');
61394
61395 for(var i = 0; i < array.length; i++) {
61396 var itemi = array[i];
61397 if(!Lib.isPlainObject(itemi)) continue;
61398
61399 var xref = itemi.xref;
61400 var yref = itemi.yref;
61401
61402 var hasXref = idRegex.x.test(xref);
61403 var hasYref = idRegex.y.test(yref);
61404 if(hasXref || hasYref) {
61405 if(!hasCartesianOrGL2D) Lib.pushUnique(layoutOut._basePlotModules, Cartesian);
61406
61407 var newAxis = false;
61408 if(hasXref && xaList.indexOf(xref) === -1) {
61409 xaList.push(xref);
61410 newAxis = true;
61411 }
61412 if(hasYref && yaList.indexOf(yref) === -1) {
61413 yaList.push(yref);
61414 newAxis = true;
61415 }
61416
61417 /*
61418 * Notice the logic here: only add a subplot for a component if
61419 * it's referencing both x and y axes AND it's creating a new axis
61420 * so for example if your plot already has xy and x2y2, an annotation
61421 * on x2y or xy2 will not create a new subplot.
61422 */
61423 if(newAxis && hasXref && hasYref) {
61424 cartesianList.push(xref + yref);
61425 }
61426 }
61427 }
61428 };
61429};
61430
61431},{"../../lib":177,"../../registry":272}],235:[function(_dereq_,module,exports){
61432/**
61433* Copyright 2012-2020, Plotly, Inc.
61434* All rights reserved.
61435*
61436* This source code is licensed under the MIT license found in the
61437* LICENSE file in the root directory of this source tree.
61438*/
61439
61440
61441'use strict';
61442
61443var d3 = _dereq_('d3');
61444
61445var Registry = _dereq_('../../registry');
61446var Lib = _dereq_('../../lib');
61447var Plots = _dereq_('../plots');
61448var Drawing = _dereq_('../../components/drawing');
61449
61450var getModuleCalcData = _dereq_('../get_data').getModuleCalcData;
61451var axisIds = _dereq_('./axis_ids');
61452var constants = _dereq_('./constants');
61453var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
61454
61455var ensureSingle = Lib.ensureSingle;
61456
61457function ensureSingleAndAddDatum(parent, nodeType, className) {
61458 return Lib.ensureSingle(parent, nodeType, className, function(s) {
61459 s.datum(className);
61460 });
61461}
61462
61463exports.name = 'cartesian';
61464
61465exports.attr = ['xaxis', 'yaxis'];
61466
61467exports.idRoot = ['x', 'y'];
61468
61469exports.idRegex = constants.idRegex;
61470
61471exports.attrRegex = constants.attrRegex;
61472
61473exports.attributes = _dereq_('./attributes');
61474
61475exports.layoutAttributes = _dereq_('./layout_attributes');
61476
61477exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
61478
61479exports.transitionAxes = _dereq_('./transition_axes');
61480
61481exports.finalizeSubplots = function(layoutIn, layoutOut) {
61482 var subplots = layoutOut._subplots;
61483 var xList = subplots.xaxis;
61484 var yList = subplots.yaxis;
61485 var spSVG = subplots.cartesian;
61486 var spAll = spSVG.concat(subplots.gl2d || []);
61487 var allX = {};
61488 var allY = {};
61489 var i, xi, yi;
61490
61491 for(i = 0; i < spAll.length; i++) {
61492 var parts = spAll[i].split('y');
61493 allX[parts[0]] = 1;
61494 allY['y' + parts[1]] = 1;
61495 }
61496
61497 // check for x axes with no subplot, and make one from the anchor of that x axis
61498 for(i = 0; i < xList.length; i++) {
61499 xi = xList[i];
61500 if(!allX[xi]) {
61501 yi = (layoutIn[axisIds.id2name(xi)] || {}).anchor;
61502 if(!constants.idRegex.y.test(yi)) yi = 'y';
61503 spSVG.push(xi + yi);
61504 spAll.push(xi + yi);
61505
61506 if(!allY[yi]) {
61507 allY[yi] = 1;
61508 Lib.pushUnique(yList, yi);
61509 }
61510 }
61511 }
61512
61513 // same for y axes with no subplot
61514 for(i = 0; i < yList.length; i++) {
61515 yi = yList[i];
61516 if(!allY[yi]) {
61517 xi = (layoutIn[axisIds.id2name(yi)] || {}).anchor;
61518 if(!constants.idRegex.x.test(xi)) xi = 'x';
61519 spSVG.push(xi + yi);
61520 spAll.push(xi + yi);
61521
61522 if(!allX[xi]) {
61523 allX[xi] = 1;
61524 Lib.pushUnique(xList, xi);
61525 }
61526 }
61527 }
61528
61529 // finally, if we've gotten here we're supposed to show cartesian...
61530 // so if there are NO subplots at all, make one from the first
61531 // x & y axes in the input layout
61532 if(!spAll.length) {
61533 xi = '';
61534 yi = '';
61535 for(var ki in layoutIn) {
61536 if(constants.attrRegex.test(ki)) {
61537 var axLetter = ki.charAt(0);
61538 if(axLetter === 'x') {
61539 if(!xi || (+ki.substr(5) < +xi.substr(5))) {
61540 xi = ki;
61541 }
61542 } else if(!yi || (+ki.substr(5) < +yi.substr(5))) {
61543 yi = ki;
61544 }
61545 }
61546 }
61547 xi = xi ? axisIds.name2id(xi) : 'x';
61548 yi = yi ? axisIds.name2id(yi) : 'y';
61549 xList.push(xi);
61550 yList.push(yi);
61551 spSVG.push(xi + yi);
61552 }
61553};
61554
61555/**
61556 * Cartesian.plot
61557 *
61558 * @param {DOM div | object} gd
61559 * @param {array (optional)} traces
61560 * array of traces indices to plot
61561 * if undefined, plots all cartesian traces,
61562 * @param {object} (optional) transitionOpts
61563 * transition option object
61564 * @param {function} (optional) makeOnCompleteCallback
61565 * transition make callback function from Plots.transition
61566 */
61567exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
61568 var fullLayout = gd._fullLayout;
61569 var subplots = fullLayout._subplots.cartesian;
61570 var calcdata = gd.calcdata;
61571 var i;
61572
61573 if(!Array.isArray(traces)) {
61574 // If traces is not provided, then it's a complete replot and missing
61575 // traces are removed
61576 traces = [];
61577 for(i = 0; i < calcdata.length; i++) traces.push(i);
61578 }
61579
61580 for(i = 0; i < subplots.length; i++) {
61581 var subplot = subplots[i];
61582 var subplotInfo = fullLayout._plots[subplot];
61583
61584 // Get all calcdata for this subplot:
61585 var cdSubplot = [];
61586 var pcd;
61587
61588 for(var j = 0; j < calcdata.length; j++) {
61589 var cd = calcdata[j];
61590 var trace = cd[0].trace;
61591
61592 // Skip trace if whitelist provided and it's not whitelisted:
61593 // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue;
61594 if(trace.xaxis + trace.yaxis === subplot) {
61595 // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet
61596 // axis has actually changed:
61597 //
61598 // If this trace is specifically requested, add it to the list:
61599 if(traces.indexOf(trace.index) !== -1 || trace.carpet) {
61600 // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate
61601 // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill
61602 // is outdated. So this retroactively adds the previous trace if the
61603 // traces are interdependent.
61604 if(
61605 pcd &&
61606 pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot &&
61607 ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 &&
61608 cdSubplot.indexOf(pcd) === -1
61609 ) {
61610 cdSubplot.push(pcd);
61611 }
61612
61613 cdSubplot.push(cd);
61614 }
61615
61616 // Track the previous trace on this subplot for the retroactive-add step
61617 // above:
61618 pcd = cd;
61619 }
61620 }
61621
61622 plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback);
61623 }
61624};
61625
61626function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
61627 var traceLayerClasses = constants.traceLayerClasses;
61628 var fullLayout = gd._fullLayout;
61629 var modules = fullLayout._modules;
61630 var _module, cdModuleAndOthers, cdModule;
61631
61632 var layerData = [];
61633 var zoomScaleQueryParts = [];
61634
61635 for(var i = 0; i < modules.length; i++) {
61636 _module = modules[i];
61637 var name = _module.name;
61638 var categories = Registry.modules[name].categories;
61639
61640 if(categories.svg) {
61641 var className = (_module.layerName || name + 'layer');
61642 var plotMethod = _module.plot;
61643
61644 // plot all visible traces of this type on this subplot at once
61645 cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
61646 cdModule = cdModuleAndOthers[0];
61647 // don't need to search the found traces again - in fact we need to NOT
61648 // so that if two modules share the same plotter we don't double-plot
61649 cdSubplot = cdModuleAndOthers[1];
61650
61651 if(cdModule.length) {
61652 layerData.push({
61653 i: traceLayerClasses.indexOf(className),
61654 className: className,
61655 plotMethod: plotMethod,
61656 cdModule: cdModule
61657 });
61658 }
61659
61660 if(categories.zoomScale) {
61661 zoomScaleQueryParts.push('.' + className);
61662 }
61663 }
61664 }
61665
61666 layerData.sort(function(a, b) { return a.i - b.i; });
61667
61668 var layers = plotinfo.plot.selectAll('g.mlayer')
61669 .data(layerData, function(d) { return d.className; });
61670
61671 layers.enter().append('g')
61672 .attr('class', function(d) { return d.className; })
61673 .classed('mlayer', true)
61674 .classed('rangeplot', plotinfo.isRangePlot);
61675
61676 layers.exit().remove();
61677
61678 layers.order();
61679
61680 layers.each(function(d) {
61681 var sel = d3.select(this);
61682 var className = d.className;
61683
61684 d.plotMethod(
61685 gd, plotinfo, d.cdModule, sel,
61686 transitionOpts, makeOnCompleteCallback
61687 );
61688
61689 // layers that allow `cliponaxis: false`
61690 if(constants.clipOnAxisFalseQuery.indexOf('.' + className) === -1) {
61691 Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
61692 }
61693 });
61694
61695 // call Scattergl.plot separately
61696 if(fullLayout._has('scattergl')) {
61697 _module = Registry.getModule('scattergl');
61698 cdModule = getModuleCalcData(cdSubplot, _module)[0];
61699 _module.plot(gd, plotinfo, cdModule);
61700 }
61701
61702 // stash "hot" selections for faster interaction on drag and scroll
61703 if(!gd._context.staticPlot) {
61704 if(plotinfo._hasClipOnAxisFalse) {
61705 plotinfo.clipOnAxisFalseTraces = plotinfo.plot
61706 .selectAll(constants.clipOnAxisFalseQuery.join(','))
61707 .selectAll('.trace');
61708 }
61709
61710 if(zoomScaleQueryParts.length) {
61711 var traces = plotinfo.plot
61712 .selectAll(zoomScaleQueryParts.join(','))
61713 .selectAll('.trace');
61714
61715 plotinfo.zoomScalePts = traces.selectAll('path.point');
61716 plotinfo.zoomScaleTxt = traces.selectAll('.textpoint');
61717 }
61718 }
61719}
61720
61721exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
61722 var oldPlots = oldFullLayout._plots || {};
61723 var newPlots = newFullLayout._plots || {};
61724 var oldSubplotList = oldFullLayout._subplots || {};
61725 var plotinfo;
61726 var i, k;
61727
61728 // when going from a large splom graph to something else,
61729 // we need to clear <g subplot> so that the new cartesian subplot
61730 // can have the correct layer ordering
61731 if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) {
61732 for(k in oldPlots) {
61733 plotinfo = oldPlots[k];
61734 if(plotinfo.plotgroup) plotinfo.plotgroup.remove();
61735 }
61736 }
61737
61738 var hadGl = (oldFullLayout._has && oldFullLayout._has('gl'));
61739 var hasGl = (newFullLayout._has && newFullLayout._has('gl'));
61740
61741 if(hadGl && !hasGl) {
61742 for(k in oldPlots) {
61743 plotinfo = oldPlots[k];
61744 if(plotinfo._scene) plotinfo._scene.destroy();
61745 }
61746 }
61747
61748 // delete any titles we don't need anymore
61749 // check if axis list has changed, and if so clear old titles
61750 if(oldSubplotList.xaxis && oldSubplotList.yaxis) {
61751 var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout});
61752 for(i = 0; i < oldAxIDs.length; i++) {
61753 var oldAxId = oldAxIDs[i];
61754 if(!newFullLayout[axisIds.id2name(oldAxId)]) {
61755 oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove();
61756 }
61757 }
61758 }
61759
61760 var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian'));
61761 var hasCartesian = (newFullLayout._has && newFullLayout._has('cartesian'));
61762
61763 if(hadCartesian && !hasCartesian) {
61764 // if we've gotten rid of all cartesian traces, remove all the subplot svg items
61765
61766 purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout);
61767 oldFullLayout._defs.selectAll('.axesclip').remove();
61768 delete oldFullLayout._axisConstraintGroups;
61769 } else if(oldSubplotList.cartesian) {
61770 // otherwise look for subplots we need to remove
61771
61772 for(i = 0; i < oldSubplotList.cartesian.length; i++) {
61773 var oldSubplotId = oldSubplotList.cartesian[i];
61774 if(!newPlots[oldSubplotId]) {
61775 var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y';
61776 oldFullLayout._cartesianlayer.selectAll(selector).remove();
61777 removeSubplotExtras(oldSubplotId, oldFullLayout);
61778 }
61779 }
61780 }
61781};
61782
61783exports.drawFramework = function(gd) {
61784 var fullLayout = gd._fullLayout;
61785 var subplotData = makeSubplotData(gd);
61786
61787 var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot')
61788 .data(subplotData, String);
61789
61790 subplotLayers.enter().append('g')
61791 .attr('class', function(d) { return 'subplot ' + d[0]; });
61792
61793 subplotLayers.order();
61794
61795 subplotLayers.exit()
61796 .call(purgeSubplotLayers, fullLayout);
61797
61798 subplotLayers.each(function(d) {
61799 var id = d[0];
61800 var plotinfo = fullLayout._plots[id];
61801
61802 plotinfo.plotgroup = d3.select(this);
61803 makeSubplotLayer(gd, plotinfo);
61804
61805 // make separate drag layers for each subplot,
61806 // but append them to paper rather than the plot groups,
61807 // so they end up on top of the rest
61808 plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id);
61809 });
61810};
61811
61812exports.rangePlot = function(gd, plotinfo, cdSubplot) {
61813 makeSubplotLayer(gd, plotinfo);
61814 plotOne(gd, plotinfo, cdSubplot);
61815 Plots.style(gd);
61816};
61817
61818function makeSubplotData(gd) {
61819 var fullLayout = gd._fullLayout;
61820 var ids = fullLayout._subplots.cartesian;
61821 var len = ids.length;
61822 var i, j, id, plotinfo, xa, ya;
61823
61824 // split 'regular' and 'overlaying' subplots
61825 var regulars = [];
61826 var overlays = [];
61827
61828 for(i = 0; i < len; i++) {
61829 id = ids[i];
61830 plotinfo = fullLayout._plots[id];
61831 xa = plotinfo.xaxis;
61832 ya = plotinfo.yaxis;
61833
61834 var xa2 = xa._mainAxis;
61835 var ya2 = ya._mainAxis;
61836 var mainplot = xa2._id + ya2._id;
61837 var mainplotinfo = fullLayout._plots[mainplot];
61838 plotinfo.overlays = [];
61839
61840 if(mainplot !== id && mainplotinfo) {
61841 plotinfo.mainplot = mainplot;
61842 plotinfo.mainplotinfo = mainplotinfo;
61843 overlays.push(id);
61844 } else {
61845 plotinfo.mainplot = undefined;
61846 plotinfo.mainPlotinfo = undefined;
61847 regulars.push(id);
61848 }
61849 }
61850
61851 // fill in list of overlaying subplots in 'main plot'
61852 for(i = 0; i < overlays.length; i++) {
61853 id = overlays[i];
61854 plotinfo = fullLayout._plots[id];
61855 plotinfo.mainplotinfo.overlays.push(plotinfo);
61856 }
61857
61858 // put 'regular' subplot data before 'overlaying'
61859 var subplotIds = regulars.concat(overlays);
61860 var subplotData = new Array(len);
61861
61862 for(i = 0; i < len; i++) {
61863 id = subplotIds[i];
61864 plotinfo = fullLayout._plots[id];
61865 xa = plotinfo.xaxis;
61866 ya = plotinfo.yaxis;
61867
61868 // use info about axis layer and overlaying pattern
61869 // to clean what need to be cleaned up in exit selection
61870 var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || ''];
61871 for(j = 0; j < plotinfo.overlays.length; j++) {
61872 d.push(plotinfo.overlays[j].id);
61873 }
61874 subplotData[i] = d;
61875 }
61876
61877 return subplotData;
61878}
61879
61880function makeSubplotLayer(gd, plotinfo) {
61881 var plotgroup = plotinfo.plotgroup;
61882 var id = plotinfo.id;
61883 var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
61884 var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
61885 var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms;
61886
61887 if(!plotinfo.mainplot) {
61888 if(hasOnlyLargeSploms) {
61889 // TODO could do even better
61890 // - we don't need plot (but we would have to mock it in lsInner
61891 // and other places
61892 // - we don't (x|y)lines and (x|y)axislayer for most subplots
61893 // usually just the bottom x and left y axes.
61894 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
61895 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
61896 plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above');
61897 plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above');
61898 } else {
61899 var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
61900 plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
61901 plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
61902
61903 plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
61904 plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
61905
61906 ensureSingle(plotgroup, 'path', 'xlines-below');
61907 ensureSingle(plotgroup, 'path', 'ylines-below');
61908 plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
61909
61910 ensureSingle(plotgroup, 'g', 'xaxislayer-below');
61911 ensureSingle(plotgroup, 'g', 'yaxislayer-below');
61912 plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
61913
61914 plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
61915 plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
61916
61917 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
61918 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
61919 plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above');
61920
61921 ensureSingle(plotgroup, 'g', 'xaxislayer-above');
61922 ensureSingle(plotgroup, 'g', 'yaxislayer-above');
61923 plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
61924
61925 // set refs to correct layers as determined by 'axis.layer'
61926 plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
61927 plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
61928 plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
61929 plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
61930 }
61931 } else {
61932 var mainplotinfo = plotinfo.mainplotinfo;
61933 var mainplotgroup = mainplotinfo.plotgroup;
61934 var xId = id + '-x';
61935 var yId = id + '-y';
61936
61937 // now make the components of overlaid subplots
61938 // overlays don't have backgrounds, and append all
61939 // their other components to the corresponding
61940 // extra groups of their main plots.
61941
61942 plotinfo.gridlayer = mainplotinfo.gridlayer;
61943 plotinfo.zerolinelayer = mainplotinfo.zerolinelayer;
61944
61945 ensureSingle(mainplotinfo.overlinesBelow, 'path', xId);
61946 ensureSingle(mainplotinfo.overlinesBelow, 'path', yId);
61947 ensureSingle(mainplotinfo.overaxesBelow, 'g', xId);
61948 ensureSingle(mainplotinfo.overaxesBelow, 'g', yId);
61949
61950 plotinfo.plot = ensureSingle(mainplotinfo.overplot, 'g', id);
61951
61952 ensureSingle(mainplotinfo.overlinesAbove, 'path', xId);
61953 ensureSingle(mainplotinfo.overlinesAbove, 'path', yId);
61954 ensureSingle(mainplotinfo.overaxesAbove, 'g', xId);
61955 ensureSingle(mainplotinfo.overaxesAbove, 'g', yId);
61956
61957 // set refs to correct layers as determined by 'abovetraces'
61958 plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
61959 plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
61960 plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
61961 plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
61962 }
61963
61964 // common attributes for all subplots, overlays or not
61965
61966 if(!hasOnlyLargeSploms) {
61967 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
61968 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
61969 plotinfo.gridlayer.selectAll('g')
61970 .map(function(d) { return d[0]; })
61971 .sort(axisIds.idSort);
61972 }
61973
61974 plotinfo.xlines
61975 .style('fill', 'none')
61976 .classed('crisp', true);
61977
61978 plotinfo.ylines
61979 .style('fill', 'none')
61980 .classed('crisp', true);
61981}
61982
61983function purgeSubplotLayers(layers, fullLayout) {
61984 if(!layers) return;
61985
61986 var overlayIdsToRemove = {};
61987
61988 layers.each(function(d) {
61989 var id = d[0];
61990 var plotgroup = d3.select(this);
61991
61992 plotgroup.remove();
61993 removeSubplotExtras(id, fullLayout);
61994 overlayIdsToRemove[id] = true;
61995
61996 // do not remove individual axis <clipPath>s here
61997 // as other subplots may need them
61998 });
61999
62000 // must remove overlaid subplot trace layers 'manually'
62001
62002 for(var k in fullLayout._plots) {
62003 var subplotInfo = fullLayout._plots[k];
62004 var overlays = subplotInfo.overlays || [];
62005
62006 for(var j = 0; j < overlays.length; j++) {
62007 var overlayInfo = overlays[j];
62008
62009 if(overlayIdsToRemove[overlayInfo.id]) {
62010 overlayInfo.plot.selectAll('.trace').remove();
62011 }
62012 }
62013 }
62014}
62015
62016function removeSubplotExtras(subplotId, fullLayout) {
62017 fullLayout._draggers.selectAll('g.' + subplotId).remove();
62018 fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove();
62019}
62020
62021exports.toSVG = function(gd) {
62022 var imageRoot = gd._fullLayout._glimages;
62023 var root = d3.select(gd).selectAll('.svg-container');
62024 var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
62025 .selectAll('.gl-canvas-context, .gl-canvas-focus');
62026
62027 function canvasToImage() {
62028 var canvas = this;
62029 var imageData = canvas.toDataURL('image/png');
62030 var image = imageRoot.append('svg:image');
62031
62032 image.attr({
62033 xmlns: xmlnsNamespaces.svg,
62034 'xlink:href': imageData,
62035 preserveAspectRatio: 'none',
62036 x: 0,
62037 y: 0,
62038 width: canvas.width,
62039 height: canvas.height
62040 });
62041 }
62042
62043 canvases.each(canvasToImage);
62044};
62045
62046exports.updateFx = _dereq_('./graph_interact').updateFx;
62047
62048},{"../../components/drawing":72,"../../constants/xmlns_namespaces":156,"../../lib":177,"../../registry":272,"../get_data":259,"../plots":263,"./attributes":220,"./axis_ids":225,"./constants":228,"./graph_interact":231,"./layout_attributes":236,"./layout_defaults":237,"./transition_axes":246,"d3":13}],236:[function(_dereq_,module,exports){
62049/**
62050* Copyright 2012-2020, Plotly, Inc.
62051* All rights reserved.
62052*
62053* This source code is licensed under the MIT license found in the
62054* LICENSE file in the root directory of this source tree.
62055*/
62056
62057'use strict';
62058
62059var fontAttrs = _dereq_('../font_attributes');
62060var colorAttrs = _dereq_('../../components/color/attributes');
62061var dash = _dereq_('../../components/drawing/attributes').dash;
62062var extendFlat = _dereq_('../../lib/extend').extendFlat;
62063var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
62064
62065var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK;
62066var DATE_FORMAT_LINK = _dereq_('../../constants/docs').DATE_FORMAT_LINK;
62067var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
62068var constants = _dereq_('./constants');
62069var HOUR = constants.HOUR_PATTERN;
62070var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
62071
62072module.exports = {
62073 visible: {
62074 valType: 'boolean',
62075
62076 editType: 'plot',
62077
62078 },
62079 color: {
62080 valType: 'color',
62081 dflt: colorAttrs.defaultLine,
62082
62083 editType: 'ticks',
62084
62085 },
62086 title: {
62087 text: {
62088 valType: 'string',
62089
62090 editType: 'ticks',
62091
62092 },
62093 font: fontAttrs({
62094 editType: 'ticks',
62095
62096 }),
62097 standoff: {
62098 valType: 'number',
62099
62100 min: 0,
62101 editType: 'ticks',
62102
62103 },
62104 editType: 'ticks'
62105 },
62106 type: {
62107 valType: 'enumerated',
62108 // '-' means we haven't yet run autotype or couldn't find any data
62109 // it gets turned into linear in gd._fullLayout but not copied back
62110 // to gd.data like the others are.
62111 values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'],
62112 dflt: '-',
62113
62114 editType: 'calc',
62115 // we forget when an axis has been autotyped, just writing the auto
62116 // value back to the input - so it doesn't make sense to template this.
62117 // Note: we do NOT prohibit this in `coerce`, so if someone enters a
62118 // type in the template explicitly it will be honored as the default.
62119 _noTemplating: true,
62120
62121 },
62122 autorange: {
62123 valType: 'enumerated',
62124 values: [true, false, 'reversed'],
62125 dflt: true,
62126
62127 editType: 'axrange',
62128 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
62129
62130 },
62131 rangemode: {
62132 valType: 'enumerated',
62133 values: ['normal', 'tozero', 'nonnegative'],
62134 dflt: 'normal',
62135
62136 editType: 'plot',
62137
62138 },
62139 range: {
62140 valType: 'info_array',
62141
62142 items: [
62143 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true},
62144 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true}
62145 ],
62146 editType: 'axrange',
62147 impliedEdits: {'autorange': false},
62148 anim: true,
62149
62150 },
62151 fixedrange: {
62152 valType: 'boolean',
62153 dflt: false,
62154
62155 editType: 'calc',
62156
62157 },
62158 // scaleanchor: not used directly, just put here for reference
62159 // values are any opposite-letter axis id
62160 scaleanchor: {
62161 valType: 'enumerated',
62162 values: [
62163 constants.idRegex.x.toString(),
62164 constants.idRegex.y.toString()
62165 ],
62166
62167 editType: 'plot',
62168
62169 },
62170 scaleratio: {
62171 valType: 'number',
62172 min: 0,
62173 dflt: 1,
62174
62175 editType: 'plot',
62176
62177 },
62178 constrain: {
62179 valType: 'enumerated',
62180 values: ['range', 'domain'],
62181 dflt: 'range',
62182
62183 editType: 'plot',
62184
62185 },
62186 // constraintoward: not used directly, just put here for reference
62187 constraintoward: {
62188 valType: 'enumerated',
62189 values: ['left', 'center', 'right', 'top', 'middle', 'bottom'],
62190
62191 editType: 'plot',
62192
62193 },
62194 matches: {
62195 valType: 'enumerated',
62196 values: [
62197 constants.idRegex.x.toString(),
62198 constants.idRegex.y.toString()
62199 ],
62200
62201 editType: 'calc',
62202
62203 },
62204
62205 rangebreaks: templatedArray('rangebreak', {
62206 enabled: {
62207 valType: 'boolean',
62208
62209 dflt: true,
62210 editType: 'calc',
62211
62212 },
62213
62214 bounds: {
62215 valType: 'info_array',
62216
62217 items: [
62218 {valType: 'any', editType: 'calc'},
62219 {valType: 'any', editType: 'calc'}
62220 ],
62221 editType: 'calc',
62222
62223 },
62224
62225 pattern: {
62226 valType: 'enumerated',
62227 values: [DAY_OF_WEEK, HOUR, ''],
62228
62229 editType: 'calc',
62230
62231 },
62232
62233 values: {
62234 valType: 'info_array',
62235 freeLength: true,
62236
62237 editType: 'calc',
62238 items: {
62239 valType: 'any',
62240 editType: 'calc'
62241 },
62242
62243 },
62244 dvalue: {
62245 // TODO could become 'any' to add support for 'months', 'years'
62246 valType: 'number',
62247
62248 editType: 'calc',
62249 min: 0,
62250 dflt: ONEDAY,
62251
62252 },
62253
62254 /*
62255 gap: {
62256 valType: 'number',
62257 min: 0,
62258 dflt: 0, // for *date* axes, maybe something else for *linear*
62259 editType: 'calc',
62260
62261
62262 },
62263 gapmode: {
62264 valType: 'enumerated',
62265 values: ['pixels', 'fraction'],
62266 dflt: 'pixels',
62267 editType: 'calc',
62268
62269
62270 },
62271 */
62272
62273 // To complete https://github.com/plotly/plotly.js/issues/4210
62274 // we additionally need `gap` and make this work on *linear*, and
62275 // possibly all other cartesian axis types. We possibly would also need
62276 // some style attributes controlling the zig-zag on the corresponding
62277 // axis.
62278
62279 editType: 'calc'
62280 }),
62281
62282 // ticks
62283 tickmode: {
62284 valType: 'enumerated',
62285 values: ['auto', 'linear', 'array'],
62286
62287 editType: 'ticks',
62288 impliedEdits: {tick0: undefined, dtick: undefined},
62289
62290 },
62291 nticks: {
62292 valType: 'integer',
62293 min: 0,
62294 dflt: 0,
62295
62296 editType: 'ticks',
62297
62298 },
62299 tick0: {
62300 valType: 'any',
62301
62302 editType: 'ticks',
62303 impliedEdits: {tickmode: 'linear'},
62304
62305 },
62306 dtick: {
62307 valType: 'any',
62308
62309 editType: 'ticks',
62310 impliedEdits: {tickmode: 'linear'},
62311
62312 },
62313 tickvals: {
62314 valType: 'data_array',
62315 editType: 'ticks',
62316
62317 },
62318 ticktext: {
62319 valType: 'data_array',
62320 editType: 'ticks',
62321
62322 },
62323 ticks: {
62324 valType: 'enumerated',
62325 values: ['outside', 'inside', ''],
62326
62327 editType: 'ticks',
62328
62329 },
62330 tickson: {
62331 valType: 'enumerated',
62332 values: ['labels', 'boundaries'],
62333
62334 dflt: 'labels',
62335 editType: 'ticks',
62336
62337 },
62338 mirror: {
62339 valType: 'enumerated',
62340 values: [true, 'ticks', false, 'all', 'allticks'],
62341 dflt: false,
62342
62343 editType: 'ticks+layoutstyle',
62344
62345 },
62346 ticklen: {
62347 valType: 'number',
62348 min: 0,
62349 dflt: 5,
62350
62351 editType: 'ticks',
62352
62353 },
62354 tickwidth: {
62355 valType: 'number',
62356 min: 0,
62357 dflt: 1,
62358
62359 editType: 'ticks',
62360
62361 },
62362 tickcolor: {
62363 valType: 'color',
62364 dflt: colorAttrs.defaultLine,
62365
62366 editType: 'ticks',
62367
62368 },
62369 showticklabels: {
62370 valType: 'boolean',
62371 dflt: true,
62372
62373 editType: 'ticks',
62374
62375 },
62376 automargin: {
62377 valType: 'boolean',
62378 dflt: false,
62379
62380 editType: 'ticks',
62381
62382 },
62383 showspikes: {
62384 valType: 'boolean',
62385 dflt: false,
62386
62387 editType: 'modebar',
62388
62389 },
62390 spikecolor: {
62391 valType: 'color',
62392 dflt: null,
62393
62394 editType: 'none',
62395
62396 },
62397 spikethickness: {
62398 valType: 'number',
62399 dflt: 3,
62400
62401 editType: 'none',
62402
62403 },
62404 spikedash: extendFlat({}, dash, {dflt: 'dash', editType: 'none'}),
62405 spikemode: {
62406 valType: 'flaglist',
62407 flags: ['toaxis', 'across', 'marker'],
62408
62409 dflt: 'toaxis',
62410 editType: 'none',
62411
62412 },
62413 spikesnap: {
62414 valType: 'enumerated',
62415 values: ['data', 'cursor', 'hovered data'],
62416 dflt: 'data',
62417
62418 editType: 'none',
62419
62420 },
62421 tickfont: fontAttrs({
62422 editType: 'ticks',
62423
62424 }),
62425 tickangle: {
62426 valType: 'angle',
62427 dflt: 'auto',
62428
62429 editType: 'ticks',
62430
62431 },
62432 tickprefix: {
62433 valType: 'string',
62434 dflt: '',
62435
62436 editType: 'ticks',
62437
62438 },
62439 showtickprefix: {
62440 valType: 'enumerated',
62441 values: ['all', 'first', 'last', 'none'],
62442 dflt: 'all',
62443
62444 editType: 'ticks',
62445
62446 },
62447 ticksuffix: {
62448 valType: 'string',
62449 dflt: '',
62450
62451 editType: 'ticks',
62452
62453 },
62454 showticksuffix: {
62455 valType: 'enumerated',
62456 values: ['all', 'first', 'last', 'none'],
62457 dflt: 'all',
62458
62459 editType: 'ticks',
62460
62461 },
62462 showexponent: {
62463 valType: 'enumerated',
62464 values: ['all', 'first', 'last', 'none'],
62465 dflt: 'all',
62466
62467 editType: 'ticks',
62468
62469 },
62470 exponentformat: {
62471 valType: 'enumerated',
62472 values: ['none', 'e', 'E', 'power', 'SI', 'B'],
62473 dflt: 'B',
62474
62475 editType: 'ticks',
62476
62477 },
62478 separatethousands: {
62479 valType: 'boolean',
62480 dflt: false,
62481
62482 editType: 'ticks',
62483
62484 },
62485 tickformat: {
62486 valType: 'string',
62487 dflt: '',
62488
62489 editType: 'ticks',
62490
62491 },
62492 tickformatstops: templatedArray('tickformatstop', {
62493 enabled: {
62494 valType: 'boolean',
62495
62496 dflt: true,
62497 editType: 'ticks',
62498
62499 },
62500 dtickrange: {
62501 valType: 'info_array',
62502
62503 items: [
62504 {valType: 'any', editType: 'ticks'},
62505 {valType: 'any', editType: 'ticks'}
62506 ],
62507 editType: 'ticks',
62508
62509 },
62510 value: {
62511 valType: 'string',
62512 dflt: '',
62513
62514 editType: 'ticks',
62515
62516 },
62517 editType: 'ticks'
62518 }),
62519 hoverformat: {
62520 valType: 'string',
62521 dflt: '',
62522
62523 editType: 'none',
62524
62525 },
62526 // lines and grids
62527 showline: {
62528 valType: 'boolean',
62529 dflt: false,
62530
62531 editType: 'ticks+layoutstyle',
62532
62533 },
62534 linecolor: {
62535 valType: 'color',
62536 dflt: colorAttrs.defaultLine,
62537
62538 editType: 'layoutstyle',
62539
62540 },
62541 linewidth: {
62542 valType: 'number',
62543 min: 0,
62544 dflt: 1,
62545
62546 editType: 'ticks+layoutstyle',
62547
62548 },
62549 showgrid: {
62550 valType: 'boolean',
62551
62552 editType: 'ticks',
62553
62554 },
62555 gridcolor: {
62556 valType: 'color',
62557 dflt: colorAttrs.lightLine,
62558
62559 editType: 'ticks',
62560
62561 },
62562 gridwidth: {
62563 valType: 'number',
62564 min: 0,
62565 dflt: 1,
62566
62567 editType: 'ticks',
62568
62569 },
62570 zeroline: {
62571 valType: 'boolean',
62572
62573 editType: 'ticks',
62574
62575 },
62576 zerolinecolor: {
62577 valType: 'color',
62578 dflt: colorAttrs.defaultLine,
62579
62580 editType: 'ticks',
62581
62582 },
62583 zerolinewidth: {
62584 valType: 'number',
62585 dflt: 1,
62586
62587 editType: 'ticks',
62588
62589 },
62590
62591 showdividers: {
62592 valType: 'boolean',
62593 dflt: true,
62594
62595 editType: 'ticks',
62596
62597 },
62598 dividercolor: {
62599 valType: 'color',
62600 dflt: colorAttrs.defaultLine,
62601
62602 editType: 'ticks',
62603
62604 },
62605 dividerwidth: {
62606 valType: 'number',
62607 dflt: 1,
62608
62609 editType: 'ticks',
62610
62611 },
62612 // TODO dividerlen: that would override "to label base" length?
62613
62614 // positioning attributes
62615 // anchor: not used directly, just put here for reference
62616 // values are any opposite-letter axis id
62617 anchor: {
62618 valType: 'enumerated',
62619 values: [
62620 'free',
62621 constants.idRegex.x.toString(),
62622 constants.idRegex.y.toString()
62623 ],
62624
62625 editType: 'plot',
62626
62627 },
62628 // side: not used directly, as values depend on direction
62629 // values are top, bottom for x axes, and left, right for y
62630 side: {
62631 valType: 'enumerated',
62632 values: ['top', 'bottom', 'left', 'right'],
62633
62634 editType: 'plot',
62635
62636 },
62637 // overlaying: not used directly, just put here for reference
62638 // values are false and any other same-letter axis id that's not
62639 // itself overlaying anything
62640 overlaying: {
62641 valType: 'enumerated',
62642 values: [
62643 'free',
62644 constants.idRegex.x.toString(),
62645 constants.idRegex.y.toString()
62646 ],
62647
62648 editType: 'plot',
62649
62650 },
62651 layer: {
62652 valType: 'enumerated',
62653 values: ['above traces', 'below traces'],
62654 dflt: 'above traces',
62655
62656 editType: 'plot',
62657
62658 },
62659 domain: {
62660 valType: 'info_array',
62661
62662 items: [
62663 {valType: 'number', min: 0, max: 1, editType: 'plot'},
62664 {valType: 'number', min: 0, max: 1, editType: 'plot'}
62665 ],
62666 dflt: [0, 1],
62667 editType: 'plot',
62668
62669 },
62670 position: {
62671 valType: 'number',
62672 min: 0,
62673 max: 1,
62674 dflt: 0,
62675
62676 editType: 'plot',
62677
62678 },
62679 categoryorder: {
62680 valType: 'enumerated',
62681 values: [
62682 'trace', 'category ascending', 'category descending', 'array',
62683 'total ascending', 'total descending',
62684 'min ascending', 'min descending',
62685 'max ascending', 'max descending',
62686 'sum ascending', 'sum descending',
62687 'mean ascending', 'mean descending',
62688 'median ascending', 'median descending'
62689 ],
62690 dflt: 'trace',
62691
62692 editType: 'calc',
62693
62694 },
62695 categoryarray: {
62696 valType: 'data_array',
62697
62698 editType: 'calc',
62699
62700 },
62701 uirevision: {
62702 valType: 'any',
62703
62704 editType: 'none',
62705
62706 },
62707 editType: 'calc',
62708
62709 _deprecated: {
62710 autotick: {
62711 valType: 'boolean',
62712
62713 editType: 'ticks',
62714
62715 },
62716 title: {
62717 valType: 'string',
62718
62719 editType: 'ticks',
62720
62721 },
62722 titlefont: fontAttrs({
62723 editType: 'ticks',
62724
62725 })
62726 }
62727};
62728
62729},{"../../components/color/attributes":49,"../../components/drawing/attributes":71,"../../constants/docs":153,"../../constants/numerical":155,"../../lib/extend":170,"../../plot_api/plot_template":212,"../font_attributes":250,"./constants":228}],237:[function(_dereq_,module,exports){
62730/**
62731* Copyright 2012-2020, Plotly, Inc.
62732* All rights reserved.
62733*
62734* This source code is licensed under the MIT license found in the
62735* LICENSE file in the root directory of this source tree.
62736*/
62737
62738
62739'use strict';
62740
62741var Lib = _dereq_('../../lib');
62742var Color = _dereq_('../../components/color');
62743var isUnifiedHover = _dereq_('../../components/fx/helpers').isUnifiedHover;
62744var handleHoverModeDefaults = _dereq_('../../components/fx/hovermode_defaults');
62745var Template = _dereq_('../../plot_api/plot_template');
62746var basePlotLayoutAttributes = _dereq_('../layout_attributes');
62747
62748var layoutAttributes = _dereq_('./layout_attributes');
62749var handleTypeDefaults = _dereq_('./type_defaults');
62750var handleAxisDefaults = _dereq_('./axis_defaults');
62751var handleConstraintDefaults = _dereq_('./constraints').handleConstraintDefaults;
62752var handlePositionDefaults = _dereq_('./position_defaults');
62753
62754var axisIds = _dereq_('./axis_ids');
62755var id2name = axisIds.id2name;
62756var name2id = axisIds.name2id;
62757
62758var AX_ID_PATTERN = _dereq_('./constants').AX_ID_PATTERN;
62759
62760var Registry = _dereq_('../../registry');
62761var traceIs = Registry.traceIs;
62762var getComponentMethod = Registry.getComponentMethod;
62763
62764function appendList(cont, k, item) {
62765 if(Array.isArray(cont[k])) cont[k].push(item);
62766 else cont[k] = [item];
62767}
62768
62769module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
62770 var ax2traces = {};
62771 var xaMayHide = {};
62772 var yaMayHide = {};
62773 var xaMustDisplay = {};
62774 var yaMustDisplay = {};
62775 var yaMustNotReverse = {};
62776 var yaMayReverse = {};
62777 var axHasImage = {};
62778 var outerTicks = {};
62779 var noGrids = {};
62780 var i, j;
62781
62782 // look for axes in the data
62783 for(i = 0; i < fullData.length; i++) {
62784 var trace = fullData[i];
62785 if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue;
62786
62787 var xaName;
62788 if(trace.xaxis) {
62789 xaName = id2name(trace.xaxis);
62790 appendList(ax2traces, xaName, trace);
62791 } else if(trace.xaxes) {
62792 for(j = 0; j < trace.xaxes.length; j++) {
62793 appendList(ax2traces, id2name(trace.xaxes[j]), trace);
62794 }
62795 }
62796
62797 var yaName;
62798 if(trace.yaxis) {
62799 yaName = id2name(trace.yaxis);
62800 appendList(ax2traces, yaName, trace);
62801 } else if(trace.yaxes) {
62802 for(j = 0; j < trace.yaxes.length; j++) {
62803 appendList(ax2traces, id2name(trace.yaxes[j]), trace);
62804 }
62805 }
62806
62807 // logic for funnels
62808 if(trace.type === 'funnel') {
62809 if(trace.orientation === 'h') {
62810 if(xaName) xaMayHide[xaName] = true;
62811 if(yaName) yaMayReverse[yaName] = true;
62812 } else {
62813 if(yaName) yaMayHide[yaName] = true;
62814 }
62815 } else if(trace.type === 'image') {
62816 if(yaName) axHasImage[yaName] = true;
62817 if(xaName) axHasImage[xaName] = true;
62818 } else {
62819 if(yaName) {
62820 yaMustDisplay[yaName] = true;
62821 yaMustNotReverse[yaName] = true;
62822 }
62823
62824 if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
62825 if(xaName) xaMustDisplay[xaName] = true;
62826 }
62827 }
62828
62829 // Two things trigger axis visibility:
62830 // 1. is not carpet
62831 // 2. carpet that's not cheater
62832
62833 // The above check for definitely-not-cheater is not adequate. This
62834 // second list tracks which axes *could* be a cheater so that the
62835 // full condition triggering hiding is:
62836 // *could* be a cheater and *is not definitely visible*
62837 if(trace.type === 'carpet' && trace._cheater) {
62838 if(xaName) xaMayHide[xaName] = true;
62839 }
62840
62841 // check for default formatting tweaks
62842 if(traceIs(trace, '2dMap')) {
62843 outerTicks[xaName] = true;
62844 outerTicks[yaName] = true;
62845 }
62846
62847 if(traceIs(trace, 'oriented')) {
62848 var positionAxis = trace.orientation === 'h' ? yaName : xaName;
62849 noGrids[positionAxis] = true;
62850 }
62851 }
62852
62853 var subplots = layoutOut._subplots;
62854 var xIds = subplots.xaxis;
62855 var yIds = subplots.yaxis;
62856 var xNames = Lib.simpleMap(xIds, id2name);
62857 var yNames = Lib.simpleMap(yIds, id2name);
62858 var axNames = xNames.concat(yNames);
62859
62860 // plot_bgcolor only makes sense if there's a (2D) plot!
62861 // TODO: bgcolor for each subplot, to inherit from the main one
62862 var plotBgColor = Color.background;
62863 if(xIds.length && yIds.length) {
62864 plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor');
62865 }
62866
62867 var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor);
62868
62869 // name of single axis (e.g. 'xaxis', 'yaxis2')
62870 var axName;
62871 // id of single axis (e.g. 'y', 'x5')
62872 var axId;
62873 // 'x' or 'y'
62874 var axLetter;
62875 // input layout axis container
62876 var axLayoutIn;
62877 // full layout axis container
62878 var axLayoutOut;
62879
62880 function newAxLayoutOut() {
62881 var traces = ax2traces[axName] || [];
62882 axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
62883 axLayoutOut._annIndices = [];
62884 axLayoutOut._shapeIndices = [];
62885 axLayoutOut._imgIndices = [];
62886 axLayoutOut._subplotsWith = [];
62887 axLayoutOut._counterAxes = [];
62888 axLayoutOut._name = axLayoutOut._attr = axName;
62889 axLayoutOut._id = axId;
62890 }
62891
62892 function coerce(attr, dflt) {
62893 return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
62894 }
62895
62896 function coerce2(attr, dflt) {
62897 return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
62898 }
62899
62900 function getCounterAxes(axLetter) {
62901 return (axLetter === 'x') ? yIds : xIds;
62902 }
62903
62904 function getOverlayableAxes(axLetter, axName) {
62905 var list = (axLetter === 'x') ? xNames : yNames;
62906 var out = [];
62907
62908 for(var j = 0; j < list.length; j++) {
62909 var axName2 = list[j];
62910
62911 if(axName2 !== axName && !(layoutIn[axName2] || {}).overlaying) {
62912 out.push(name2id(axName2));
62913 }
62914 }
62915
62916 return out;
62917 }
62918
62919 // list of available counter axis names
62920 var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
62921 // list of all x AND y axis ids
62922 var allAxisIds = counterAxes.x.concat(counterAxes.y);
62923 // lookup and list of axis ids that axes in axNames have a reference to,
62924 // even though they are missing from allAxisIds
62925 var missingMatchedAxisIdsLookup = {};
62926 var missingMatchedAxisIds = [];
62927
62928 // fill in 'missing' axis lookup when an axis is set to match an axis
62929 // not part of the allAxisIds list, save axis type so that we can propagate
62930 // it to the missing axes
62931 function addMissingMatchedAxis() {
62932 var matchesIn = axLayoutIn.matches;
62933 if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) {
62934 missingMatchedAxisIdsLookup[matchesIn] = axLayoutIn.type;
62935 missingMatchedAxisIds = Object.keys(missingMatchedAxisIdsLookup);
62936 }
62937 }
62938
62939 var hovermode = handleHoverModeDefaults(layoutIn, layoutOut, fullData);
62940 var unifiedHover = isUnifiedHover(hovermode);
62941
62942 // first pass creates the containers, determines types, and handles most of the settings
62943 for(i = 0; i < axNames.length; i++) {
62944 axName = axNames[i];
62945 axId = name2id(axName);
62946 axLetter = axName.charAt(0);
62947
62948 if(!Lib.isPlainObject(layoutIn[axName])) {
62949 layoutIn[axName] = {};
62950 }
62951
62952 axLayoutIn = layoutIn[axName];
62953 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
62954 newAxLayoutOut();
62955
62956 var visibleDflt =
62957 (axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) ||
62958 (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]);
62959
62960 var reverseDflt =
62961 (axLetter === 'y' &&
62962 (
62963 (!yaMustNotReverse[axName] && yaMayReverse[axName]) ||
62964 axHasImage[axName]
62965 ));
62966
62967 var defaultOptions = {
62968 letter: axLetter,
62969 font: layoutOut.font,
62970 outerTicks: outerTicks[axName],
62971 showGrid: !noGrids[axName],
62972 data: ax2traces[axName] || [],
62973 bgColor: bgColor,
62974 calendar: layoutOut.calendar,
62975 automargin: true,
62976 visibleDflt: visibleDflt,
62977 reverseDflt: reverseDflt,
62978 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
62979 };
62980
62981 coerce('uirevision', layoutOut.uirevision);
62982
62983 handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions);
62984 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
62985
62986 var unifiedSpike = unifiedHover && axLetter === hovermode.charAt(0);
62987 var spikecolor = coerce2('spikecolor', unifiedHover ? axLayoutOut.color : undefined);
62988 var spikethickness = coerce2('spikethickness', unifiedHover ? 1.5 : undefined);
62989 var spikedash = coerce2('spikedash', unifiedHover ? 'dot' : undefined);
62990 var spikemode = coerce2('spikemode', unifiedHover ? 'across' : undefined);
62991 var spikesnap = coerce2('spikesnap', unifiedHover ? 'hovered data' : undefined);
62992 var showSpikes = coerce('showspikes', !!unifiedSpike || !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap);
62993
62994 if(!showSpikes) {
62995 delete axLayoutOut.spikecolor;
62996 delete axLayoutOut.spikethickness;
62997 delete axLayoutOut.spikedash;
62998 delete axLayoutOut.spikemode;
62999 delete axLayoutOut.spikesnap;
63000 }
63001
63002 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
63003 letter: axLetter,
63004 counterAxes: counterAxes[axLetter],
63005 overlayableAxes: getOverlayableAxes(axLetter, axName),
63006 grid: layoutOut.grid
63007 });
63008
63009 coerce('title.standoff');
63010
63011 addMissingMatchedAxis();
63012
63013 axLayoutOut._input = axLayoutIn;
63014 }
63015
63016 // coerce the 'missing' axes
63017 i = 0;
63018 while(i < missingMatchedAxisIds.length) {
63019 axId = missingMatchedAxisIds[i++];
63020 axName = id2name(axId);
63021 axLetter = axName.charAt(0);
63022
63023 if(!Lib.isPlainObject(layoutIn[axName])) {
63024 layoutIn[axName] = {};
63025 }
63026
63027 axLayoutIn = layoutIn[axName];
63028 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
63029 newAxLayoutOut();
63030
63031 var defaultOptions2 = {
63032 letter: axLetter,
63033 font: layoutOut.font,
63034 outerTicks: outerTicks[axName],
63035 showGrid: !noGrids[axName],
63036 data: [],
63037 bgColor: bgColor,
63038 calendar: layoutOut.calendar,
63039 automargin: true,
63040 visibleDflt: false,
63041 reverseDflt: false,
63042 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
63043 };
63044
63045 coerce('uirevision', layoutOut.uirevision);
63046
63047 axLayoutOut.type = missingMatchedAxisIdsLookup[axId] || 'linear';
63048
63049 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2, layoutOut);
63050
63051 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
63052 letter: axLetter,
63053 counterAxes: counterAxes[axLetter],
63054 overlayableAxes: getOverlayableAxes(axLetter, axName),
63055 grid: layoutOut.grid
63056 });
63057
63058 coerce('fixedrange');
63059
63060 addMissingMatchedAxis();
63061
63062 axLayoutOut._input = axLayoutIn;
63063 }
63064
63065 // quick second pass for range slider and selector defaults
63066 var rangeSliderDefaults = getComponentMethod('rangeslider', 'handleDefaults');
63067 var rangeSelectorDefaults = getComponentMethod('rangeselector', 'handleDefaults');
63068
63069 for(i = 0; i < xNames.length; i++) {
63070 axName = xNames[i];
63071 axLayoutIn = layoutIn[axName];
63072 axLayoutOut = layoutOut[axName];
63073
63074 rangeSliderDefaults(layoutIn, layoutOut, axName);
63075
63076 if(axLayoutOut.type === 'date') {
63077 rangeSelectorDefaults(
63078 axLayoutIn,
63079 axLayoutOut,
63080 layoutOut,
63081 yNames,
63082 axLayoutOut.calendar
63083 );
63084 }
63085
63086 coerce('fixedrange');
63087 }
63088
63089 for(i = 0; i < yNames.length; i++) {
63090 axName = yNames[i];
63091 axLayoutIn = layoutIn[axName];
63092 axLayoutOut = layoutOut[axName];
63093
63094 var anchoredAxis = layoutOut[id2name(axLayoutOut.anchor)];
63095
63096 var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis);
63097
63098 coerce('fixedrange', fixedRangeDflt);
63099 }
63100
63101 // Finally, handle scale constraints and matching axes.
63102 //
63103 // We need to do this after all axes have coerced both `type`
63104 // (so we link only axes of the same type) and
63105 // `fixedrange` (so we can avoid linking from OR TO a fixed axis).
63106
63107 // sets of axes linked by `scaleanchor` along with the scaleratios compounded
63108 // together, populated in handleConstraintDefaults
63109 var constraintGroups = layoutOut._axisConstraintGroups = [];
63110 // similar to _axisConstraintGroups, but for matching axes
63111 var matchGroups = layoutOut._axisMatchGroups = [];
63112 // make sure to include 'missing' axes here
63113 var allAxisIdsIncludingMissing = allAxisIds.concat(missingMatchedAxisIds);
63114 var axNamesIncludingMissing = axNames.concat(Lib.simpleMap(missingMatchedAxisIds, id2name));
63115
63116 for(i = 0; i < axNamesIncludingMissing.length; i++) {
63117 axName = axNamesIncludingMissing[i];
63118 axLetter = axName.charAt(0);
63119 axLayoutIn = layoutIn[axName];
63120 axLayoutOut = layoutOut[axName];
63121
63122 var scaleanchorDflt;
63123 if(axLetter === 'y' && !axLayoutIn.hasOwnProperty('scaleanchor') && axHasImage[axName]) {
63124 scaleanchorDflt = axLayoutOut.anchor;
63125 } else {
63126 scaleanchorDflt = undefined;
63127 }
63128
63129 var constrainDflt;
63130 if(!axLayoutIn.hasOwnProperty('constrain') && axHasImage[axName]) {
63131 constrainDflt = 'domain';
63132 } else {
63133 constrainDflt = undefined;
63134 }
63135
63136 handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, {
63137 allAxisIds: allAxisIdsIncludingMissing,
63138 layoutOut: layoutOut,
63139 scaleanchorDflt: scaleanchorDflt,
63140 constrainDflt: constrainDflt
63141 });
63142 }
63143
63144 for(i = 0; i < matchGroups.length; i++) {
63145 var group = matchGroups[i];
63146 var rng = null;
63147 var autorange = null;
63148
63149 // find 'matching' range attrs
63150 for(axId in group) {
63151 axLayoutOut = layoutOut[id2name(axId)];
63152 if(!axLayoutOut.matches) {
63153 rng = axLayoutOut.range;
63154 autorange = axLayoutOut.autorange;
63155 }
63156 }
63157 // if `ax.matches` values are reciprocal,
63158 // pick values of first axis in group
63159 if(rng === null || autorange === null) {
63160 for(axId in group) {
63161 axLayoutOut = layoutOut[id2name(axId)];
63162 rng = axLayoutOut.range;
63163 autorange = axLayoutOut.autorange;
63164 break;
63165 }
63166 }
63167 // apply matching range attrs
63168 for(axId in group) {
63169 axLayoutOut = layoutOut[id2name(axId)];
63170 if(axLayoutOut.matches) {
63171 axLayoutOut.range = rng.slice();
63172 axLayoutOut.autorange = autorange;
63173 }
63174 axLayoutOut._matchGroup = group;
63175 }
63176
63177 // remove matching axis from scaleanchor constraint groups (for now)
63178 if(constraintGroups.length) {
63179 for(axId in group) {
63180 for(j = 0; j < constraintGroups.length; j++) {
63181 var group2 = constraintGroups[j];
63182 for(var axId2 in group2) {
63183 if(axId === axId2) {
63184 Lib.warn('Axis ' + axId2 + ' is set with both ' +
63185 'a *scaleanchor* and *matches* constraint; ' +
63186 'ignoring the scale constraint.');
63187
63188 delete group2[axId2];
63189 if(Object.keys(group2).length < 2) {
63190 constraintGroups.splice(j, 1);
63191 }
63192 }
63193 }
63194 }
63195 }
63196 }
63197 }
63198};
63199
63200},{"../../components/color":50,"../../components/fx/helpers":86,"../../components/fx/hovermode_defaults":89,"../../lib":177,"../../plot_api/plot_template":212,"../../registry":272,"../layout_attributes":261,"./axis_defaults":224,"./axis_ids":225,"./constants":228,"./constraints":229,"./layout_attributes":236,"./position_defaults":239,"./type_defaults":247}],238:[function(_dereq_,module,exports){
63201/**
63202* Copyright 2012-2020, Plotly, Inc.
63203* All rights reserved.
63204*
63205* This source code is licensed under the MIT license found in the
63206* LICENSE file in the root directory of this source tree.
63207*/
63208
63209'use strict';
63210
63211var colorMix = _dereq_('tinycolor2').mix;
63212var lightFraction = _dereq_('../../components/color/attributes').lightFraction;
63213var Lib = _dereq_('../../lib');
63214
63215/**
63216 * @param {object} opts :
63217 * - dfltColor {string} : default axis color
63218 * - bgColor {string} : combined subplot bg color
63219 * - blend {number, optional} : blend percentage (to compute dflt grid color)
63220 * - showLine {boolean} : show line by default
63221 * - showGrid {boolean} : show grid by default
63222 * - noZeroLine {boolean} : don't coerce zeroline* attributes
63223 * - attributes {object} : attribute object associated with input containers
63224 */
63225module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) {
63226 opts = opts || {};
63227
63228 var dfltColor = opts.dfltColor;
63229
63230 function coerce2(attr, dflt) {
63231 return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt);
63232 }
63233
63234 var lineColor = coerce2('linecolor', dfltColor);
63235 var lineWidth = coerce2('linewidth');
63236 var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth);
63237
63238 if(!showLine) {
63239 delete containerOut.linecolor;
63240 delete containerOut.linewidth;
63241 }
63242
63243 var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString();
63244 var gridColor = coerce2('gridcolor', gridColorDflt);
63245 var gridWidth = coerce2('gridwidth');
63246 var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth);
63247
63248 if(!showGridLines) {
63249 delete containerOut.gridcolor;
63250 delete containerOut.gridwidth;
63251 }
63252
63253 if(!opts.noZeroLine) {
63254 var zeroLineColor = coerce2('zerolinecolor', dfltColor);
63255 var zeroLineWidth = coerce2('zerolinewidth');
63256 var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth);
63257
63258 if(!showZeroLine) {
63259 delete containerOut.zerolinecolor;
63260 delete containerOut.zerolinewidth;
63261 }
63262 }
63263};
63264
63265},{"../../components/color/attributes":49,"../../lib":177,"tinycolor2":32}],239:[function(_dereq_,module,exports){
63266/**
63267* Copyright 2012-2020, Plotly, Inc.
63268* All rights reserved.
63269*
63270* This source code is licensed under the MIT license found in the
63271* LICENSE file in the root directory of this source tree.
63272*/
63273
63274
63275'use strict';
63276
63277var isNumeric = _dereq_('fast-isnumeric');
63278
63279var Lib = _dereq_('../../lib');
63280
63281
63282module.exports = function handlePositionDefaults(containerIn, containerOut, coerce, options) {
63283 var counterAxes = options.counterAxes || [];
63284 var overlayableAxes = options.overlayableAxes || [];
63285 var letter = options.letter;
63286 var grid = options.grid;
63287
63288 var dfltAnchor, dfltDomain, dfltSide, dfltPosition;
63289
63290 if(grid) {
63291 dfltDomain = grid._domains[letter][grid._axisMap[containerOut._id]];
63292 dfltAnchor = grid._anchors[containerOut._id];
63293 if(dfltDomain) {
63294 dfltSide = grid[letter + 'side'].split(' ')[0];
63295 dfltPosition = grid.domain[letter][dfltSide === 'right' || dfltSide === 'top' ? 1 : 0];
63296 }
63297 }
63298
63299 // Even if there's a grid, this axis may not be in it - fall back on non-grid defaults
63300 dfltDomain = dfltDomain || [0, 1];
63301 dfltAnchor = dfltAnchor || (isNumeric(containerIn.position) ? 'free' : (counterAxes[0] || 'free'));
63302 dfltSide = dfltSide || (letter === 'x' ? 'bottom' : 'left');
63303 dfltPosition = dfltPosition || 0;
63304
63305 var anchor = Lib.coerce(containerIn, containerOut, {
63306 anchor: {
63307 valType: 'enumerated',
63308 values: ['free'].concat(counterAxes),
63309 dflt: dfltAnchor
63310 }
63311 }, 'anchor');
63312
63313 if(anchor === 'free') coerce('position', dfltPosition);
63314
63315 Lib.coerce(containerIn, containerOut, {
63316 side: {
63317 valType: 'enumerated',
63318 values: letter === 'x' ? ['bottom', 'top'] : ['left', 'right'],
63319 dflt: dfltSide
63320 }
63321 }, 'side');
63322
63323 var overlaying = false;
63324 if(overlayableAxes.length) {
63325 overlaying = Lib.coerce(containerIn, containerOut, {
63326 overlaying: {
63327 valType: 'enumerated',
63328 values: [false].concat(overlayableAxes),
63329 dflt: false
63330 }
63331 }, 'overlaying');
63332 }
63333
63334 if(!overlaying) {
63335 // TODO: right now I'm copying this domain over to overlaying axes
63336 // in ax.setscale()... but this means we still need (imperfect) logic
63337 // in the axes popover to hide domain for the overlaying axis.
63338 // perhaps I should make a private version _domain that all axes get???
63339 var domain = coerce('domain', dfltDomain);
63340
63341 // according to https://www.npmjs.com/package/canvas-size
63342 // the minimum value of max canvas width across browsers and devices is 4096
63343 // which applied in the calculation below:
63344 if(domain[0] > domain[1] - 1 / 4096) containerOut.domain = dfltDomain;
63345 Lib.noneOrAll(containerIn.domain, containerOut.domain, dfltDomain);
63346 }
63347
63348 coerce('layer');
63349
63350 return containerOut;
63351};
63352
63353},{"../../lib":177,"fast-isnumeric":15}],240:[function(_dereq_,module,exports){
63354/**
63355* Copyright 2012-2020, Plotly, Inc.
63356* All rights reserved.
63357*
63358* This source code is licensed under the MIT license found in the
63359* LICENSE file in the root directory of this source tree.
63360*/
63361
63362
63363'use strict';
63364
63365var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
63366
63367module.exports = function scaleZoom(ax, factor, centerFraction) {
63368 if(centerFraction === undefined) {
63369 centerFraction = FROM_BL[ax.constraintoward || 'center'];
63370 }
63371
63372 var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])];
63373 var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction;
63374
63375 ax.range = ax._input.range = [
63376 ax.l2r(center + (rangeLinear[0] - center) * factor),
63377 ax.l2r(center + (rangeLinear[1] - center) * factor)
63378 ];
63379};
63380
63381},{"../../constants/alignment":152}],241:[function(_dereq_,module,exports){
63382/**
63383* Copyright 2012-2020, Plotly, Inc.
63384* All rights reserved.
63385*
63386* This source code is licensed under the MIT license found in the
63387* LICENSE file in the root directory of this source tree.
63388*/
63389
63390
63391'use strict';
63392
63393var polybool = _dereq_('polybooljs');
63394
63395var Registry = _dereq_('../../registry');
63396var dashStyle = _dereq_('../../components/drawing').dashStyle;
63397var Color = _dereq_('../../components/color');
63398var Fx = _dereq_('../../components/fx');
63399var makeEventData = _dereq_('../../components/fx/helpers').makeEventData;
63400var dragHelpers = _dereq_('../../components/dragelement/helpers');
63401var freeMode = dragHelpers.freeMode;
63402var rectMode = dragHelpers.rectMode;
63403var drawMode = dragHelpers.drawMode;
63404var openMode = dragHelpers.openMode;
63405var selectMode = dragHelpers.selectMode;
63406
63407var displayOutlines = _dereq_('../../components/shapes/draw_newshape/display_outlines');
63408var handleEllipse = _dereq_('../../components/shapes/draw_newshape/helpers').handleEllipse;
63409var newShapes = _dereq_('../../components/shapes/draw_newshape/newshapes');
63410
63411var Lib = _dereq_('../../lib');
63412var polygon = _dereq_('../../lib/polygon');
63413var throttle = _dereq_('../../lib/throttle');
63414var getFromId = _dereq_('./axis_ids').getFromId;
63415var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
63416
63417var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
63418
63419var constants = _dereq_('./constants');
63420var MINSELECT = constants.MINSELECT;
63421
63422var filteredPolygon = polygon.filter;
63423var polygonTester = polygon.tester;
63424
63425var clearSelect = _dereq_('./handle_outline').clearSelect;
63426
63427var helpers = _dereq_('./helpers');
63428var p2r = helpers.p2r;
63429var axValue = helpers.axValue;
63430var getTransform = helpers.getTransform;
63431
63432function prepSelect(e, startX, startY, dragOptions, mode) {
63433 var isFreeMode = freeMode(mode);
63434 var isRectMode = rectMode(mode);
63435 var isOpenMode = openMode(mode);
63436 var isDrawMode = drawMode(mode);
63437 var isSelectMode = selectMode(mode);
63438
63439 var isLine = mode === 'drawline';
63440 var isEllipse = mode === 'drawcircle';
63441 var isLineOrEllipse = isLine || isEllipse; // cases with two start & end positions
63442
63443 var gd = dragOptions.gd;
63444 var fullLayout = gd._fullLayout;
63445 var zoomLayer = fullLayout._zoomlayer;
63446 var dragBBox = dragOptions.element.getBoundingClientRect();
63447 var plotinfo = dragOptions.plotinfo;
63448 var transform = getTransform(plotinfo);
63449 var x0 = startX - dragBBox.left;
63450 var y0 = startY - dragBBox.top;
63451 var x1 = x0;
63452 var y1 = y0;
63453 var path0 = 'M' + x0 + ',' + y0;
63454 var pw = dragOptions.xaxes[0]._length;
63455 var ph = dragOptions.yaxes[0]._length;
63456 var allAxes = dragOptions.xaxes.concat(dragOptions.yaxes);
63457 var subtract = e.altKey &&
63458 !(drawMode(mode) && isOpenMode);
63459
63460 var filterPoly, selectionTester, mergedPolygons, currentPolygon;
63461 var i, searchInfo, eventData;
63462
63463 coerceSelectionsCache(e, gd, dragOptions);
63464
63465 if(isFreeMode) {
63466 filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX);
63467 }
63468
63469 var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data(isDrawMode ? [0] : [1, 2]);
63470 var drwStyle = fullLayout.newshape;
63471
63472 outlines.enter()
63473 .append('path')
63474 .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; })
63475 .style(isDrawMode ? {
63476 opacity: drwStyle.opacity / 2,
63477 fill: isOpenMode ? undefined : drwStyle.fillcolor,
63478 stroke: drwStyle.line.color,
63479 'stroke-dasharray': dashStyle(drwStyle.line.dash, drwStyle.line.width),
63480 'stroke-width': drwStyle.line.width + 'px'
63481 } : {})
63482 .attr('fill-rule', drwStyle.fillrule)
63483 .classed('cursor-move', isDrawMode ? true : false)
63484 .attr('transform', transform)
63485 .attr('d', path0 + 'Z');
63486
63487 var corners = zoomLayer.append('path')
63488 .attr('class', 'zoombox-corners')
63489 .style({
63490 fill: Color.background,
63491 stroke: Color.defaultLine,
63492 'stroke-width': 1
63493 })
63494 .attr('transform', transform)
63495 .attr('d', 'M0,0Z');
63496
63497
63498 var throttleID = fullLayout._uid + constants.SELECTID;
63499 var selection = [];
63500
63501 // find the traces to search for selection points
63502 var searchTraces = determineSearchTraces(gd, dragOptions.xaxes,
63503 dragOptions.yaxes, dragOptions.subplot);
63504
63505 function ascending(a, b) { return a - b; }
63506
63507 // allow subplots to override fillRangeItems routine
63508 var fillRangeItems;
63509
63510 if(plotinfo.fillRangeItems) {
63511 fillRangeItems = plotinfo.fillRangeItems;
63512 } else {
63513 if(isRectMode) {
63514 fillRangeItems = function(eventData, poly) {
63515 var ranges = eventData.range = {};
63516
63517 for(i = 0; i < allAxes.length; i++) {
63518 var ax = allAxes[i];
63519 var axLetter = ax._id.charAt(0);
63520
63521 ranges[ax._id] = [
63522 p2r(ax, poly[axLetter + 'min']),
63523 p2r(ax, poly[axLetter + 'max'])
63524 ].sort(ascending);
63525 }
63526 };
63527 } else { // case of isFreeMode
63528 fillRangeItems = function(eventData, poly, filterPoly) {
63529 var dataPts = eventData.lassoPoints = {};
63530
63531 for(i = 0; i < allAxes.length; i++) {
63532 var ax = allAxes[i];
63533 dataPts[ax._id] = filterPoly.filtered.map(axValue(ax));
63534 }
63535 };
63536 }
63537 }
63538
63539 dragOptions.moveFn = function(dx0, dy0) {
63540 x1 = Math.max(0, Math.min(pw, dx0 + x0));
63541 y1 = Math.max(0, Math.min(ph, dy0 + y0));
63542
63543 var dx = Math.abs(x1 - x0);
63544 var dy = Math.abs(y1 - y0);
63545
63546 if(isRectMode) {
63547 var direction;
63548 var start, end;
63549
63550 if(isSelectMode) {
63551 var q = fullLayout.selectdirection;
63552
63553 if(q === 'any') {
63554 if(dy < Math.min(dx * 0.6, MINSELECT)) {
63555 direction = 'h';
63556 } else if(dx < Math.min(dy * 0.6, MINSELECT)) {
63557 direction = 'v';
63558 } else {
63559 direction = 'd';
63560 }
63561 } else {
63562 direction = q;
63563 }
63564
63565 switch(direction) {
63566 case 'h':
63567 start = isEllipse ? ph / 2 : 0;
63568 end = ph;
63569 break;
63570 case 'v':
63571 start = isEllipse ? pw / 2 : 0;
63572 end = pw;
63573 break;
63574 }
63575 }
63576
63577 if(isDrawMode) {
63578 switch(fullLayout.newshape.drawdirection) {
63579 case 'vertical':
63580 direction = 'h';
63581 start = isEllipse ? ph / 2 : 0;
63582 end = ph;
63583 break;
63584 case 'horizontal':
63585 direction = 'v';
63586 start = isEllipse ? pw / 2 : 0;
63587 end = pw;
63588 break;
63589 case 'ortho':
63590 if(dx < dy) {
63591 direction = 'h';
63592 start = y0;
63593 end = y1;
63594 } else {
63595 direction = 'v';
63596 start = x0;
63597 end = x1;
63598 }
63599 break;
63600 default: // i.e. case of 'diagonal'
63601 direction = 'd';
63602 }
63603 }
63604
63605 if(direction === 'h') {
63606 // horizontal motion
63607 currentPolygon = isLineOrEllipse ?
63608 handleEllipse(isEllipse, [x1, start], [x1, end]) : // using x1 instead of x0 allows adjusting the line while drawing
63609 [[x0, start], [x0, end], [x1, end], [x1, start]]; // make a vertical box
63610
63611 currentPolygon.xmin = isLineOrEllipse ? x1 : Math.min(x0, x1);
63612 currentPolygon.xmax = isLineOrEllipse ? x1 : Math.max(x0, x1);
63613 currentPolygon.ymin = Math.min(start, end);
63614 currentPolygon.ymax = Math.max(start, end);
63615 // extras to guide users in keeping a straight selection
63616 corners.attr('d', 'M' + currentPolygon.xmin + ',' + (y0 - MINSELECT) +
63617 'h-4v' + (2 * MINSELECT) + 'h4Z' +
63618 'M' + (currentPolygon.xmax - 1) + ',' + (y0 - MINSELECT) +
63619 'h4v' + (2 * MINSELECT) + 'h-4Z');
63620 } else if(direction === 'v') {
63621 // vertical motion
63622 currentPolygon = isLineOrEllipse ?
63623 handleEllipse(isEllipse, [start, y1], [end, y1]) : // using y1 instead of y0 allows adjusting the line while drawing
63624 [[start, y0], [start, y1], [end, y1], [end, y0]]; // make a horizontal box
63625
63626 currentPolygon.xmin = Math.min(start, end);
63627 currentPolygon.xmax = Math.max(start, end);
63628 currentPolygon.ymin = isLineOrEllipse ? y1 : Math.min(y0, y1);
63629 currentPolygon.ymax = isLineOrEllipse ? y1 : Math.max(y0, y1);
63630 corners.attr('d', 'M' + (x0 - MINSELECT) + ',' + currentPolygon.ymin +
63631 'v-4h' + (2 * MINSELECT) + 'v4Z' +
63632 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) +
63633 'v4h' + (2 * MINSELECT) + 'v-4Z');
63634 } else if(direction === 'd') {
63635 // diagonal motion
63636 currentPolygon = isLineOrEllipse ?
63637 handleEllipse(isEllipse, [x0, y0], [x1, y1]) :
63638 [[x0, y0], [x0, y1], [x1, y1], [x1, y0]];
63639
63640 currentPolygon.xmin = Math.min(x0, x1);
63641 currentPolygon.xmax = Math.max(x0, x1);
63642 currentPolygon.ymin = Math.min(y0, y1);
63643 currentPolygon.ymax = Math.max(y0, y1);
63644 corners.attr('d', 'M0,0Z');
63645 }
63646 } else if(isFreeMode) {
63647 filterPoly.addPt([x1, y1]);
63648 currentPolygon = filterPoly.filtered;
63649 }
63650
63651 // create outline & tester
63652 if(dragOptions.selectionDefs && dragOptions.selectionDefs.length) {
63653 mergedPolygons = mergePolygons(dragOptions.mergedPolygons, currentPolygon, subtract);
63654 currentPolygon.subtract = subtract;
63655 selectionTester = multiTester(dragOptions.selectionDefs.concat([currentPolygon]));
63656 } else {
63657 mergedPolygons = [currentPolygon];
63658 selectionTester = polygonTester(currentPolygon);
63659 }
63660
63661 // display polygons on the screen
63662 displayOutlines(convertPoly(mergedPolygons, isOpenMode), outlines, dragOptions);
63663
63664 if(isSelectMode) {
63665 throttle.throttle(
63666 throttleID,
63667 constants.SELECTDELAY,
63668 function() {
63669 selection = [];
63670
63671 var thisSelection;
63672 var traceSelections = [];
63673 var traceSelection;
63674 for(i = 0; i < searchTraces.length; i++) {
63675 searchInfo = searchTraces[i];
63676
63677 traceSelection = searchInfo._module.selectPoints(searchInfo, selectionTester);
63678 traceSelections.push(traceSelection);
63679
63680 thisSelection = fillSelectionItem(traceSelection, searchInfo);
63681
63682 if(selection.length) {
63683 for(var j = 0; j < thisSelection.length; j++) {
63684 selection.push(thisSelection[j]);
63685 }
63686 } else selection = thisSelection;
63687 }
63688
63689 eventData = {points: selection};
63690 updateSelectedState(gd, searchTraces, eventData);
63691 fillRangeItems(eventData, currentPolygon, filterPoly);
63692 dragOptions.gd.emit('plotly_selecting', eventData);
63693 }
63694 );
63695 }
63696 };
63697
63698 dragOptions.clickFn = function(numClicks, evt) {
63699 corners.remove();
63700
63701 if(gd._fullLayout._activeShapeIndex >= 0) {
63702 gd._fullLayout._deactivateShape(gd);
63703 return;
63704 }
63705 if(isDrawMode) return;
63706
63707 var clickmode = fullLayout.clickmode;
63708
63709 throttle.done(throttleID).then(function() {
63710 throttle.clear(throttleID);
63711 if(numClicks === 2) {
63712 // clear selection on doubleclick
63713 outlines.remove();
63714 for(i = 0; i < searchTraces.length; i++) {
63715 searchInfo = searchTraces[i];
63716 searchInfo._module.selectPoints(searchInfo, false);
63717 }
63718
63719 updateSelectedState(gd, searchTraces);
63720
63721 clearSelectionsCache(dragOptions);
63722
63723 gd.emit('plotly_deselect', null);
63724 } else {
63725 if(clickmode.indexOf('select') > -1) {
63726 selectOnClick(evt, gd, dragOptions.xaxes, dragOptions.yaxes,
63727 dragOptions.subplot, dragOptions, outlines);
63728 }
63729
63730 if(clickmode === 'event') {
63731 // TODO: remove in v2 - this was probably never intended to work as it does,
63732 // but in case anyone depends on it we don't want to break it now.
63733 // Note that click-to-select introduced pre v2 also emitts proper
63734 // event data when clickmode is having 'select' in its flag list.
63735 gd.emit('plotly_selected', undefined);
63736 }
63737 }
63738
63739 Fx.click(gd, evt);
63740 }).catch(Lib.error);
63741 };
63742
63743 dragOptions.doneFn = function() {
63744 corners.remove();
63745
63746 throttle.done(throttleID).then(function() {
63747 throttle.clear(throttleID);
63748 dragOptions.gd.emit('plotly_selected', eventData);
63749
63750 if(currentPolygon && dragOptions.selectionDefs) {
63751 // save last polygons
63752 currentPolygon.subtract = subtract;
63753 dragOptions.selectionDefs.push(currentPolygon);
63754
63755 // we have to keep reference to arrays container
63756 dragOptions.mergedPolygons.length = 0;
63757 [].push.apply(dragOptions.mergedPolygons, mergedPolygons);
63758 }
63759
63760 if(dragOptions.doneFnCompleted) {
63761 dragOptions.doneFnCompleted(selection);
63762 }
63763 }).catch(Lib.error);
63764
63765 if(isDrawMode) {
63766 clearSelectionsCache(dragOptions);
63767 }
63768 };
63769}
63770
63771function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) {
63772 var hoverData = gd._hoverdata;
63773 var fullLayout = gd._fullLayout;
63774 var clickmode = fullLayout.clickmode;
63775 var sendEvents = clickmode.indexOf('event') > -1;
63776 var selection = [];
63777 var searchTraces, searchInfo, currentSelectionDef, selectionTester, traceSelection;
63778 var thisTracesSelection, pointOrBinSelected, subtract, eventData, i;
63779
63780 if(isHoverDataSet(hoverData)) {
63781 coerceSelectionsCache(evt, gd, dragOptions);
63782 searchTraces = determineSearchTraces(gd, xAxes, yAxes, subplot);
63783 var clickedPtInfo = extractClickedPtInfo(hoverData, searchTraces);
63784 var isBinnedTrace = clickedPtInfo.pointNumbers.length > 0;
63785
63786
63787 // Note: potentially costly operation isPointOrBinSelected is
63788 // called as late as possible through the use of an assignment
63789 // in an if condition.
63790 if(isBinnedTrace ?
63791 isOnlyThisBinSelected(searchTraces, clickedPtInfo) :
63792 isOnlyOnePointSelected(searchTraces) &&
63793 (pointOrBinSelected = isPointOrBinSelected(clickedPtInfo))) {
63794 if(polygonOutlines) polygonOutlines.remove();
63795 for(i = 0; i < searchTraces.length; i++) {
63796 searchInfo = searchTraces[i];
63797 searchInfo._module.selectPoints(searchInfo, false);
63798 }
63799
63800 updateSelectedState(gd, searchTraces);
63801
63802 clearSelectionsCache(dragOptions);
63803
63804 if(sendEvents) {
63805 gd.emit('plotly_deselect', null);
63806 }
63807 } else {
63808 subtract = evt.shiftKey &&
63809 (pointOrBinSelected !== undefined ?
63810 pointOrBinSelected :
63811 isPointOrBinSelected(clickedPtInfo));
63812 currentSelectionDef = newPointSelectionDef(clickedPtInfo.pointNumber, clickedPtInfo.searchInfo, subtract);
63813
63814 var allSelectionDefs = dragOptions.selectionDefs.concat([currentSelectionDef]);
63815 selectionTester = multiTester(allSelectionDefs);
63816
63817 for(i = 0; i < searchTraces.length; i++) {
63818 traceSelection = searchTraces[i]._module.selectPoints(searchTraces[i], selectionTester);
63819 thisTracesSelection = fillSelectionItem(traceSelection, searchTraces[i]);
63820
63821 if(selection.length) {
63822 for(var j = 0; j < thisTracesSelection.length; j++) {
63823 selection.push(thisTracesSelection[j]);
63824 }
63825 } else selection = thisTracesSelection;
63826 }
63827
63828 eventData = {points: selection};
63829 updateSelectedState(gd, searchTraces, eventData);
63830
63831 if(currentSelectionDef && dragOptions) {
63832 dragOptions.selectionDefs.push(currentSelectionDef);
63833 }
63834
63835 if(polygonOutlines) {
63836 var polygons = dragOptions.mergedPolygons;
63837 var isOpenMode = openMode(dragOptions.dragmode);
63838
63839 // display polygons on the screen
63840 displayOutlines(convertPoly(polygons, isOpenMode), polygonOutlines, dragOptions);
63841 }
63842
63843 if(sendEvents) {
63844 gd.emit('plotly_selected', eventData);
63845 }
63846 }
63847 }
63848}
63849
63850/**
63851 * Constructs a new point selection definition object.
63852 */
63853function newPointSelectionDef(pointNumber, searchInfo, subtract) {
63854 return {
63855 pointNumber: pointNumber,
63856 searchInfo: searchInfo,
63857 subtract: subtract
63858 };
63859}
63860
63861function isPointSelectionDef(o) {
63862 return 'pointNumber' in o && 'searchInfo' in o;
63863}
63864
63865/*
63866 * Constructs a new point number tester.
63867 */
63868function newPointNumTester(pointSelectionDef) {
63869 return {
63870 xmin: 0,
63871 xmax: 0,
63872 ymin: 0,
63873 ymax: 0,
63874 pts: [],
63875 contains: function(pt, omitFirstEdge, pointNumber, searchInfo) {
63876 var idxWantedTrace = pointSelectionDef.searchInfo.cd[0].trace._expandedIndex;
63877 var idxActualTrace = searchInfo.cd[0].trace._expandedIndex;
63878 return idxActualTrace === idxWantedTrace &&
63879 pointNumber === pointSelectionDef.pointNumber;
63880 },
63881 isRect: false,
63882 degenerate: false,
63883 subtract: pointSelectionDef.subtract
63884 };
63885}
63886
63887/**
63888 * Wraps multiple selection testers.
63889 *
63890 * @param {Array} list - An array of selection testers.
63891 *
63892 * @return a selection tester object with a contains function
63893 * that can be called to evaluate a point against all wrapped
63894 * selection testers that were passed in list.
63895 */
63896function multiTester(list) {
63897 var testers = [];
63898 var xmin = isPointSelectionDef(list[0]) ? 0 : list[0][0][0];
63899 var xmax = xmin;
63900 var ymin = isPointSelectionDef(list[0]) ? 0 : list[0][0][1];
63901 var ymax = ymin;
63902
63903 for(var i = 0; i < list.length; i++) {
63904 if(isPointSelectionDef(list[i])) {
63905 testers.push(newPointNumTester(list[i]));
63906 } else {
63907 var tester = polygon.tester(list[i]);
63908 tester.subtract = list[i].subtract;
63909 testers.push(tester);
63910 xmin = Math.min(xmin, tester.xmin);
63911 xmax = Math.max(xmax, tester.xmax);
63912 ymin = Math.min(ymin, tester.ymin);
63913 ymax = Math.max(ymax, tester.ymax);
63914 }
63915 }
63916
63917 /**
63918 * Tests if the given point is within this tester.
63919 *
63920 * @param {Array} pt - [0] is the x coordinate, [1] is the y coordinate of the point.
63921 * @param {*} arg - An optional parameter to pass down to wrapped testers.
63922 * @param {number} pointNumber - The point number of the point within the underlying data array.
63923 * @param {number} searchInfo - An object identifying the trace the point is contained in.
63924 *
63925 * @return {boolean} true if point is considered to be selected, false otherwise.
63926 */
63927 function contains(pt, arg, pointNumber, searchInfo) {
63928 var contained = false;
63929 for(var i = 0; i < testers.length; i++) {
63930 if(testers[i].contains(pt, arg, pointNumber, searchInfo)) {
63931 // if contained by subtract tester - exclude the point
63932 contained = testers[i].subtract === false;
63933 }
63934 }
63935
63936 return contained;
63937 }
63938
63939 return {
63940 xmin: xmin,
63941 xmax: xmax,
63942 ymin: ymin,
63943 ymax: ymax,
63944 pts: [],
63945 contains: contains,
63946 isRect: false,
63947 degenerate: false
63948 };
63949}
63950
63951function coerceSelectionsCache(evt, gd, dragOptions) {
63952 gd._fullLayout._drawing = false;
63953
63954 var fullLayout = gd._fullLayout;
63955 var plotinfo = dragOptions.plotinfo;
63956 var dragmode = dragOptions.dragmode;
63957
63958 var selectingOnSameSubplot = (
63959 fullLayout._lastSelectedSubplot &&
63960 fullLayout._lastSelectedSubplot === plotinfo.id
63961 );
63962
63963 var hasModifierKey = (evt.shiftKey || evt.altKey) &&
63964 !(drawMode(dragmode) && openMode(dragmode));
63965
63966 if(selectingOnSameSubplot && hasModifierKey &&
63967 (plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) {
63968 // take over selection definitions from prev mode, if any
63969 dragOptions.selectionDefs = plotinfo.selection.selectionDefs;
63970 dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons;
63971 } else if(!hasModifierKey || !plotinfo.selection) {
63972 clearSelectionsCache(dragOptions);
63973 }
63974
63975 // clear selection outline when selecting a different subplot
63976 if(!selectingOnSameSubplot) {
63977 clearSelect(gd);
63978 fullLayout._lastSelectedSubplot = plotinfo.id;
63979 }
63980}
63981
63982function clearSelectionsCache(dragOptions) {
63983 var dragmode = dragOptions.dragmode;
63984 var plotinfo = dragOptions.plotinfo;
63985
63986 var gd = dragOptions.gd;
63987 if(gd._fullLayout._activeShapeIndex >= 0) {
63988 gd._fullLayout._deactivateShape(gd);
63989 }
63990
63991 if(drawMode(dragmode)) {
63992 var fullLayout = gd._fullLayout;
63993 var zoomLayer = fullLayout._zoomlayer;
63994
63995 var outlines = zoomLayer.selectAll('.select-outline-' + plotinfo.id);
63996 if(outlines && gd._fullLayout._drawing) {
63997 // add shape
63998 var shapes = newShapes(outlines, dragOptions);
63999 if(shapes) {
64000 Registry.call('_guiRelayout', gd, {
64001 shapes: shapes
64002 });
64003 }
64004
64005 gd._fullLayout._drawing = false;
64006 }
64007 }
64008
64009 plotinfo.selection = {};
64010 plotinfo.selection.selectionDefs = dragOptions.selectionDefs = [];
64011 plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = [];
64012}
64013
64014function determineSearchTraces(gd, xAxes, yAxes, subplot) {
64015 var searchTraces = [];
64016 var xAxisIds = xAxes.map(function(ax) { return ax._id; });
64017 var yAxisIds = yAxes.map(function(ax) { return ax._id; });
64018 var cd, trace, i;
64019
64020 for(i = 0; i < gd.calcdata.length; i++) {
64021 cd = gd.calcdata[i];
64022 trace = cd[0].trace;
64023
64024 if(trace.visible !== true || !trace._module || !trace._module.selectPoints) continue;
64025
64026 if(subplot && (trace.subplot === subplot || trace.geo === subplot)) {
64027 searchTraces.push(createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]));
64028 } else if(
64029 trace.type === 'splom' &&
64030 // FIXME: make sure we don't have more than single axis for splom
64031 trace._xaxes[xAxisIds[0]] && trace._yaxes[yAxisIds[0]]
64032 ) {
64033 var info = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
64034 info.scene = gd._fullLayout._splomScenes[trace.uid];
64035 searchTraces.push(info);
64036 } else if(
64037 trace.type === 'sankey'
64038 ) {
64039 var sankeyInfo = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
64040 searchTraces.push(sankeyInfo);
64041 } else {
64042 if(xAxisIds.indexOf(trace.xaxis) === -1) continue;
64043 if(yAxisIds.indexOf(trace.yaxis) === -1) continue;
64044
64045 searchTraces.push(createSearchInfo(trace._module, cd,
64046 getFromId(gd, trace.xaxis), getFromId(gd, trace.yaxis)));
64047 }
64048 }
64049
64050 return searchTraces;
64051
64052 function createSearchInfo(module, calcData, xaxis, yaxis) {
64053 return {
64054 _module: module,
64055 cd: calcData,
64056 xaxis: xaxis,
64057 yaxis: yaxis
64058 };
64059 }
64060}
64061
64062function isHoverDataSet(hoverData) {
64063 return hoverData &&
64064 Array.isArray(hoverData) &&
64065 hoverData[0].hoverOnBox !== true;
64066}
64067
64068function extractClickedPtInfo(hoverData, searchTraces) {
64069 var hoverDatum = hoverData[0];
64070 var pointNumber = -1;
64071 var pointNumbers = [];
64072 var searchInfo, i;
64073
64074 for(i = 0; i < searchTraces.length; i++) {
64075 searchInfo = searchTraces[i];
64076 if(hoverDatum.fullData._expandedIndex === searchInfo.cd[0].trace._expandedIndex) {
64077 // Special case for box (and violin)
64078 if(hoverDatum.hoverOnBox === true) {
64079 break;
64080 }
64081
64082 // Hint: in some traces like histogram, one graphical element
64083 // doesn't correspond to one particular data point, but to
64084 // bins of data points. Thus, hoverDatum can have a binNumber
64085 // property instead of pointNumber.
64086 if(hoverDatum.pointNumber !== undefined) {
64087 pointNumber = hoverDatum.pointNumber;
64088 } else if(hoverDatum.binNumber !== undefined) {
64089 pointNumber = hoverDatum.binNumber;
64090 pointNumbers = hoverDatum.pointNumbers;
64091 }
64092
64093 break;
64094 }
64095 }
64096
64097 return {
64098 pointNumber: pointNumber,
64099 pointNumbers: pointNumbers,
64100 searchInfo: searchInfo
64101 };
64102}
64103
64104function isPointOrBinSelected(clickedPtInfo) {
64105 var trace = clickedPtInfo.searchInfo.cd[0].trace;
64106 var ptNum = clickedPtInfo.pointNumber;
64107 var ptNums = clickedPtInfo.pointNumbers;
64108 var ptNumsSet = ptNums.length > 0;
64109
64110 // When pointsNumbers is set (e.g. histogram's binning),
64111 // it is assumed that when the first point of
64112 // a bin is selected, all others are as well
64113 var ptNumToTest = ptNumsSet ? ptNums[0] : ptNum;
64114
64115 // TODO potential performance improvement
64116 // Primarily we need this function to determine if a click adds
64117 // or subtracts from a selection.
64118 // In cases `trace.selectedpoints` is a huge array, indexOf
64119 // might be slow. One remedy would be to introduce a hash somewhere.
64120 return trace.selectedpoints ? trace.selectedpoints.indexOf(ptNumToTest) > -1 : false;
64121}
64122
64123function isOnlyThisBinSelected(searchTraces, clickedPtInfo) {
64124 var tracesWithSelectedPts = [];
64125 var searchInfo, trace, isSameTrace, i;
64126
64127 for(i = 0; i < searchTraces.length; i++) {
64128 searchInfo = searchTraces[i];
64129 if(searchInfo.cd[0].trace.selectedpoints && searchInfo.cd[0].trace.selectedpoints.length > 0) {
64130 tracesWithSelectedPts.push(searchInfo);
64131 }
64132 }
64133
64134 if(tracesWithSelectedPts.length === 1) {
64135 isSameTrace = tracesWithSelectedPts[0] === clickedPtInfo.searchInfo;
64136 if(isSameTrace) {
64137 trace = clickedPtInfo.searchInfo.cd[0].trace;
64138 if(trace.selectedpoints.length === clickedPtInfo.pointNumbers.length) {
64139 for(i = 0; i < clickedPtInfo.pointNumbers.length; i++) {
64140 if(trace.selectedpoints.indexOf(clickedPtInfo.pointNumbers[i]) < 0) {
64141 return false;
64142 }
64143 }
64144 return true;
64145 }
64146 }
64147 }
64148
64149 return false;
64150}
64151
64152function isOnlyOnePointSelected(searchTraces) {
64153 var len = 0;
64154 var searchInfo, trace, i;
64155
64156 for(i = 0; i < searchTraces.length; i++) {
64157 searchInfo = searchTraces[i];
64158 trace = searchInfo.cd[0].trace;
64159 if(trace.selectedpoints) {
64160 if(trace.selectedpoints.length > 1) return false;
64161
64162 len += trace.selectedpoints.length;
64163 if(len > 1) return false;
64164 }
64165 }
64166
64167 return len === 1;
64168}
64169
64170function updateSelectedState(gd, searchTraces, eventData) {
64171 var i, searchInfo, cd, trace;
64172
64173 // before anything else, update preGUI if necessary
64174 for(i = 0; i < searchTraces.length; i++) {
64175 var fullInputTrace = searchTraces[i].cd[0].trace._fullInput;
64176 var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid] || {};
64177 if(tracePreGUI.selectedpoints === undefined) {
64178 tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null;
64179 }
64180 }
64181
64182 if(eventData) {
64183 var pts = eventData.points || [];
64184
64185 for(i = 0; i < searchTraces.length; i++) {
64186 trace = searchTraces[i].cd[0].trace;
64187 trace._input.selectedpoints = trace._fullInput.selectedpoints = [];
64188 if(trace._fullInput !== trace) trace.selectedpoints = [];
64189 }
64190
64191 for(i = 0; i < pts.length; i++) {
64192 var pt = pts[i];
64193 var data = pt.data;
64194 var fullData = pt.fullData;
64195
64196 if(pt.pointIndices) {
64197 [].push.apply(data.selectedpoints, pt.pointIndices);
64198 if(trace._fullInput !== trace) {
64199 [].push.apply(fullData.selectedpoints, pt.pointIndices);
64200 }
64201 } else {
64202 data.selectedpoints.push(pt.pointIndex);
64203 if(trace._fullInput !== trace) {
64204 fullData.selectedpoints.push(pt.pointIndex);
64205 }
64206 }
64207 }
64208 } else {
64209 for(i = 0; i < searchTraces.length; i++) {
64210 trace = searchTraces[i].cd[0].trace;
64211 delete trace.selectedpoints;
64212 delete trace._input.selectedpoints;
64213 if(trace._fullInput !== trace) {
64214 delete trace._fullInput.selectedpoints;
64215 }
64216 }
64217 }
64218
64219 var hasRegl = false;
64220
64221 for(i = 0; i < searchTraces.length; i++) {
64222 searchInfo = searchTraces[i];
64223 cd = searchInfo.cd;
64224 trace = cd[0].trace;
64225
64226 if(Registry.traceIs(trace, 'regl')) {
64227 hasRegl = true;
64228 }
64229
64230 var _module = searchInfo._module;
64231 var fn = _module.styleOnSelect || _module.style;
64232 if(fn) {
64233 fn(gd, cd, cd[0].node3);
64234 if(cd[0].nodeRangePlot3) fn(gd, cd, cd[0].nodeRangePlot3);
64235 }
64236 }
64237
64238 if(hasRegl) {
64239 clearGlCanvases(gd);
64240 redrawReglTraces(gd);
64241 }
64242}
64243
64244function mergePolygons(list, poly, subtract) {
64245 var res;
64246
64247 if(subtract) {
64248 res = polybool.difference({
64249 regions: list,
64250 inverted: false
64251 }, {
64252 regions: [poly],
64253 inverted: false
64254 });
64255
64256 return res.regions;
64257 }
64258
64259 res = polybool.union({
64260 regions: list,
64261 inverted: false
64262 }, {
64263 regions: [poly],
64264 inverted: false
64265 });
64266
64267 return res.regions;
64268}
64269
64270function fillSelectionItem(selection, searchInfo) {
64271 if(Array.isArray(selection)) {
64272 var cd = searchInfo.cd;
64273 var trace = searchInfo.cd[0].trace;
64274
64275 for(var i = 0; i < selection.length; i++) {
64276 selection[i] = makeEventData(selection[i], trace, cd);
64277 }
64278 }
64279
64280 return selection;
64281}
64282
64283function convertPoly(polygonsIn, isOpenMode) { // add M and L command to draft positions
64284 var polygonsOut = [];
64285 for(var i = 0; i < polygonsIn.length; i++) {
64286 polygonsOut[i] = [];
64287 for(var j = 0; j < polygonsIn[i].length; j++) {
64288 polygonsOut[i][j] = [];
64289 polygonsOut[i][j][0] = j ? 'L' : 'M';
64290 for(var k = 0; k < polygonsIn[i][j].length; k++) {
64291 polygonsOut[i][j].push(
64292 polygonsIn[i][j][k]
64293 );
64294 }
64295 }
64296
64297 if(!isOpenMode) {
64298 polygonsOut[i].push([
64299 'Z',
64300 polygonsOut[i][0][1], // initial x
64301 polygonsOut[i][0][2] // initial y
64302 ]);
64303 }
64304 }
64305
64306 return polygonsOut;
64307}
64308
64309module.exports = {
64310 prepSelect: prepSelect,
64311 clearSelect: clearSelect,
64312 clearSelectionsCache: clearSelectionsCache,
64313 selectOnClick: selectOnClick
64314};
64315
64316},{"../../components/color":50,"../../components/dragelement/helpers":68,"../../components/drawing":72,"../../components/fx":90,"../../components/fx/helpers":86,"../../components/shapes/draw_newshape/display_outlines":135,"../../components/shapes/draw_newshape/helpers":136,"../../components/shapes/draw_newshape/newshapes":137,"../../lib":177,"../../lib/clear_gl_canvases":164,"../../lib/polygon":189,"../../lib/throttle":199,"../../plot_api/subroutines":213,"../../registry":272,"./axis_ids":225,"./constants":228,"./handle_outline":232,"./helpers":233,"polybooljs":23}],242:[function(_dereq_,module,exports){
64317/**
64318* Copyright 2012-2020, Plotly, Inc.
64319* All rights reserved.
64320*
64321* This source code is licensed under the MIT license found in the
64322* LICENSE file in the root directory of this source tree.
64323*/
64324
64325'use strict';
64326
64327var d3 = _dereq_('d3');
64328var isNumeric = _dereq_('fast-isnumeric');
64329
64330var Lib = _dereq_('../../lib');
64331var cleanNumber = Lib.cleanNumber;
64332var ms2DateTime = Lib.ms2DateTime;
64333var dateTime2ms = Lib.dateTime2ms;
64334var ensureNumber = Lib.ensureNumber;
64335var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
64336
64337var numConstants = _dereq_('../../constants/numerical');
64338var FP_SAFE = numConstants.FP_SAFE;
64339var BADNUM = numConstants.BADNUM;
64340var LOG_CLIP = numConstants.LOG_CLIP;
64341var ONEDAY = numConstants.ONEDAY;
64342var ONEHOUR = numConstants.ONEHOUR;
64343var ONEMIN = numConstants.ONEMIN;
64344var ONESEC = numConstants.ONESEC;
64345
64346var axisIds = _dereq_('./axis_ids');
64347
64348var constants = _dereq_('./constants');
64349var HOUR_PATTERN = constants.HOUR_PATTERN;
64350var WEEKDAY_PATTERN = constants.WEEKDAY_PATTERN;
64351
64352function fromLog(v) {
64353 return Math.pow(10, v);
64354}
64355
64356function isValidCategory(v) {
64357 return v !== null && v !== undefined;
64358}
64359
64360/**
64361 * Define the conversion functions for an axis data is used in 5 ways:
64362 *
64363 * d: data, in whatever form it's provided
64364 * c: calcdata: turned into numbers, but not linearized
64365 * l: linearized - same as c except for log axes (and other nonlinear
64366 * mappings later?) this is used when we need to know if it's
64367 * *possible* to show some data on this axis, without caring about
64368 * the current range
64369 * p: pixel value - mapped to the screen with current size and zoom
64370 * r: ranges, tick0, and annotation positions match one of the above
64371 * but are handled differently for different types:
64372 * - linear and date: data format (d)
64373 * - category: calcdata format (c), and will stay that way because
64374 * the data format has no continuous mapping
64375 * - log: linearized (l) format
64376 * TODO: in v2.0 we plan to change it to data format. At that point
64377 * shapes will work the same way as ranges, tick0, and annotations
64378 * so they can use this conversion too.
64379 *
64380 * Creates/updates these conversion functions, and a few more utilities
64381 * like cleanRange, and makeCalcdata
64382 *
64383 * also clears the autotick constraints ._minDtick, ._forceTick0
64384 */
64385module.exports = function setConvert(ax, fullLayout) {
64386 fullLayout = fullLayout || {};
64387
64388 var axId = (ax._id || 'x');
64389 var axLetter = axId.charAt(0);
64390
64391 function toLog(v, clip) {
64392 if(v > 0) return Math.log(v) / Math.LN10;
64393
64394 else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
64395 // clip NaN (ie past negative infinity) to LOG_CLIP axis
64396 // length past the negative edge
64397 var r0 = ax.range[0];
64398 var r1 = ax.range[1];
64399 return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1));
64400 } else return BADNUM;
64401 }
64402
64403 /*
64404 * wrapped dateTime2ms that:
64405 * - accepts ms numbers for backward compatibility
64406 * - inserts a dummy arg so calendar is the 3rd arg (see notes below).
64407 * - defaults to ax.calendar
64408 */
64409 function dt2ms(v, _, calendar, opts) {
64410 // NOTE: Changed this behavior: previously we took any numeric value
64411 // to be a ms, even if it was a string that could be a bare year.
64412 // Now we convert it as a date if at all possible, and only try
64413 // as (local) ms if that fails.
64414 var ms = dateTime2ms(v, calendar || ax.calendar);
64415 if(ms === BADNUM) {
64416 if(isNumeric(v)) {
64417 v = +v;
64418 if((opts || {}).msUTC) {
64419 // For now it is only used
64420 // to fix bar length in milliseconds.
64421 // It could be applied in other places in v2
64422 return v;
64423 }
64424
64425 // keep track of tenths of ms, that `new Date` will drop
64426 // same logic as in Lib.ms2DateTime
64427 var msecTenths = Math.floor(Lib.mod(v + 0.05, 1) * 10);
64428 var msRounded = Math.round(v - msecTenths / 10);
64429 ms = dateTime2ms(new Date(msRounded)) + msecTenths / 10;
64430 } else return BADNUM;
64431 }
64432 return ms;
64433 }
64434
64435 // wrapped ms2DateTime to insert default ax.calendar
64436 function ms2dt(v, r, calendar) {
64437 return ms2DateTime(v, r, calendar || ax.calendar);
64438 }
64439
64440 function getCategoryName(v) {
64441 return ax._categories[Math.round(v)];
64442 }
64443
64444 /*
64445 * setCategoryIndex: return the index of category v,
64446 * inserting it in the list if it's not already there
64447 *
64448 * this will enter the categories in the order it
64449 * encounters them, ie all the categories from the
64450 * first data set, then all the ones from the second
64451 * that aren't in the first etc.
64452 *
64453 * it is assumed that this function is being invoked in the
64454 * already sorted category order; otherwise there would be
64455 * a disconnect between the array and the index returned
64456 */
64457 function setCategoryIndex(v) {
64458 if(isValidCategory(v)) {
64459 if(ax._categoriesMap === undefined) {
64460 ax._categoriesMap = {};
64461 }
64462
64463 if(ax._categoriesMap[v] !== undefined) {
64464 return ax._categoriesMap[v];
64465 } else {
64466 ax._categories.push(typeof v === 'number' ? String(v) : v);
64467
64468 var curLength = ax._categories.length - 1;
64469 ax._categoriesMap[v] = curLength;
64470
64471 return curLength;
64472 }
64473 }
64474 return BADNUM;
64475 }
64476
64477 function setMultiCategoryIndex(arrayIn, len) {
64478 var arrayOut = new Array(len);
64479
64480 for(var i = 0; i < len; i++) {
64481 var v0 = (arrayIn[0] || [])[i];
64482 var v1 = (arrayIn[1] || [])[i];
64483 arrayOut[i] = getCategoryIndex([v0, v1]);
64484 }
64485
64486 return arrayOut;
64487 }
64488
64489 function getCategoryIndex(v) {
64490 if(ax._categoriesMap) {
64491 return ax._categoriesMap[v];
64492 }
64493 }
64494
64495 function getCategoryPosition(v) {
64496 // d2l/d2c variant that that won't add categories but will also
64497 // allow numbers to be mapped to the linearized axis positions
64498 var index = getCategoryIndex(v);
64499 if(index !== undefined) return index;
64500 if(isNumeric(v)) return +v;
64501 }
64502
64503 // include 2 fractional digits on pixel, for PDF zooming etc
64504 function _l2p(v, m, b) { return d3.round(b + m * v, 2); }
64505
64506 function _p2l(px, m, b) { return (px - b) / m; }
64507
64508 var l2p = function l2p(v) {
64509 if(!isNumeric(v)) return BADNUM;
64510 return _l2p(v, ax._m, ax._b);
64511 };
64512
64513 var p2l = function(px) {
64514 return _p2l(px, ax._m, ax._b);
64515 };
64516
64517 if(ax.rangebreaks) {
64518 var isY = axLetter === 'y';
64519
64520 l2p = function(v) {
64521 if(!isNumeric(v)) return BADNUM;
64522 var len = ax._rangebreaks.length;
64523 if(!len) return _l2p(v, ax._m, ax._b);
64524
64525 var flip = isY;
64526 if(ax.range[0] > ax.range[1]) flip = !flip;
64527 var signAx = flip ? -1 : 1;
64528 var pos = signAx * v;
64529
64530 var q = 0;
64531 for(var i = 0; i < len; i++) {
64532 var min = signAx * ax._rangebreaks[i].min;
64533 var max = signAx * ax._rangebreaks[i].max;
64534
64535 if(pos < min) break;
64536 if(pos > max) q = i + 1;
64537 else {
64538 // when falls into break, pick 'closest' offset
64539 q = pos < (min + max) / 2 ? i : i + 1;
64540 break;
64541 }
64542 }
64543 var b2 = ax._B[q] || 0;
64544 if(!isFinite(b2)) return 0; // avoid NaN translate e.g. in positionLabels if one keep zooming exactly into a break
64545 return _l2p(v, ax._m2, b2);
64546 };
64547
64548 p2l = function(px) {
64549 var len = ax._rangebreaks.length;
64550 if(!len) return _p2l(px, ax._m, ax._b);
64551
64552 var q = 0;
64553 for(var i = 0; i < len; i++) {
64554 if(px < ax._rangebreaks[i].pmin) break;
64555 if(px > ax._rangebreaks[i].pmax) q = i + 1;
64556 }
64557 return _p2l(px, ax._m2, ax._B[q]);
64558 };
64559 }
64560
64561 // conversions among c/l/p are fairly simple - do them together for all axis types
64562 ax.c2l = (ax.type === 'log') ? toLog : ensureNumber;
64563 ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber;
64564
64565 ax.l2p = l2p;
64566 ax.p2l = p2l;
64567
64568 ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p;
64569 ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l;
64570
64571 /*
64572 * now type-specific conversions for **ALL** other combinations
64573 * they're all written out, instead of being combinations of each other, for
64574 * both clarity and speed.
64575 */
64576 if(['linear', '-'].indexOf(ax.type) !== -1) {
64577 // all are data vals, but d and r need cleaning
64578 ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber;
64579 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ensureNumber;
64580
64581 ax.d2p = ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
64582 ax.p2d = ax.p2r = p2l;
64583
64584 ax.cleanPos = ensureNumber;
64585 } else if(ax.type === 'log') {
64586 // d and c are data vals, r and l are logged (but d and r need cleaning)
64587 ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); };
64588 ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); };
64589
64590 ax.d2c = ax.r2l = cleanNumber;
64591 ax.c2d = ax.l2r = ensureNumber;
64592
64593 ax.c2r = toLog;
64594 ax.l2d = fromLog;
64595
64596 ax.d2p = function(v, clip) { return ax.l2p(ax.d2r(v, clip)); };
64597 ax.p2d = function(px) { return fromLog(p2l(px)); };
64598
64599 ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
64600 ax.p2r = p2l;
64601
64602 ax.cleanPos = ensureNumber;
64603 } else if(ax.type === 'date') {
64604 // r and d are date strings, l and c are ms
64605
64606 /*
64607 * Any of these functions with r and d on either side, calendar is the
64608 * **3rd** argument. log has reserved the second argument.
64609 *
64610 * Unless you need the special behavior of the second arg (ms2DateTime
64611 * uses this to limit precision, toLog uses true to clip negatives
64612 * to offscreen low rather than undefined), it's safe to pass 0.
64613 */
64614 ax.d2r = ax.r2d = Lib.identity;
64615
64616 ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms;
64617 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt;
64618
64619 ax.d2p = ax.r2p = function(v, _, calendar) { return ax.l2p(dt2ms(v, 0, calendar)); };
64620 ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); };
64621
64622 ax.cleanPos = function(v) { return Lib.cleanDate(v, BADNUM, ax.calendar); };
64623 } else if(ax.type === 'category') {
64624 // d is categories (string)
64625 // c and l are indices (numbers)
64626 // r is categories or numbers
64627
64628 ax.d2c = ax.d2l = setCategoryIndex;
64629 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
64630
64631 ax.d2r = ax.d2l_noadd = getCategoryPosition;
64632
64633 ax.r2c = function(v) {
64634 var index = getCategoryPosition(v);
64635 return index !== undefined ? index : ax.fraction2r(0.5);
64636 };
64637
64638 ax.l2r = ax.c2r = ensureNumber;
64639 ax.r2l = getCategoryPosition;
64640
64641 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
64642 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
64643 ax.r2p = ax.d2p;
64644 ax.p2r = p2l;
64645
64646 ax.cleanPos = function(v) {
64647 if(typeof v === 'string' && v !== '') return v;
64648 return ensureNumber(v);
64649 };
64650 } else if(ax.type === 'multicategory') {
64651 // N.B. multicategory axes don't define d2c and d2l,
64652 // as 'data-to-calcdata' conversion needs to take into
64653 // account all data array items as in ax.makeCalcdata.
64654
64655 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
64656 ax.d2r = ax.d2l_noadd = getCategoryPosition;
64657
64658 ax.r2c = function(v) {
64659 var index = getCategoryPosition(v);
64660 return index !== undefined ? index : ax.fraction2r(0.5);
64661 };
64662
64663 ax.r2c_just_indices = getCategoryIndex;
64664
64665 ax.l2r = ax.c2r = ensureNumber;
64666 ax.r2l = getCategoryPosition;
64667
64668 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
64669 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
64670 ax.r2p = ax.d2p;
64671 ax.p2r = p2l;
64672
64673 ax.cleanPos = function(v) {
64674 if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v;
64675 return ensureNumber(v);
64676 };
64677
64678 ax.setupMultiCategory = function(fullData) {
64679 var traceIndices = ax._traceIndices;
64680 var i, j;
64681
64682 var matchGroups = fullLayout._axisMatchGroups;
64683 if(matchGroups && matchGroups.length && ax._categories.length === 0) {
64684 for(i = 0; i < matchGroups.length; i++) {
64685 var group = matchGroups[i];
64686 if(group[axId]) {
64687 for(var axId2 in group) {
64688 if(axId2 !== axId) {
64689 var ax2 = fullLayout[axisIds.id2name(axId2)];
64690 traceIndices = traceIndices.concat(ax2._traceIndices);
64691 }
64692 }
64693 }
64694 }
64695 }
64696
64697 // [ [cnt, {$cat: index}], for 1,2 ]
64698 var seen = [[0, {}], [0, {}]];
64699 // [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ]
64700 var list = [];
64701
64702 for(i = 0; i < traceIndices.length; i++) {
64703 var trace = fullData[traceIndices[i]];
64704
64705 if(axLetter in trace) {
64706 var arrayIn = trace[axLetter];
64707 var len = trace._length || Lib.minRowLength(arrayIn);
64708
64709 if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) {
64710 for(j = 0; j < len; j++) {
64711 var v0 = arrayIn[0][j];
64712 var v1 = arrayIn[1][j];
64713
64714 if(isValidCategory(v0) && isValidCategory(v1)) {
64715 list.push([v0, v1]);
64716
64717 if(!(v0 in seen[0][1])) {
64718 seen[0][1][v0] = seen[0][0]++;
64719 }
64720 if(!(v1 in seen[1][1])) {
64721 seen[1][1][v1] = seen[1][0]++;
64722 }
64723 }
64724 }
64725 }
64726 }
64727 }
64728
64729 list.sort(function(a, b) {
64730 var ind0 = seen[0][1];
64731 var d = ind0[a[0]] - ind0[b[0]];
64732 if(d) return d;
64733
64734 var ind1 = seen[1][1];
64735 return ind1[a[1]] - ind1[b[1]];
64736 });
64737
64738 for(i = 0; i < list.length; i++) {
64739 setCategoryIndex(list[i]);
64740 }
64741 };
64742 }
64743
64744 // find the range value at the specified (linear) fraction of the axis
64745 ax.fraction2r = function(v) {
64746 var rl0 = ax.r2l(ax.range[0]);
64747 var rl1 = ax.r2l(ax.range[1]);
64748 return ax.l2r(rl0 + v * (rl1 - rl0));
64749 };
64750
64751 // find the fraction of the range at the specified range value
64752 ax.r2fraction = function(v) {
64753 var rl0 = ax.r2l(ax.range[0]);
64754 var rl1 = ax.r2l(ax.range[1]);
64755 return (ax.r2l(v) - rl0) / (rl1 - rl0);
64756 };
64757
64758 /*
64759 * cleanRange: make sure range is a couplet of valid & distinct values
64760 * keep numbers away from the limits of floating point numbers,
64761 * and dates away from the ends of our date system (+/- 9999 years)
64762 *
64763 * optional param rangeAttr: operate on a different attribute, like
64764 * ax._r, rather than ax.range
64765 */
64766 ax.cleanRange = function(rangeAttr, opts) {
64767 if(!opts) opts = {};
64768 if(!rangeAttr) rangeAttr = 'range';
64769
64770 var range = Lib.nestedProperty(ax, rangeAttr).get();
64771 var i, dflt;
64772
64773 if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
64774 else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
64775 else dflt = opts.dfltRange || constants.DFLTRANGEX;
64776
64777 // make sure we don't later mutate the defaults
64778 dflt = dflt.slice();
64779
64780 if(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') {
64781 dflt[0] = 0;
64782 }
64783
64784 if(!range || range.length !== 2) {
64785 Lib.nestedProperty(ax, rangeAttr).set(dflt);
64786 return;
64787 }
64788
64789 if(ax.type === 'date' && !ax.autorange) {
64790 // check if milliseconds or js date objects are provided for range
64791 // and convert to date strings
64792 range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar);
64793 range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar);
64794 }
64795
64796 for(i = 0; i < 2; i++) {
64797 if(ax.type === 'date') {
64798 if(!Lib.isDateTime(range[i], ax.calendar)) {
64799 ax[rangeAttr] = dflt;
64800 break;
64801 }
64802
64803 if(ax.r2l(range[0]) === ax.r2l(range[1])) {
64804 // split by +/- 1 second
64805 var linCenter = Lib.constrain(ax.r2l(range[0]),
64806 Lib.MIN_MS + 1000, Lib.MAX_MS - 1000);
64807 range[0] = ax.l2r(linCenter - 1000);
64808 range[1] = ax.l2r(linCenter + 1000);
64809 break;
64810 }
64811 } else {
64812 if(!isNumeric(range[i])) {
64813 if(isNumeric(range[1 - i])) {
64814 range[i] = range[1 - i] * (i ? 10 : 0.1);
64815 } else {
64816 ax[rangeAttr] = dflt;
64817 break;
64818 }
64819 }
64820
64821 if(range[i] < -FP_SAFE) range[i] = -FP_SAFE;
64822 else if(range[i] > FP_SAFE) range[i] = FP_SAFE;
64823
64824 if(range[0] === range[1]) {
64825 // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger
64826 var inc = Math.max(1, Math.abs(range[0] * 1e-6));
64827 range[0] -= inc;
64828 range[1] += inc;
64829 }
64830 }
64831 }
64832 };
64833
64834 // set scaling to pixels
64835 ax.setScale = function(usePrivateRange) {
64836 var gs = fullLayout._size;
64837
64838 // make sure we have a domain (pull it in from the axis
64839 // this one is overlaying if necessary)
64840 if(ax.overlaying) {
64841 var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying);
64842 ax.domain = ax2.domain;
64843 }
64844
64845 // While transitions are occurring, we get a double-transform
64846 // issue if we transform the drawn layer *and* use the new axis range to
64847 // draw the data. This allows us to construct setConvert using the pre-
64848 // interaction values of the range:
64849 var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range';
64850 var calendar = ax.calendar;
64851 ax.cleanRange(rangeAttr);
64852
64853 var rl0 = ax.r2l(ax[rangeAttr][0], calendar);
64854 var rl1 = ax.r2l(ax[rangeAttr][1], calendar);
64855
64856 var isY = axLetter === 'y';
64857 if(isY) {
64858 ax._offset = gs.t + (1 - ax.domain[1]) * gs.h;
64859 ax._length = gs.h * (ax.domain[1] - ax.domain[0]);
64860 ax._m = ax._length / (rl0 - rl1);
64861 ax._b = -ax._m * rl1;
64862 } else {
64863 ax._offset = gs.l + ax.domain[0] * gs.w;
64864 ax._length = gs.w * (ax.domain[1] - ax.domain[0]);
64865 ax._m = ax._length / (rl1 - rl0);
64866 ax._b = -ax._m * rl0;
64867 }
64868
64869 // set of "N" disjoint rangebreaks inside the range
64870 ax._rangebreaks = [];
64871 // length of these rangebreaks in value space - negative on reversed axes
64872 ax._lBreaks = 0;
64873 // l2p slope (same for all intervals)
64874 ax._m2 = 0;
64875 // set of l2p offsets (one for each of the (N+1) piecewise intervals)
64876 ax._B = [];
64877
64878 if(ax.rangebreaks) {
64879 var i, brk;
64880
64881 ax._rangebreaks = ax.locateBreaks(
64882 Math.min(rl0, rl1),
64883 Math.max(rl0, rl1)
64884 );
64885
64886 if(ax._rangebreaks.length) {
64887 for(i = 0; i < ax._rangebreaks.length; i++) {
64888 brk = ax._rangebreaks[i];
64889 ax._lBreaks += Math.abs(brk.max - brk.min);
64890 }
64891
64892 var flip = isY;
64893 if(rl0 > rl1) flip = !flip;
64894 if(flip) ax._rangebreaks.reverse();
64895 var sign = flip ? -1 : 1;
64896
64897 ax._m2 = sign * ax._length / (Math.abs(rl1 - rl0) - ax._lBreaks);
64898 ax._B.push(-ax._m2 * (isY ? rl1 : rl0));
64899 for(i = 0; i < ax._rangebreaks.length; i++) {
64900 brk = ax._rangebreaks[i];
64901 ax._B.push(
64902 ax._B[ax._B.length - 1] -
64903 sign * ax._m2 * (brk.max - brk.min)
64904 );
64905 }
64906
64907 // fill pixel (i.e. 'p') min/max here,
64908 // to not have to loop through the _rangebreaks twice during `p2l`
64909 for(i = 0; i < ax._rangebreaks.length; i++) {
64910 brk = ax._rangebreaks[i];
64911 brk.pmin = l2p(brk.min);
64912 brk.pmax = l2p(brk.max);
64913 }
64914 }
64915 }
64916
64917 if(!isFinite(ax._m) || !isFinite(ax._b) || ax._length < 0) {
64918 fullLayout._replotting = false;
64919 throw new Error('Something went wrong with axis scaling');
64920 }
64921 };
64922
64923 ax.maskBreaks = function(v) {
64924 var rangebreaksIn = ax.rangebreaks || [];
64925 var bnds, b0, b1, vb, vDate;
64926
64927 for(var i = 0; i < rangebreaksIn.length; i++) {
64928 var brk = rangebreaksIn[i];
64929
64930 if(brk.enabled) {
64931 if(brk.bounds) {
64932 var pattern = brk.pattern;
64933 bnds = Lib.simpleMap(brk.bounds, pattern ?
64934 cleanNumber :
64935 ax.d2c // case of pattern: ''
64936 );
64937 b0 = bnds[0];
64938 b1 = bnds[1];
64939
64940 switch(pattern) {
64941 case WEEKDAY_PATTERN:
64942 vDate = new Date(v);
64943 vb = vDate.getUTCDay();
64944
64945 if(b0 > b1) {
64946 b1 += 7;
64947 if(vb < b0) vb += 7;
64948 }
64949
64950 break;
64951 case HOUR_PATTERN:
64952 vDate = new Date(v);
64953 var hours = vDate.getUTCHours();
64954 var minutes = vDate.getUTCMinutes();
64955 var seconds = vDate.getUTCSeconds();
64956 var milliseconds = vDate.getUTCMilliseconds();
64957
64958 vb = hours + (
64959 minutes / 60 +
64960 seconds / 3600 +
64961 milliseconds / 3600000
64962 );
64963
64964 if(b0 > b1) {
64965 b1 += 24;
64966 if(vb < b0) vb += 24;
64967 }
64968
64969 break;
64970 case '':
64971 // N.B. should work on date axes as well!
64972 // e.g. { bounds: ['2020-01-04', '2020-01-05 23:59'] }
64973 // TODO should work with reversed-range axes
64974 vb = v;
64975 break;
64976 }
64977
64978 if(vb >= b0 && vb < b1) return BADNUM;
64979 } else {
64980 var vals = Lib.simpleMap(brk.values, ax.d2c).sort(Lib.sorterAsc);
64981 for(var j = 0; j < vals.length; j++) {
64982 b0 = vals[j];
64983 b1 = b0 + brk.dvalue;
64984 if(v >= b0 && v < b1) return BADNUM;
64985 }
64986 }
64987 }
64988 }
64989 return v;
64990 };
64991
64992 ax.locateBreaks = function(r0, r1) {
64993 var i, bnds, b0, b1;
64994
64995 var rangebreaksOut = [];
64996 if(!ax.rangebreaks) return rangebreaksOut;
64997
64998 var rangebreaksIn = ax.rangebreaks.slice().sort(function(a, b) {
64999 if(a.pattern === WEEKDAY_PATTERN && b.pattern === HOUR_PATTERN) return -1;
65000 if(b.pattern === WEEKDAY_PATTERN && a.pattern === HOUR_PATTERN) return 1;
65001 return 0;
65002 });
65003
65004 var addBreak = function(min, max) {
65005 min = Lib.constrain(min, r0, r1);
65006 max = Lib.constrain(max, r0, r1);
65007 if(min === max) return;
65008
65009 var isNewBreak = true;
65010 for(var j = 0; j < rangebreaksOut.length; j++) {
65011 var brkj = rangebreaksOut[j];
65012 if(min < brkj.max && max >= brkj.min) {
65013 if(min < brkj.min) {
65014 brkj.min = min;
65015 }
65016 if(max > brkj.max) {
65017 brkj.max = max;
65018 }
65019 isNewBreak = false;
65020 }
65021 }
65022 if(isNewBreak) {
65023 rangebreaksOut.push({min: min, max: max});
65024 }
65025 };
65026
65027 for(i = 0; i < rangebreaksIn.length; i++) {
65028 var brk = rangebreaksIn[i];
65029
65030 if(brk.enabled) {
65031 if(brk.bounds) {
65032 var t0 = r0;
65033 var t1 = r1;
65034 if(brk.pattern) {
65035 // to remove decimal (most often found in auto ranges)
65036 t0 = Math.floor(t0);
65037 }
65038
65039 bnds = Lib.simpleMap(brk.bounds, brk.pattern ? cleanNumber : ax.r2l);
65040 b0 = bnds[0];
65041 b1 = bnds[1];
65042
65043 // r0 value as date
65044 var t0Date = new Date(t0);
65045 // r0 value for break pattern
65046 var bndDelta;
65047 // step in ms between rangebreaks
65048 var step;
65049
65050 switch(brk.pattern) {
65051 case WEEKDAY_PATTERN:
65052 step = 7 * ONEDAY;
65053
65054 bndDelta = (
65055 (b1 < b0 ? 7 : 0) +
65056 (b1 - b0)
65057 ) * ONEDAY;
65058
65059 t0 += b0 * ONEDAY - (
65060 t0Date.getUTCDay() * ONEDAY +
65061 t0Date.getUTCHours() * ONEHOUR +
65062 t0Date.getUTCMinutes() * ONEMIN +
65063 t0Date.getUTCSeconds() * ONESEC +
65064 t0Date.getUTCMilliseconds()
65065 );
65066 break;
65067 case HOUR_PATTERN:
65068 step = ONEDAY;
65069
65070 bndDelta = (
65071 (b1 < b0 ? 24 : 0) +
65072 (b1 - b0)
65073 ) * ONEHOUR;
65074
65075 t0 += b0 * ONEHOUR - (
65076 t0Date.getUTCHours() * ONEHOUR +
65077 t0Date.getUTCMinutes() * ONEMIN +
65078 t0Date.getUTCSeconds() * ONESEC +
65079 t0Date.getUTCMilliseconds()
65080 );
65081 break;
65082 default:
65083 t0 = Math.min(bnds[0], bnds[1]);
65084 t1 = Math.max(bnds[0], bnds[1]);
65085 step = t1 - t0;
65086 bndDelta = step;
65087 }
65088
65089 for(var t = t0; t < t1; t += step) {
65090 addBreak(t, t + bndDelta);
65091 }
65092 } else {
65093 var vals = Lib.simpleMap(brk.values, ax.d2c);
65094 for(var j = 0; j < vals.length; j++) {
65095 b0 = vals[j];
65096 b1 = b0 + brk.dvalue;
65097 addBreak(b0, b1);
65098 }
65099 }
65100 }
65101 }
65102
65103 rangebreaksOut.sort(function(a, b) { return a.min - b.min; });
65104
65105 return rangebreaksOut;
65106 };
65107
65108 // makeCalcdata: takes an x or y array and converts it
65109 // to a position on the axis object "ax"
65110 // inputs:
65111 // trace - a data object from gd.data
65112 // axLetter - a string, either 'x' or 'y', for which item
65113 // to convert (TODO: is this now always the same as
65114 // the first letter of ax._id?)
65115 // in case the expected data isn't there, make a list of
65116 // integers based on the opposite data
65117 ax.makeCalcdata = function(trace, axLetter, opts) {
65118 var arrayIn, arrayOut, i, len;
65119
65120 var axType = ax.type;
65121 var cal = axType === 'date' && trace[axLetter + 'calendar'];
65122
65123 if(axLetter in trace) {
65124 arrayIn = trace[axLetter];
65125 len = trace._length || Lib.minRowLength(arrayIn);
65126
65127 if(Lib.isTypedArray(arrayIn) && (axType === 'linear' || axType === 'log')) {
65128 if(len === arrayIn.length) {
65129 return arrayIn;
65130 } else if(arrayIn.subarray) {
65131 return arrayIn.subarray(0, len);
65132 }
65133 }
65134
65135 if(axType === 'multicategory') {
65136 return setMultiCategoryIndex(arrayIn, len);
65137 }
65138
65139 arrayOut = new Array(len);
65140 for(i = 0; i < len; i++) {
65141 arrayOut[i] = ax.d2c(arrayIn[i], 0, cal, opts);
65142 }
65143 } else {
65144 var v0 = ((axLetter + '0') in trace) ? ax.d2c(trace[axLetter + '0'], 0, cal) : 0;
65145 var dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1;
65146
65147 // the opposing data, for size if we have x and dx etc
65148 arrayIn = trace[{x: 'y', y: 'x'}[axLetter]];
65149 len = trace._length || arrayIn.length;
65150 arrayOut = new Array(len);
65151
65152 for(i = 0; i < len; i++) {
65153 arrayOut[i] = v0 + i * dv;
65154 }
65155 }
65156
65157 // mask (i.e. set to BADNUM) coords that fall inside rangebreaks
65158 if(ax.rangebreaks) {
65159 for(i = 0; i < len; i++) {
65160 arrayOut[i] = ax.maskBreaks(arrayOut[i]);
65161 }
65162 }
65163
65164 return arrayOut;
65165 };
65166
65167 ax.isValidRange = function(range) {
65168 return (
65169 Array.isArray(range) &&
65170 range.length === 2 &&
65171 isNumeric(ax.r2l(range[0])) &&
65172 isNumeric(ax.r2l(range[1]))
65173 );
65174 };
65175
65176 ax.isPtWithinRange = function(d, calendar) {
65177 var coord = ax.c2l(d[axLetter], null, calendar);
65178 var r0 = ax.r2l(ax.range[0]);
65179 var r1 = ax.r2l(ax.range[1]);
65180
65181 if(r0 < r1) {
65182 return r0 <= coord && coord <= r1;
65183 } else {
65184 // Reversed axis case.
65185 return r1 <= coord && coord <= r0;
65186 }
65187 };
65188
65189 // should skip if not category nor multicategory
65190 ax.clearCalc = function() {
65191 var emptyCategories = function() {
65192 ax._categories = [];
65193 ax._categoriesMap = {};
65194 };
65195
65196 var matchGroups = fullLayout._axisMatchGroups;
65197
65198 if(matchGroups && matchGroups.length) {
65199 var found = false;
65200
65201 for(var i = 0; i < matchGroups.length; i++) {
65202 var group = matchGroups[i];
65203
65204 if(group[axId]) {
65205 found = true;
65206 var categories = null;
65207 var categoriesMap = null;
65208
65209 for(var axId2 in group) {
65210 var ax2 = fullLayout[axisIds.id2name(axId2)];
65211 if(ax2._categories) {
65212 categories = ax2._categories;
65213 categoriesMap = ax2._categoriesMap;
65214 break;
65215 }
65216 }
65217
65218 if(categories && categoriesMap) {
65219 ax._categories = categories;
65220 ax._categoriesMap = categoriesMap;
65221 } else {
65222 emptyCategories();
65223 }
65224 break;
65225 }
65226 }
65227 if(!found) emptyCategories();
65228 } else {
65229 emptyCategories();
65230 }
65231
65232 if(ax._initialCategories) {
65233 for(var j = 0; j < ax._initialCategories.length; j++) {
65234 setCategoryIndex(ax._initialCategories[j]);
65235 }
65236 }
65237 };
65238
65239 // sort the axis (and all the matching ones) by _initialCategories
65240 // returns the indices of the traces affected by the reordering
65241 ax.sortByInitialCategories = function() {
65242 var affectedTraces = [];
65243 var emptyCategories = function() {
65244 ax._categories = [];
65245 ax._categoriesMap = {};
65246 };
65247
65248 emptyCategories();
65249
65250 if(ax._initialCategories) {
65251 for(var j = 0; j < ax._initialCategories.length; j++) {
65252 setCategoryIndex(ax._initialCategories[j]);
65253 }
65254 }
65255
65256 affectedTraces = affectedTraces.concat(ax._traceIndices);
65257
65258 // Propagate to matching axes
65259 var group = ax._matchGroup;
65260 for(var axId2 in group) {
65261 if(axId === axId2) continue;
65262 var ax2 = fullLayout[axisIds.id2name(axId2)];
65263 ax2._categories = ax._categories;
65264 ax2._categoriesMap = ax._categoriesMap;
65265 affectedTraces = affectedTraces.concat(ax2._traceIndices);
65266 }
65267 return affectedTraces;
65268 };
65269
65270 // Propagate localization into the axis so that
65271 // methods in Axes can use it w/o having to pass fullLayout
65272 // Default (non-d3) number formatting uses separators directly
65273 // dates and d3-formatted numbers use the d3 locale
65274 // Fall back on default format for dummy axes that don't care about formatting
65275 var locale = fullLayout._d3locale;
65276 if(ax.type === 'date') {
65277 ax._dateFormat = locale ? locale.timeFormat.utc : d3.time.format.utc;
65278 ax._extraFormat = fullLayout._extraFormat;
65279 }
65280 // occasionally we need _numFormat to pass through
65281 // even though it won't be needed by this axis
65282 ax._separators = fullLayout.separators;
65283 ax._numFormat = locale ? locale.numberFormat : d3.format;
65284
65285 // and for bar charts and box plots: reset forced minimum tick spacing
65286 delete ax._minDtick;
65287 delete ax._forceTick0;
65288};
65289
65290},{"../../constants/numerical":155,"../../lib":177,"./axis_ids":225,"./constants":228,"d3":13,"fast-isnumeric":15}],243:[function(_dereq_,module,exports){
65291/**
65292* Copyright 2012-2020, Plotly, Inc.
65293* All rights reserved.
65294*
65295* This source code is licensed under the MIT license found in the
65296* LICENSE file in the root directory of this source tree.
65297*/
65298
65299
65300'use strict';
65301
65302var Lib = _dereq_('../../lib');
65303var layoutAttributes = _dereq_('./layout_attributes');
65304var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
65305
65306module.exports = function handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, config) {
65307 if(!config || config.pass === 1) {
65308 handlePrefixSuffix(containerIn, containerOut, coerce, axType, options);
65309 }
65310
65311 if(!config || config.pass === 2) {
65312 handleOtherDefaults(containerIn, containerOut, coerce, axType, options);
65313 }
65314};
65315
65316function handlePrefixSuffix(containerIn, containerOut, coerce, axType, options) {
65317 var showAttrDflt = getShowAttrDflt(containerIn);
65318
65319 var tickPrefix = coerce('tickprefix');
65320 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
65321
65322 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
65323 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
65324}
65325
65326function handleOtherDefaults(containerIn, containerOut, coerce, axType, options) {
65327 var showAttrDflt = getShowAttrDflt(containerIn);
65328
65329 var tickPrefix = coerce('tickprefix');
65330 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
65331
65332 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
65333 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
65334
65335 var showTickLabels = coerce('showticklabels');
65336 if(showTickLabels) {
65337 var font = options.font || {};
65338 var contColor = containerOut.color;
65339 // as with titlefont.color, inherit axis.color only if one was
65340 // explicitly provided
65341 var dfltFontColor = (contColor && contColor !== layoutAttributes.color.dflt) ?
65342 contColor : font.color;
65343 Lib.coerceFont(coerce, 'tickfont', {
65344 family: font.family,
65345 size: font.size,
65346 color: dfltFontColor
65347 });
65348 coerce('tickangle');
65349
65350 if(axType !== 'category') {
65351 var tickFormat = coerce('tickformat');
65352
65353 handleArrayContainerDefaults(containerIn, containerOut, {
65354 name: 'tickformatstops',
65355 inclusionAttr: 'enabled',
65356 handleItemDefaults: tickformatstopDefaults
65357 });
65358 if(!containerOut.tickformatstops.length) {
65359 delete containerOut.tickformatstops;
65360 }
65361
65362 if(!tickFormat && axType !== 'date') {
65363 coerce('showexponent', showAttrDflt);
65364 coerce('exponentformat');
65365 coerce('separatethousands');
65366 }
65367 }
65368 }
65369}
65370
65371/*
65372 * Attributes 'showexponent', 'showtickprefix' and 'showticksuffix'
65373 * share values.
65374 *
65375 * If only 1 attribute is set,
65376 * the remaining attributes inherit that value.
65377 *
65378 * If 2 attributes are set to the same value,
65379 * the remaining attribute inherits that value.
65380 *
65381 * If 2 attributes are set to different values,
65382 * the remaining is set to its dflt value.
65383 *
65384 */
65385function getShowAttrDflt(containerIn) {
65386 var showAttrsAll = ['showexponent', 'showtickprefix', 'showticksuffix'];
65387 var showAttrs = showAttrsAll.filter(function(a) {
65388 return containerIn[a] !== undefined;
65389 });
65390 var sameVal = function(a) {
65391 return containerIn[a] === containerIn[showAttrs[0]];
65392 };
65393
65394 if(showAttrs.every(sameVal) || showAttrs.length === 1) {
65395 return containerIn[showAttrs[0]];
65396 }
65397}
65398
65399function tickformatstopDefaults(valueIn, valueOut) {
65400 function coerce(attr, dflt) {
65401 return Lib.coerce(valueIn, valueOut, layoutAttributes.tickformatstops, attr, dflt);
65402 }
65403
65404 var enabled = coerce('enabled');
65405 if(enabled) {
65406 coerce('dtickrange');
65407 coerce('value');
65408 }
65409}
65410
65411},{"../../lib":177,"../array_container_defaults":218,"./layout_attributes":236}],244:[function(_dereq_,module,exports){
65412/**
65413* Copyright 2012-2020, Plotly, Inc.
65414* All rights reserved.
65415*
65416* This source code is licensed under the MIT license found in the
65417* LICENSE file in the root directory of this source tree.
65418*/
65419
65420
65421'use strict';
65422
65423var Lib = _dereq_('../../lib');
65424
65425var layoutAttributes = _dereq_('./layout_attributes');
65426
65427
65428/**
65429 * options: inherits outerTicks from axes.handleAxisDefaults
65430 */
65431module.exports = function handleTickDefaults(containerIn, containerOut, coerce, options) {
65432 var tickLen = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'ticklen');
65433 var tickWidth = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickwidth');
65434 var tickColor = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickcolor', containerOut.color);
65435 var showTicks = coerce('ticks', (options.outerTicks || tickLen || tickWidth || tickColor) ? 'outside' : '');
65436
65437 if(!showTicks) {
65438 delete containerOut.ticklen;
65439 delete containerOut.tickwidth;
65440 delete containerOut.tickcolor;
65441 }
65442};
65443
65444},{"../../lib":177,"./layout_attributes":236}],245:[function(_dereq_,module,exports){
65445/**
65446* Copyright 2012-2020, Plotly, Inc.
65447* All rights reserved.
65448*
65449* This source code is licensed under the MIT license found in the
65450* LICENSE file in the root directory of this source tree.
65451*/
65452
65453'use strict';
65454
65455var cleanTicks = _dereq_('./clean_ticks');
65456var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
65457
65458module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) {
65459 function readInput(attr) {
65460 var v = containerIn[attr];
65461 return (
65462 v !== undefined
65463 ) ? v : (containerOut._template || {})[attr];
65464 }
65465
65466 var _tick0 = readInput('tick0');
65467 var _dtick = readInput('dtick');
65468 var _tickvals = readInput('tickvals');
65469
65470 var tickmodeDefault = isArrayOrTypedArray(_tickvals) ? 'array' :
65471 _dtick ? 'linear' :
65472 'auto';
65473 var tickmode = coerce('tickmode', tickmodeDefault);
65474
65475 if(tickmode === 'auto') coerce('nticks');
65476 else if(tickmode === 'linear') {
65477 // dtick is usually a positive number, but there are some
65478 // special strings available for log or date axes
65479 // tick0 also has special logic
65480 var dtick = containerOut.dtick = cleanTicks.dtick(
65481 _dtick, axType);
65482 containerOut.tick0 = cleanTicks.tick0(
65483 _tick0, axType, containerOut.calendar, dtick);
65484 } else if(axType !== 'multicategory') {
65485 var tickvals = coerce('tickvals');
65486 if(tickvals === undefined) containerOut.tickmode = 'auto';
65487 else coerce('ticktext');
65488 }
65489};
65490
65491},{"../../lib":177,"./clean_ticks":227}],246:[function(_dereq_,module,exports){
65492/**
65493* Copyright 2012-2020, Plotly, Inc.
65494* All rights reserved.
65495*
65496* This source code is licensed under the MIT license found in the
65497* LICENSE file in the root directory of this source tree.
65498*/
65499
65500'use strict';
65501
65502var d3 = _dereq_('d3');
65503
65504var Registry = _dereq_('../../registry');
65505var Lib = _dereq_('../../lib');
65506var Drawing = _dereq_('../../components/drawing');
65507var Axes = _dereq_('./axes');
65508
65509/**
65510 * transitionAxes
65511 *
65512 * transition axes from one set of ranges to another, using a svg
65513 * transformations, similar to during panning.
65514 *
65515 * @param {DOM element | object} gd
65516 * @param {array} edits : array of 'edits', each item with
65517 * - plotinfo {object} subplot object
65518 * - xr0 {array} initial x-range
65519 * - xr1 {array} end x-range
65520 * - yr0 {array} initial y-range
65521 * - yr1 {array} end y-range
65522 * @param {object} transitionOpts
65523 * @param {function} makeOnCompleteCallback
65524 */
65525module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) {
65526 var fullLayout = gd._fullLayout;
65527
65528 // special case for redraw:false Plotly.animate that relies on this
65529 // to update axis-referenced layout components
65530 if(edits.length === 0) {
65531 Axes.redrawComponents(gd);
65532 return;
65533 }
65534
65535 function unsetSubplotTransform(subplot) {
65536 var xa = subplot.xaxis;
65537 var ya = subplot.yaxis;
65538
65539 fullLayout._defs.select('#' + subplot.clipId + '> rect')
65540 .call(Drawing.setTranslate, 0, 0)
65541 .call(Drawing.setScale, 1, 1);
65542
65543 subplot.plot
65544 .call(Drawing.setTranslate, xa._offset, ya._offset)
65545 .call(Drawing.setScale, 1, 1);
65546
65547 var traceGroups = subplot.plot.selectAll('.scatterlayer .trace');
65548
65549 // This is specifically directed at scatter traces, applying an inverse
65550 // scale to individual points to counteract the scale of the trace
65551 // as a whole:
65552 traceGroups.selectAll('.point')
65553 .call(Drawing.setPointGroupScale, 1, 1);
65554 traceGroups.selectAll('.textpoint')
65555 .call(Drawing.setTextPointsScale, 1, 1);
65556 traceGroups
65557 .call(Drawing.hideOutsideRangePoints, subplot);
65558 }
65559
65560 function updateSubplot(edit, progress) {
65561 var plotinfo = edit.plotinfo;
65562 var xa = plotinfo.xaxis;
65563 var ya = plotinfo.yaxis;
65564 var xlen = xa._length;
65565 var ylen = ya._length;
65566 var editX = !!edit.xr1;
65567 var editY = !!edit.yr1;
65568 var viewBox = [];
65569
65570 if(editX) {
65571 var xr0 = Lib.simpleMap(edit.xr0, xa.r2l);
65572 var xr1 = Lib.simpleMap(edit.xr1, xa.r2l);
65573 var dx0 = xr0[1] - xr0[0];
65574 var dx1 = xr1[1] - xr1[0];
65575 viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen;
65576 viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0);
65577 xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]);
65578 xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]);
65579 } else {
65580 viewBox[0] = 0;
65581 viewBox[2] = xlen;
65582 }
65583
65584 if(editY) {
65585 var yr0 = Lib.simpleMap(edit.yr0, ya.r2l);
65586 var yr1 = Lib.simpleMap(edit.yr1, ya.r2l);
65587 var dy0 = yr0[1] - yr0[0];
65588 var dy1 = yr1[1] - yr1[0];
65589 viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen;
65590 viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0);
65591 ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]);
65592 ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]);
65593 } else {
65594 viewBox[1] = 0;
65595 viewBox[3] = ylen;
65596 }
65597
65598 Axes.drawOne(gd, xa, {skipTitle: true});
65599 Axes.drawOne(gd, ya, {skipTitle: true});
65600 Axes.redrawComponents(gd, [xa._id, ya._id]);
65601
65602 var xScaleFactor = editX ? xlen / viewBox[2] : 1;
65603 var yScaleFactor = editY ? ylen / viewBox[3] : 1;
65604 var clipDx = editX ? viewBox[0] : 0;
65605 var clipDy = editY ? viewBox[1] : 0;
65606 var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0;
65607 var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0;
65608 var plotDx = xa._offset - fracDx;
65609 var plotDy = ya._offset - fracDy;
65610
65611 plotinfo.clipRect
65612 .call(Drawing.setTranslate, clipDx, clipDy)
65613 .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor);
65614
65615 plotinfo.plot
65616 .call(Drawing.setTranslate, plotDx, plotDy)
65617 .call(Drawing.setScale, xScaleFactor, yScaleFactor);
65618
65619 // apply an inverse scale to individual points to counteract
65620 // the scale of the trace group.
65621 Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor);
65622 Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor);
65623 }
65624
65625 var onComplete;
65626 if(makeOnCompleteCallback) {
65627 // This module makes the choice whether or not it notifies Plotly.transition
65628 // about completion:
65629 onComplete = makeOnCompleteCallback();
65630 }
65631
65632 function transitionComplete() {
65633 var aobj = {};
65634
65635 for(var i = 0; i < edits.length; i++) {
65636 var edit = edits[i];
65637 var xa = edit.plotinfo.xaxis;
65638 var ya = edit.plotinfo.yaxis;
65639 if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice();
65640 if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice();
65641 }
65642
65643 // Signal that this transition has completed:
65644 onComplete && onComplete();
65645
65646 return Registry.call('relayout', gd, aobj).then(function() {
65647 for(var i = 0; i < edits.length; i++) {
65648 unsetSubplotTransform(edits[i].plotinfo);
65649 }
65650 });
65651 }
65652
65653 function transitionInterrupt() {
65654 var aobj = {};
65655
65656 for(var i = 0; i < edits.length; i++) {
65657 var edit = edits[i];
65658 var xa = edit.plotinfo.xaxis;
65659 var ya = edit.plotinfo.yaxis;
65660 if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice();
65661 if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice();
65662 }
65663
65664 return Registry.call('relayout', gd, aobj).then(function() {
65665 for(var i = 0; i < edits.length; i++) {
65666 unsetSubplotTransform(edits[i].plotinfo);
65667 }
65668 });
65669 }
65670
65671 var t1, t2, raf;
65672 var easeFn = d3.ease(transitionOpts.easing);
65673
65674 gd._transitionData._interruptCallbacks.push(function() {
65675 window.cancelAnimationFrame(raf);
65676 raf = null;
65677 return transitionInterrupt();
65678 });
65679
65680 function doFrame() {
65681 t2 = Date.now();
65682
65683 var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration);
65684 var progress = easeFn(tInterp);
65685
65686 for(var i = 0; i < edits.length; i++) {
65687 updateSubplot(edits[i], progress);
65688 }
65689
65690 if(t2 - t1 > transitionOpts.duration) {
65691 transitionComplete();
65692 raf = window.cancelAnimationFrame(doFrame);
65693 } else {
65694 raf = window.requestAnimationFrame(doFrame);
65695 }
65696 }
65697
65698 t1 = Date.now();
65699 raf = window.requestAnimationFrame(doFrame);
65700
65701 return Promise.resolve();
65702};
65703
65704},{"../../components/drawing":72,"../../lib":177,"../../registry":272,"./axes":222,"d3":13}],247:[function(_dereq_,module,exports){
65705/**
65706* Copyright 2012-2020, Plotly, Inc.
65707* All rights reserved.
65708*
65709* This source code is licensed under the MIT license found in the
65710* LICENSE file in the root directory of this source tree.
65711*/
65712
65713'use strict';
65714
65715var traceIs = _dereq_('../../registry').traceIs;
65716var autoType = _dereq_('./axis_autotype');
65717
65718/*
65719 * data: the plot data to use in choosing auto type
65720 * name: axis object name (ie 'xaxis') if one should be stored
65721 */
65722module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
65723 var axType = coerce('type', (options.splomStash || {}).type);
65724
65725 if(axType === '-') {
65726 setAutoType(containerOut, options.data);
65727
65728 if(containerOut.type === '-') {
65729 containerOut.type = 'linear';
65730 } else {
65731 // copy autoType back to input axis
65732 // note that if this object didn't exist
65733 // in the input layout, we have to put it in
65734 // this happens in the main supplyDefaults function
65735 containerIn.type = containerOut.type;
65736 }
65737 }
65738};
65739
65740function setAutoType(ax, data) {
65741 // new logic: let people specify any type they want,
65742 // only autotype if type is '-'
65743 if(ax.type !== '-') return;
65744
65745 var id = ax._id;
65746 var axLetter = id.charAt(0);
65747 var i;
65748
65749 // support 3d
65750 if(id.indexOf('scene') !== -1) id = axLetter;
65751
65752 var d0 = getFirstNonEmptyTrace(data, id, axLetter);
65753 if(!d0) return;
65754
65755 // first check for histograms, as the count direction
65756 // should always default to a linear axis
65757 if(d0.type === 'histogram' &&
65758 axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v']
65759 ) {
65760 ax.type = 'linear';
65761 return;
65762 }
65763
65764 var calAttr = axLetter + 'calendar';
65765 var calendar = d0[calAttr];
65766 var opts = {noMultiCategory: !traceIs(d0, 'cartesian') || traceIs(d0, 'noMultiCategory')};
65767
65768 // To not confuse 2D x/y used for per-box sample points for multicategory coordinates
65769 if(d0.type === 'box' && d0._hasPreCompStats &&
65770 axLetter === {h: 'x', v: 'y'}[d0.orientation || 'v']
65771 ) {
65772 opts.noMultiCategory = true;
65773 }
65774
65775 // check all boxes on this x axis to see
65776 // if they're dates, numbers, or categories
65777 if(isBoxWithoutPositionCoords(d0, axLetter)) {
65778 var posLetter = getBoxPosLetter(d0);
65779 var boxPositions = [];
65780
65781 for(i = 0; i < data.length; i++) {
65782 var trace = data[i];
65783 if(!traceIs(trace, 'box-violin') || (trace[axLetter + 'axis'] || axLetter) !== id) continue;
65784
65785 if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]);
65786 else if(trace.name !== undefined) boxPositions.push(trace.name);
65787 else boxPositions.push('text');
65788
65789 if(trace[calAttr] !== calendar) calendar = undefined;
65790 }
65791
65792 ax.type = autoType(boxPositions, calendar, opts);
65793 } else if(d0.type === 'splom') {
65794 var dimensions = d0.dimensions;
65795 var dim = dimensions[d0._axesDim[id]];
65796 if(dim.visible) ax.type = autoType(dim.values, calendar, opts);
65797 } else {
65798 ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar, opts);
65799 }
65800}
65801
65802function getFirstNonEmptyTrace(data, id, axLetter) {
65803 for(var i = 0; i < data.length; i++) {
65804 var trace = data[i];
65805
65806 if(trace.type === 'splom' &&
65807 trace._length > 0 &&
65808 (trace['_' + axLetter + 'axes'] || {})[id]
65809 ) {
65810 return trace;
65811 }
65812
65813 if((trace[axLetter + 'axis'] || axLetter) === id) {
65814 if(isBoxWithoutPositionCoords(trace, axLetter)) {
65815 return trace;
65816 } else if((trace[axLetter] || []).length || trace[axLetter + '0']) {
65817 return trace;
65818 }
65819 }
65820 }
65821}
65822
65823function getBoxPosLetter(trace) {
65824 return {v: 'x', h: 'y'}[trace.orientation || 'v'];
65825}
65826
65827function isBoxWithoutPositionCoords(trace, axLetter) {
65828 var posLetter = getBoxPosLetter(trace);
65829 var isBox = traceIs(trace, 'box-violin');
65830 var isCandlestick = traceIs(trace._fullInput || {}, 'candlestick');
65831
65832 return (
65833 isBox &&
65834 !isCandlestick &&
65835 axLetter === posLetter &&
65836 trace[posLetter] === undefined &&
65837 trace[posLetter + '0'] === undefined
65838 );
65839}
65840
65841},{"../../registry":272,"./axis_autotype":223}],248:[function(_dereq_,module,exports){
65842/**
65843* Copyright 2012-2020, Plotly, Inc.
65844* All rights reserved.
65845*
65846* This source code is licensed under the MIT license found in the
65847* LICENSE file in the root directory of this source tree.
65848*/
65849
65850'use strict';
65851
65852var Registry = _dereq_('../registry');
65853var Lib = _dereq_('../lib');
65854
65855/*
65856 * Create or update an observer. This function is designed to be
65857 * idempotent so that it can be called over and over as the component
65858 * updates, and will attach and detach listeners as needed.
65859 *
65860 * @param {optional object} container
65861 * An object on which the observer is stored. This is the mechanism
65862 * by which it is idempotent. If it already exists, another won't be
65863 * added. Each time it's called, the value lookup table is updated.
65864 * @param {array} commandList
65865 * An array of commands, following either `buttons` of `updatemenus`
65866 * or `steps` of `sliders`.
65867 * @param {function} onchange
65868 * A listener called when the value is changed. Receives data object
65869 * with information about the new state.
65870 */
65871exports.manageCommandObserver = function(gd, container, commandList, onchange) {
65872 var ret = {};
65873 var enabled = true;
65874
65875 if(container && container._commandObserver) {
65876 ret = container._commandObserver;
65877 }
65878
65879 if(!ret.cache) {
65880 ret.cache = {};
65881 }
65882
65883 // Either create or just recompute this:
65884 ret.lookupTable = {};
65885
65886 var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
65887
65888 if(container && container._commandObserver) {
65889 if(!binding) {
65890 // If container exists and there are no longer any bindings,
65891 // remove existing:
65892 if(container._commandObserver.remove) {
65893 container._commandObserver.remove();
65894 container._commandObserver = null;
65895 return ret;
65896 }
65897 } else {
65898 // If container exists and there *are* bindings, then the lookup
65899 // table should have been updated and check is already attached,
65900 // so there's nothing to be done:
65901 return ret;
65902 }
65903 }
65904
65905 // Determine whether there's anything to do for this binding:
65906
65907 if(binding) {
65908 // Build the cache:
65909 bindingValueHasChanged(gd, binding, ret.cache);
65910
65911 ret.check = function check() {
65912 if(!enabled) return;
65913
65914 var update = bindingValueHasChanged(gd, binding, ret.cache);
65915
65916 if(update.changed && onchange) {
65917 // Disable checks for the duration of this command in order to avoid
65918 // infinite loops:
65919 if(ret.lookupTable[update.value] !== undefined) {
65920 ret.disable();
65921 Promise.resolve(onchange({
65922 value: update.value,
65923 type: binding.type,
65924 prop: binding.prop,
65925 traces: binding.traces,
65926 index: ret.lookupTable[update.value]
65927 })).then(ret.enable, ret.enable);
65928 }
65929 }
65930
65931 return update.changed;
65932 };
65933
65934 var checkEvents = [
65935 'plotly_relayout',
65936 'plotly_redraw',
65937 'plotly_restyle',
65938 'plotly_update',
65939 'plotly_animatingframe',
65940 'plotly_afterplot'
65941 ];
65942
65943 for(var i = 0; i < checkEvents.length; i++) {
65944 gd._internalOn(checkEvents[i], ret.check);
65945 }
65946
65947 ret.remove = function() {
65948 for(var i = 0; i < checkEvents.length; i++) {
65949 gd._removeInternalListener(checkEvents[i], ret.check);
65950 }
65951 };
65952 } else {
65953 // TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
65954 // is a start
65955 Lib.log('Unable to automatically bind plot updates to API command');
65956
65957 ret.lookupTable = {};
65958 ret.remove = function() {};
65959 }
65960
65961 ret.disable = function disable() {
65962 enabled = false;
65963 };
65964
65965 ret.enable = function enable() {
65966 enabled = true;
65967 };
65968
65969 if(container) {
65970 container._commandObserver = ret;
65971 }
65972
65973 return ret;
65974};
65975
65976/*
65977 * This function checks to see if an array of objects containing
65978 * method and args properties is compatible with automatic two-way
65979 * binding. The criteria right now are that
65980 *
65981 * 1. multiple traces may be affected
65982 * 2. only one property may be affected
65983 * 3. the same property must be affected by all commands
65984 */
65985exports.hasSimpleAPICommandBindings = function(gd, commandList, bindingsByValue) {
65986 var i;
65987 var n = commandList.length;
65988
65989 var refBinding;
65990
65991 for(i = 0; i < n; i++) {
65992 var binding;
65993 var command = commandList[i];
65994 var method = command.method;
65995 var args = command.args;
65996
65997 if(!Array.isArray(args)) args = [];
65998
65999 // If any command has no method, refuse to bind:
66000 if(!method) {
66001 return false;
66002 }
66003 var bindings = exports.computeAPICommandBindings(gd, method, args);
66004
66005 // Right now, handle one and *only* one property being set:
66006 if(bindings.length !== 1) {
66007 return false;
66008 }
66009
66010 if(!refBinding) {
66011 refBinding = bindings[0];
66012 if(Array.isArray(refBinding.traces)) {
66013 refBinding.traces.sort();
66014 }
66015 } else {
66016 binding = bindings[0];
66017 if(binding.type !== refBinding.type) {
66018 return false;
66019 }
66020 if(binding.prop !== refBinding.prop) {
66021 return false;
66022 }
66023 if(Array.isArray(refBinding.traces)) {
66024 if(Array.isArray(binding.traces)) {
66025 binding.traces.sort();
66026 for(var j = 0; j < refBinding.traces.length; j++) {
66027 if(refBinding.traces[j] !== binding.traces[j]) {
66028 return false;
66029 }
66030 }
66031 } else {
66032 return false;
66033 }
66034 } else {
66035 if(binding.prop !== refBinding.prop) {
66036 return false;
66037 }
66038 }
66039 }
66040
66041 binding = bindings[0];
66042 var value = binding.value;
66043 if(Array.isArray(value)) {
66044 if(value.length === 1) {
66045 value = value[0];
66046 } else {
66047 return false;
66048 }
66049 }
66050 if(bindingsByValue) {
66051 bindingsByValue[value] = i;
66052 }
66053 }
66054
66055 return refBinding;
66056};
66057
66058function bindingValueHasChanged(gd, binding, cache) {
66059 var container, value, obj;
66060 var changed = false;
66061
66062 if(binding.type === 'data') {
66063 // If it's data, we need to get a trace. Based on the limited scope
66064 // of what we cover, we can just take the first trace from the list,
66065 // or otherwise just the first trace:
66066 container = gd._fullData[binding.traces !== null ? binding.traces[0] : 0];
66067 } else if(binding.type === 'layout') {
66068 container = gd._fullLayout;
66069 } else {
66070 return false;
66071 }
66072
66073 value = Lib.nestedProperty(container, binding.prop).get();
66074
66075 obj = cache[binding.type] = cache[binding.type] || {};
66076
66077 if(obj.hasOwnProperty(binding.prop)) {
66078 if(obj[binding.prop] !== value) {
66079 changed = true;
66080 }
66081 }
66082
66083 obj[binding.prop] = value;
66084
66085 return {
66086 changed: changed,
66087 value: value
66088 };
66089}
66090
66091/*
66092 * Execute an API command. There's really not much to this; it just provides
66093 * a common hook so that implementations don't need to be synchronized across
66094 * multiple components with the ability to invoke API commands.
66095 *
66096 * @param {string} method
66097 * The name of the plotly command to execute. Must be one of 'animate',
66098 * 'restyle', 'relayout', 'update'.
66099 * @param {array} args
66100 * A list of arguments passed to the API command
66101 */
66102exports.executeAPICommand = function(gd, method, args) {
66103 if(method === 'skip') return Promise.resolve();
66104
66105 var _method = Registry.apiMethodRegistry[method];
66106 var allArgs = [gd];
66107 if(!Array.isArray(args)) args = [];
66108
66109 for(var i = 0; i < args.length; i++) {
66110 allArgs.push(args[i]);
66111 }
66112
66113 return _method.apply(null, allArgs).catch(function(err) {
66114 Lib.warn('API call to Plotly.' + method + ' rejected.', err);
66115 return Promise.reject(err);
66116 });
66117};
66118
66119exports.computeAPICommandBindings = function(gd, method, args) {
66120 var bindings;
66121
66122 if(!Array.isArray(args)) args = [];
66123
66124 switch(method) {
66125 case 'restyle':
66126 bindings = computeDataBindings(gd, args);
66127 break;
66128 case 'relayout':
66129 bindings = computeLayoutBindings(gd, args);
66130 break;
66131 case 'update':
66132 bindings = computeDataBindings(gd, [args[0], args[2]])
66133 .concat(computeLayoutBindings(gd, [args[1]]));
66134 break;
66135 case 'animate':
66136 bindings = computeAnimateBindings(gd, args);
66137 break;
66138 default:
66139 // This is the case where intelligent logic about what affects
66140 // this command is not implemented. It causes no ill effects.
66141 // For example, addFrames simply won't bind to a control component.
66142 bindings = [];
66143 }
66144 return bindings;
66145};
66146
66147function computeAnimateBindings(gd, args) {
66148 // We'll assume that the only relevant modification an animation
66149 // makes that's meaningfully tracked is the frame:
66150 if(Array.isArray(args[0]) && args[0].length === 1 && ['string', 'number'].indexOf(typeof args[0][0]) !== -1) {
66151 return [{type: 'layout', prop: '_currentFrame', value: args[0][0].toString()}];
66152 } else {
66153 return [];
66154 }
66155}
66156
66157function computeLayoutBindings(gd, args) {
66158 var bindings = [];
66159
66160 var astr = args[0];
66161 var aobj = {};
66162 if(typeof astr === 'string') {
66163 aobj[astr] = args[1];
66164 } else if(Lib.isPlainObject(astr)) {
66165 aobj = astr;
66166 } else {
66167 return bindings;
66168 }
66169
66170 crawl(aobj, function(path, attrName, attr) {
66171 bindings.push({type: 'layout', prop: path, value: attr});
66172 }, '', 0);
66173
66174 return bindings;
66175}
66176
66177function computeDataBindings(gd, args) {
66178 var traces, astr, val, aobj;
66179 var bindings = [];
66180
66181 // Logic copied from Plotly.restyle:
66182 astr = args[0];
66183 val = args[1];
66184 traces = args[2];
66185 aobj = {};
66186 if(typeof astr === 'string') {
66187 aobj[astr] = val;
66188 } else if(Lib.isPlainObject(astr)) {
66189 // the 3-arg form
66190 aobj = astr;
66191
66192 if(traces === undefined) {
66193 traces = val;
66194 }
66195 } else {
66196 return bindings;
66197 }
66198
66199 if(traces === undefined) {
66200 // Explicitly assign this to null instead of undefined:
66201 traces = null;
66202 }
66203
66204 crawl(aobj, function(path, attrName, _attr) {
66205 var thisTraces;
66206 var attr;
66207
66208 if(Array.isArray(_attr)) {
66209 attr = _attr.slice();
66210
66211 var nAttr = Math.min(attr.length, gd.data.length);
66212 if(traces) {
66213 nAttr = Math.min(nAttr, traces.length);
66214 }
66215 thisTraces = [];
66216 for(var j = 0; j < nAttr; j++) {
66217 thisTraces[j] = traces ? traces[j] : j;
66218 }
66219 } else {
66220 attr = _attr;
66221 thisTraces = traces ? traces.slice() : null;
66222 }
66223
66224 // Convert [7] to just 7 when traces is null:
66225 if(thisTraces === null) {
66226 if(Array.isArray(attr)) {
66227 attr = attr[0];
66228 }
66229 } else if(Array.isArray(thisTraces)) {
66230 if(!Array.isArray(attr)) {
66231 var tmp = attr;
66232 attr = [];
66233 for(var i = 0; i < thisTraces.length; i++) {
66234 attr[i] = tmp;
66235 }
66236 }
66237 attr.length = Math.min(thisTraces.length, attr.length);
66238 }
66239
66240 bindings.push({
66241 type: 'data',
66242 prop: path,
66243 traces: thisTraces,
66244 value: attr
66245 });
66246 }, '', 0);
66247
66248 return bindings;
66249}
66250
66251function crawl(attrs, callback, path, depth) {
66252 Object.keys(attrs).forEach(function(attrName) {
66253 var attr = attrs[attrName];
66254
66255 if(attrName[0] === '_') return;
66256
66257 var thisPath = path + (depth > 0 ? '.' : '') + attrName;
66258
66259 if(Lib.isPlainObject(attr)) {
66260 crawl(attr, callback, thisPath, depth + 1);
66261 } else {
66262 // Only execute the callback on leaf nodes:
66263 callback(thisPath, attrName, attr);
66264 }
66265 });
66266}
66267
66268},{"../lib":177,"../registry":272}],249:[function(_dereq_,module,exports){
66269/**
66270* Copyright 2012-2020, Plotly, Inc.
66271* All rights reserved.
66272*
66273* This source code is licensed under the MIT license found in the
66274* LICENSE file in the root directory of this source tree.
66275*/
66276
66277'use strict';
66278
66279var extendFlat = _dereq_('../lib/extend').extendFlat;
66280
66281/**
66282 * Make a xy domain attribute group
66283 *
66284 * @param {object} opts
66285 * @param {string}
66286 * opts.name: name to be inserted in the default description
66287 * @param {boolean}
66288 * opts.trace: set to true for trace containers
66289 * @param {string}
66290 * opts.editType: editType for all pieces
66291 * @param {boolean}
66292 * opts.noGridCell: set to true to omit `row` and `column`
66293 *
66294 * @param {object} extra
66295 * @param {string}
66296 * extra.description: extra description. N.B we use
66297 * a separate extra container to make it compatible with
66298 * the compress_attributes transform.
66299 *
66300 * @return {object} attributes object containing {x,y} as specified
66301 */
66302exports.attributes = function(opts, extra) {
66303 opts = opts || {};
66304 extra = extra || {};
66305
66306 var base = {
66307 valType: 'info_array',
66308
66309 editType: opts.editType,
66310 items: [
66311 {valType: 'number', min: 0, max: 1, editType: opts.editType},
66312 {valType: 'number', min: 0, max: 1, editType: opts.editType}
66313 ],
66314 dflt: [0, 1]
66315 };
66316
66317 var namePart = opts.name ? opts.name + ' ' : '';
66318 var contPart = opts.trace ? 'trace ' : 'subplot ';
66319 var descPart = extra.description ? ' ' + extra.description : '';
66320
66321 var out = {
66322 x: extendFlat({}, base, {
66323
66324 }),
66325 y: extendFlat({}, base, {
66326
66327 }),
66328 editType: opts.editType
66329 };
66330
66331 if(!opts.noGridCell) {
66332 out.row = {
66333 valType: 'integer',
66334 min: 0,
66335 dflt: 0,
66336
66337 editType: opts.editType,
66338
66339 };
66340 out.column = {
66341 valType: 'integer',
66342 min: 0,
66343 dflt: 0,
66344
66345 editType: opts.editType,
66346
66347 };
66348 }
66349
66350 return out;
66351};
66352
66353exports.defaults = function(containerOut, layout, coerce, dfltDomains) {
66354 var dfltX = (dfltDomains && dfltDomains.x) || [0, 1];
66355 var dfltY = (dfltDomains && dfltDomains.y) || [0, 1];
66356
66357 var grid = layout.grid;
66358 if(grid) {
66359 var column = coerce('domain.column');
66360 if(column !== undefined) {
66361 if(column < grid.columns) dfltX = grid._domains.x[column];
66362 else delete containerOut.domain.column;
66363 }
66364
66365 var row = coerce('domain.row');
66366 if(row !== undefined) {
66367 if(row < grid.rows) dfltY = grid._domains.y[row];
66368 else delete containerOut.domain.row;
66369 }
66370 }
66371
66372 var x = coerce('domain.x', dfltX);
66373 var y = coerce('domain.y', dfltY);
66374
66375 // don't accept bad input data
66376 if(!(x[0] < x[1])) containerOut.domain.x = dfltX.slice();
66377 if(!(y[0] < y[1])) containerOut.domain.y = dfltY.slice();
66378};
66379
66380},{"../lib/extend":170}],250:[function(_dereq_,module,exports){
66381/**
66382* Copyright 2012-2020, Plotly, Inc.
66383* All rights reserved.
66384*
66385* This source code is licensed under the MIT license found in the
66386* LICENSE file in the root directory of this source tree.
66387*/
66388
66389'use strict';
66390
66391/*
66392 * make a font attribute group
66393 *
66394 * @param {object} opts
66395 * @param {string}
66396 * opts.description: where & how this font is used
66397 * @param {optional bool} arrayOk:
66398 * should each part (family, size, color) be arrayOk? default false.
66399 * @param {string} editType:
66400 * the editType for all pieces of this font
66401 * @param {optional string} colorEditType:
66402 * a separate editType just for color
66403 *
66404 * @return {object} attributes object containing {family, size, color} as specified
66405 */
66406module.exports = function(opts) {
66407 var editType = opts.editType;
66408 var colorEditType = opts.colorEditType;
66409 if(colorEditType === undefined) colorEditType = editType;
66410 var attrs = {
66411 family: {
66412 valType: 'string',
66413
66414 noBlank: true,
66415 strict: true,
66416 editType: editType,
66417
66418 },
66419 size: {
66420 valType: 'number',
66421
66422 min: 1,
66423 editType: editType
66424 },
66425 color: {
66426 valType: 'color',
66427
66428 editType: colorEditType
66429 },
66430 editType: editType,
66431 // blank strings so compress_attributes can remove
66432 // TODO - that's uber hacky... better solution?
66433
66434 };
66435
66436 if(opts.arrayOk) {
66437 attrs.family.arrayOk = true;
66438 attrs.size.arrayOk = true;
66439 attrs.color.arrayOk = true;
66440 }
66441
66442 return attrs;
66443};
66444
66445},{}],251:[function(_dereq_,module,exports){
66446/**
66447* Copyright 2012-2020, Plotly, Inc.
66448* All rights reserved.
66449*
66450* This source code is licensed under the MIT license found in the
66451* LICENSE file in the root directory of this source tree.
66452*/
66453
66454'use strict';
66455
66456module.exports = {
66457 _isLinkedToArray: 'frames_entry',
66458
66459 group: {
66460 valType: 'string',
66461
66462
66463 },
66464 name: {
66465 valType: 'string',
66466
66467
66468 },
66469 traces: {
66470 valType: 'any',
66471
66472
66473 },
66474 baseframe: {
66475 valType: 'string',
66476
66477
66478 },
66479 data: {
66480 valType: 'any',
66481
66482
66483 },
66484 layout: {
66485 valType: 'any',
66486
66487
66488 }
66489};
66490
66491},{}],252:[function(_dereq_,module,exports){
66492/**
66493* Copyright 2012-2020, Plotly, Inc.
66494* All rights reserved.
66495*
66496* This source code is licensed under the MIT license found in the
66497* LICENSE file in the root directory of this source tree.
66498*/
66499
66500'use strict';
66501
66502// projection names to d3 function name
66503exports.projNames = {
66504 // d3.geo.projection
66505 'equirectangular': 'equirectangular',
66506 'mercator': 'mercator',
66507 'orthographic': 'orthographic',
66508 'natural earth': 'naturalEarth',
66509 'kavrayskiy7': 'kavrayskiy7',
66510 'miller': 'miller',
66511 'robinson': 'robinson',
66512 'eckert4': 'eckert4',
66513 'azimuthal equal area': 'azimuthalEqualArea',
66514 'azimuthal equidistant': 'azimuthalEquidistant',
66515 'conic equal area': 'conicEqualArea',
66516 'conic conformal': 'conicConformal',
66517 'conic equidistant': 'conicEquidistant',
66518 'gnomonic': 'gnomonic',
66519 'stereographic': 'stereographic',
66520 'mollweide': 'mollweide',
66521 'hammer': 'hammer',
66522 'transverse mercator': 'transverseMercator',
66523 'albers usa': 'albersUsa',
66524 'winkel tripel': 'winkel3',
66525 'aitoff': 'aitoff',
66526 'sinusoidal': 'sinusoidal'
66527};
66528
66529// name of the axes
66530exports.axesNames = ['lonaxis', 'lataxis'];
66531
66532// max longitudinal angular span (EXPERIMENTAL)
66533exports.lonaxisSpan = {
66534 'orthographic': 180,
66535 'azimuthal equal area': 360,
66536 'azimuthal equidistant': 360,
66537 'conic conformal': 180,
66538 'gnomonic': 160,
66539 'stereographic': 180,
66540 'transverse mercator': 180,
66541 '*': 360
66542};
66543
66544// max latitudinal angular span (EXPERIMENTAL)
66545exports.lataxisSpan = {
66546 'conic conformal': 150,
66547 'stereographic': 179.5,
66548 '*': 180
66549};
66550
66551// defaults for each scope
66552exports.scopeDefaults = {
66553 world: {
66554 lonaxisRange: [-180, 180],
66555 lataxisRange: [-90, 90],
66556 projType: 'equirectangular',
66557 projRotate: [0, 0, 0]
66558 },
66559 usa: {
66560 lonaxisRange: [-180, -50],
66561 lataxisRange: [15, 80],
66562 projType: 'albers usa'
66563 },
66564 europe: {
66565 lonaxisRange: [-30, 60],
66566 lataxisRange: [30, 85],
66567 projType: 'conic conformal',
66568 projRotate: [15, 0, 0],
66569 projParallels: [0, 60]
66570 },
66571 asia: {
66572 lonaxisRange: [22, 160],
66573 lataxisRange: [-15, 55],
66574 projType: 'mercator',
66575 projRotate: [0, 0, 0]
66576 },
66577 africa: {
66578 lonaxisRange: [-30, 60],
66579 lataxisRange: [-40, 40],
66580 projType: 'mercator',
66581 projRotate: [0, 0, 0]
66582 },
66583 'north america': {
66584 lonaxisRange: [-180, -45],
66585 lataxisRange: [5, 85],
66586 projType: 'conic conformal',
66587 projRotate: [-100, 0, 0],
66588 projParallels: [29.5, 45.5]
66589 },
66590 'south america': {
66591 lonaxisRange: [-100, -30],
66592 lataxisRange: [-60, 15],
66593 projType: 'mercator',
66594 projRotate: [0, 0, 0]
66595 }
66596};
66597
66598// angular pad to avoid rounding error around clip angles
66599exports.clipPad = 1e-3;
66600
66601// map projection precision
66602exports.precision = 0.1;
66603
66604// default land and water fill colors
66605exports.landColor = '#F0DC82';
66606exports.waterColor = '#3399FF';
66607
66608// locationmode to layer name
66609exports.locationmodeToLayer = {
66610 'ISO-3': 'countries',
66611 'USA-states': 'subunits',
66612 'country names': 'countries'
66613};
66614
66615// SVG element for a sphere (use to frame maps)
66616exports.sphereSVG = {type: 'Sphere'};
66617
66618// N.B. base layer names must be the same as in the topojson files
66619
66620// base layer with a fill color
66621exports.fillLayers = {
66622 ocean: 1,
66623 land: 1,
66624 lakes: 1
66625};
66626
66627// base layer with a only a line color
66628exports.lineLayers = {
66629 subunits: 1,
66630 countries: 1,
66631 coastlines: 1,
66632 rivers: 1,
66633 frame: 1
66634};
66635
66636exports.layers = [
66637 'bg',
66638 'ocean', 'land', 'lakes',
66639 'subunits', 'countries', 'coastlines', 'rivers',
66640 'lataxis', 'lonaxis', 'frame',
66641 'backplot',
66642 'frontplot'
66643];
66644
66645exports.layersForChoropleth = [
66646 'bg',
66647 'ocean', 'land',
66648 'subunits', 'countries', 'coastlines',
66649 'lataxis', 'lonaxis', 'frame',
66650 'backplot',
66651 'rivers', 'lakes',
66652 'frontplot'
66653];
66654
66655exports.layerNameToAdjective = {
66656 ocean: 'ocean',
66657 land: 'land',
66658 lakes: 'lake',
66659 subunits: 'subunit',
66660 countries: 'country',
66661 coastlines: 'coastline',
66662 rivers: 'river',
66663 frame: 'frame'
66664};
66665
66666},{}],253:[function(_dereq_,module,exports){
66667/**
66668* Copyright 2012-2020, Plotly, Inc.
66669* All rights reserved.
66670*
66671* This source code is licensed under the MIT license found in the
66672* LICENSE file in the root directory of this source tree.
66673*/
66674
66675'use strict';
66676
66677/* global PlotlyGeoAssets:false */
66678
66679var d3 = _dereq_('d3');
66680
66681var Registry = _dereq_('../../registry');
66682var Lib = _dereq_('../../lib');
66683var Color = _dereq_('../../components/color');
66684var Drawing = _dereq_('../../components/drawing');
66685var Fx = _dereq_('../../components/fx');
66686var Plots = _dereq_('../plots');
66687var Axes = _dereq_('../cartesian/axes');
66688var getAutoRange = _dereq_('../cartesian/autorange').getAutoRange;
66689var dragElement = _dereq_('../../components/dragelement');
66690var prepSelect = _dereq_('../cartesian/select').prepSelect;
66691var clearSelect = _dereq_('../cartesian/select').clearSelect;
66692var selectOnClick = _dereq_('../cartesian/select').selectOnClick;
66693
66694var createGeoZoom = _dereq_('./zoom');
66695var constants = _dereq_('./constants');
66696
66697var geoUtils = _dereq_('../../lib/geo_location_utils');
66698var topojsonUtils = _dereq_('../../lib/topojson_utils');
66699var topojsonFeature = _dereq_('topojson-client').feature;
66700
66701_dereq_('./projections')(d3);
66702
66703function Geo(opts) {
66704 this.id = opts.id;
66705 this.graphDiv = opts.graphDiv;
66706 this.container = opts.container;
66707 this.topojsonURL = opts.topojsonURL;
66708 this.isStatic = opts.staticPlot;
66709
66710 this.topojsonName = null;
66711 this.topojson = null;
66712
66713 this.projection = null;
66714 this.scope = null;
66715 this.viewInitial = null;
66716 this.fitScale = null;
66717 this.bounds = null;
66718 this.midPt = null;
66719
66720 this.hasChoropleth = false;
66721 this.traceHash = {};
66722
66723 this.layers = {};
66724 this.basePaths = {};
66725 this.dataPaths = {};
66726 this.dataPoints = {};
66727
66728 this.clipDef = null;
66729 this.clipRect = null;
66730 this.bgRect = null;
66731
66732 this.makeFramework();
66733}
66734
66735var proto = Geo.prototype;
66736
66737module.exports = function createGeo(opts) {
66738 return new Geo(opts);
66739};
66740
66741proto.plot = function(geoCalcData, fullLayout, promises) {
66742 var _this = this;
66743 var geoLayout = fullLayout[this.id];
66744 var geoPromises = [];
66745
66746 var needsTopojson = false;
66747 for(var k in constants.layerNameToAdjective) {
66748 if(k !== 'frame' && geoLayout['show' + k]) {
66749 needsTopojson = true;
66750 break;
66751 }
66752 }
66753 for(var i = 0; i < geoCalcData.length; i++) {
66754 if(geoCalcData[0][0].trace.locationmode) {
66755 needsTopojson = true;
66756 break;
66757 }
66758 }
66759
66760 if(needsTopojson) {
66761 var topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
66762 if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
66763 _this.topojsonName = topojsonNameNew;
66764
66765 if(PlotlyGeoAssets.topojson[_this.topojsonName] === undefined) {
66766 geoPromises.push(_this.fetchTopojson());
66767 }
66768 }
66769 }
66770
66771 geoPromises = geoPromises.concat(geoUtils.fetchTraceGeoData(geoCalcData));
66772
66773 promises.push(new Promise(function(resolve, reject) {
66774 Promise.all(geoPromises).then(function() {
66775 _this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
66776 _this.update(geoCalcData, fullLayout);
66777 resolve();
66778 })
66779 .catch(reject);
66780 }));
66781};
66782
66783proto.fetchTopojson = function() {
66784 var _this = this;
66785 var topojsonPath = topojsonUtils.getTopojsonPath(_this.topojsonURL, _this.topojsonName);
66786
66787 return new Promise(function(resolve, reject) {
66788 d3.json(topojsonPath, function(err, topojson) {
66789 if(err) {
66790 if(err.status === 404) {
66791 return reject(new Error([
66792 'plotly.js could not find topojson file at',
66793 topojsonPath, '.',
66794 'Make sure the *topojsonURL* plot config option',
66795 'is set properly.'
66796 ].join(' ')));
66797 } else {
66798 return reject(new Error([
66799 'unexpected error while fetching topojson file at',
66800 topojsonPath
66801 ].join(' ')));
66802 }
66803 }
66804
66805 PlotlyGeoAssets.topojson[_this.topojsonName] = topojson;
66806 resolve();
66807 });
66808 });
66809};
66810
66811proto.update = function(geoCalcData, fullLayout) {
66812 var geoLayout = fullLayout[this.id];
66813
66814 // important: maps with choropleth traces have a different layer order
66815 this.hasChoropleth = false;
66816
66817 for(var i = 0; i < geoCalcData.length; i++) {
66818 var calcTrace = geoCalcData[i];
66819 var trace = calcTrace[0].trace;
66820
66821 if(trace.type === 'choropleth') {
66822 this.hasChoropleth = true;
66823 }
66824 if(trace.visible === true && trace._length > 0) {
66825 trace._module.calcGeoJSON(calcTrace, fullLayout);
66826 }
66827 }
66828
66829 var hasInvalidBounds = this.updateProjection(geoCalcData, fullLayout);
66830 if(hasInvalidBounds) return;
66831
66832 if(!this.viewInitial || this.scope !== geoLayout.scope) {
66833 this.saveViewInitial(geoLayout);
66834 }
66835 this.scope = geoLayout.scope;
66836
66837 this.updateBaseLayers(fullLayout, geoLayout);
66838 this.updateDims(fullLayout, geoLayout);
66839 this.updateFx(fullLayout, geoLayout);
66840
66841 Plots.generalUpdatePerTraceModule(this.graphDiv, this, geoCalcData, geoLayout);
66842
66843 var scatterLayer = this.layers.frontplot.select('.scatterlayer');
66844 this.dataPoints.point = scatterLayer.selectAll('.point');
66845 this.dataPoints.text = scatterLayer.selectAll('text');
66846 this.dataPaths.line = scatterLayer.selectAll('.js-line');
66847
66848 var choroplethLayer = this.layers.backplot.select('.choroplethlayer');
66849 this.dataPaths.choropleth = choroplethLayer.selectAll('path');
66850
66851 this.render();
66852};
66853
66854proto.updateProjection = function(geoCalcData, fullLayout) {
66855 var gd = this.graphDiv;
66856 var geoLayout = fullLayout[this.id];
66857 var gs = fullLayout._size;
66858 var domain = geoLayout.domain;
66859 var projLayout = geoLayout.projection;
66860
66861 var lonaxis = geoLayout.lonaxis;
66862 var lataxis = geoLayout.lataxis;
66863 var axLon = lonaxis._ax;
66864 var axLat = lataxis._ax;
66865
66866 var projection = this.projection = getProjection(geoLayout);
66867
66868 // setup subplot extent [[x0,y0], [x1,y1]]
66869 var extent = [[
66870 gs.l + gs.w * domain.x[0],
66871 gs.t + gs.h * (1 - domain.y[1])
66872 ], [
66873 gs.l + gs.w * domain.x[1],
66874 gs.t + gs.h * (1 - domain.y[0])
66875 ]];
66876
66877 var center = geoLayout.center || {};
66878 var rotation = projLayout.rotation || {};
66879 var lonaxisRange = lonaxis.range || [];
66880 var lataxisRange = lataxis.range || [];
66881
66882 if(geoLayout.fitbounds) {
66883 axLon._length = extent[1][0] - extent[0][0];
66884 axLat._length = extent[1][1] - extent[0][1];
66885 axLon.range = getAutoRange(gd, axLon);
66886 axLat.range = getAutoRange(gd, axLat);
66887
66888 var midLon = (axLon.range[0] + axLon.range[1]) / 2;
66889 var midLat = (axLat.range[0] + axLat.range[1]) / 2;
66890
66891 if(geoLayout._isScoped) {
66892 center = {lon: midLon, lat: midLat};
66893 } else if(geoLayout._isClipped) {
66894 center = {lon: midLon, lat: midLat};
66895 rotation = {lon: midLon, lat: midLat, roll: rotation.roll};
66896
66897 var projType = projLayout.type;
66898 var lonHalfSpan = (constants.lonaxisSpan[projType] / 2) || 180;
66899 var latHalfSpan = (constants.lataxisSpan[projType] / 2) || 180;
66900
66901 lonaxisRange = [midLon - lonHalfSpan, midLon + lonHalfSpan];
66902 lataxisRange = [midLat - latHalfSpan, midLat + latHalfSpan];
66903 } else {
66904 center = {lon: midLon, lat: midLat};
66905 rotation = {lon: midLon, lat: rotation.lat, roll: rotation.roll};
66906 }
66907 }
66908
66909 // set 'pre-fit' projection
66910 projection
66911 .center([center.lon - rotation.lon, center.lat - rotation.lat])
66912 .rotate([-rotation.lon, -rotation.lat, rotation.roll])
66913 .parallels(projLayout.parallels);
66914
66915 // fit projection 'scale' and 'translate' to set lon/lat ranges
66916 var rangeBox = makeRangeBox(lonaxisRange, lataxisRange);
66917 projection.fitExtent(extent, rangeBox);
66918
66919 var b = this.bounds = projection.getBounds(rangeBox);
66920 var s = this.fitScale = projection.scale();
66921 var t = projection.translate();
66922
66923 if(
66924 !isFinite(b[0][0]) || !isFinite(b[0][1]) ||
66925 !isFinite(b[1][0]) || !isFinite(b[1][1]) ||
66926 isNaN(t[0]) || isNaN(t[0])
66927 ) {
66928 var attrToUnset = ['fitbounds', 'projection.rotation', 'center', 'lonaxis.range', 'lataxis.range'];
66929 var msg = 'Invalid geo settings, relayout\'ing to default view.';
66930 var updateObj = {};
66931
66932 // clear all attributes that could cause invalid bounds,
66933 // clear viewInitial to update reset-view behavior
66934
66935 for(var i = 0; i < attrToUnset.length; i++) {
66936 updateObj[this.id + '.' + attrToUnset[i]] = null;
66937 }
66938
66939 this.viewInitial = null;
66940
66941 Lib.warn(msg);
66942 gd._promises.push(Registry.call('relayout', gd, updateObj));
66943 return msg;
66944 }
66945
66946 if(geoLayout.fitbounds) {
66947 var b2 = projection.getBounds(makeRangeBox(axLon.range, axLat.range));
66948 var k2 = Math.min(
66949 (b[1][0] - b[0][0]) / (b2[1][0] - b2[0][0]),
66950 (b[1][1] - b[0][1]) / (b2[1][1] - b2[0][1])
66951 );
66952
66953 if(isFinite(k2)) {
66954 projection.scale(k2 * s);
66955 } else {
66956 Lib.warn('Something went wrong during' + this.id + 'fitbounds computations.');
66957 }
66958 } else {
66959 // adjust projection to user setting
66960 projection.scale(projLayout.scale * s);
66961 }
66962
66963 // px coordinates of view mid-point,
66964 // useful to update `geo.center` after interactions
66965 var midPt = this.midPt = [
66966 (b[0][0] + b[1][0]) / 2,
66967 (b[0][1] + b[1][1]) / 2
66968 ];
66969
66970 projection
66971 .translate([t[0] + (midPt[0] - t[0]), t[1] + (midPt[1] - t[1])])
66972 .clipExtent(b);
66973
66974 // the 'albers usa' projection does not expose a 'center' method
66975 // so here's this hack to make it respond to 'geoLayout.center'
66976 if(geoLayout._isAlbersUsa) {
66977 var centerPx = projection([center.lon, center.lat]);
66978 var tt = projection.translate();
66979
66980 projection.translate([
66981 tt[0] - (centerPx[0] - tt[0]),
66982 tt[1] - (centerPx[1] - tt[1])
66983 ]);
66984 }
66985};
66986
66987proto.updateBaseLayers = function(fullLayout, geoLayout) {
66988 var _this = this;
66989 var topojson = _this.topojson;
66990 var layers = _this.layers;
66991 var basePaths = _this.basePaths;
66992
66993 function isAxisLayer(d) {
66994 return (d === 'lonaxis' || d === 'lataxis');
66995 }
66996
66997 function isLineLayer(d) {
66998 return Boolean(constants.lineLayers[d]);
66999 }
67000
67001 function isFillLayer(d) {
67002 return Boolean(constants.fillLayers[d]);
67003 }
67004
67005 var allLayers = this.hasChoropleth ?
67006 constants.layersForChoropleth :
67007 constants.layers;
67008
67009 var layerData = allLayers.filter(function(d) {
67010 return (isLineLayer(d) || isFillLayer(d)) ? geoLayout['show' + d] :
67011 isAxisLayer(d) ? geoLayout[d].showgrid :
67012 true;
67013 });
67014
67015 var join = _this.framework.selectAll('.layer')
67016 .data(layerData, String);
67017
67018 join.exit().each(function(d) {
67019 delete layers[d];
67020 delete basePaths[d];
67021 d3.select(this).remove();
67022 });
67023
67024 join.enter().append('g')
67025 .attr('class', function(d) { return 'layer ' + d; })
67026 .each(function(d) {
67027 var layer = layers[d] = d3.select(this);
67028
67029 if(d === 'bg') {
67030 _this.bgRect = layer.append('rect')
67031 .style('pointer-events', 'all');
67032 } else if(isAxisLayer(d)) {
67033 basePaths[d] = layer.append('path')
67034 .style('fill', 'none');
67035 } else if(d === 'backplot') {
67036 layer.append('g')
67037 .classed('choroplethlayer', true);
67038 } else if(d === 'frontplot') {
67039 layer.append('g')
67040 .classed('scatterlayer', true);
67041 } else if(isLineLayer(d)) {
67042 basePaths[d] = layer.append('path')
67043 .style('fill', 'none')
67044 .style('stroke-miterlimit', 2);
67045 } else if(isFillLayer(d)) {
67046 basePaths[d] = layer.append('path')
67047 .style('stroke', 'none');
67048 }
67049 });
67050
67051 join.order();
67052
67053 join.each(function(d) {
67054 var path = basePaths[d];
67055 var adj = constants.layerNameToAdjective[d];
67056
67057 if(d === 'frame') {
67058 path.datum(constants.sphereSVG);
67059 } else if(isLineLayer(d) || isFillLayer(d)) {
67060 path.datum(topojsonFeature(topojson, topojson.objects[d]));
67061 } else if(isAxisLayer(d)) {
67062 path.datum(makeGraticule(d, geoLayout, fullLayout))
67063 .call(Color.stroke, geoLayout[d].gridcolor)
67064 .call(Drawing.dashLine, '', geoLayout[d].gridwidth);
67065 }
67066
67067 if(isLineLayer(d)) {
67068 path.call(Color.stroke, geoLayout[adj + 'color'])
67069 .call(Drawing.dashLine, '', geoLayout[adj + 'width']);
67070 } else if(isFillLayer(d)) {
67071 path.call(Color.fill, geoLayout[adj + 'color']);
67072 }
67073 });
67074};
67075
67076proto.updateDims = function(fullLayout, geoLayout) {
67077 var b = this.bounds;
67078 var hFrameWidth = (geoLayout.framewidth || 0) / 2;
67079
67080 var l = b[0][0] - hFrameWidth;
67081 var t = b[0][1] - hFrameWidth;
67082 var w = b[1][0] - l + hFrameWidth;
67083 var h = b[1][1] - t + hFrameWidth;
67084
67085 Drawing.setRect(this.clipRect, l, t, w, h);
67086
67087 this.bgRect
67088 .call(Drawing.setRect, l, t, w, h)
67089 .call(Color.fill, geoLayout.bgcolor);
67090
67091 this.xaxis._offset = l;
67092 this.xaxis._length = w;
67093
67094 this.yaxis._offset = t;
67095 this.yaxis._length = h;
67096};
67097
67098proto.updateFx = function(fullLayout, geoLayout) {
67099 var _this = this;
67100 var gd = _this.graphDiv;
67101 var bgRect = _this.bgRect;
67102 var dragMode = fullLayout.dragmode;
67103 var clickMode = fullLayout.clickmode;
67104
67105 if(_this.isStatic) return;
67106
67107 function zoomReset() {
67108 var viewInitial = _this.viewInitial;
67109 var updateObj = {};
67110
67111 for(var k in viewInitial) {
67112 updateObj[_this.id + '.' + k] = viewInitial[k];
67113 }
67114
67115 Registry.call('_guiRelayout', gd, updateObj);
67116 gd.emit('plotly_doubleclick', null);
67117 }
67118
67119 function invert(lonlat) {
67120 return _this.projection.invert([
67121 lonlat[0] + _this.xaxis._offset,
67122 lonlat[1] + _this.yaxis._offset
67123 ]);
67124 }
67125
67126 var fillRangeItems;
67127
67128 if(dragMode === 'select') {
67129 fillRangeItems = function(eventData, poly) {
67130 var ranges = eventData.range = {};
67131 ranges[_this.id] = [
67132 invert([poly.xmin, poly.ymin]),
67133 invert([poly.xmax, poly.ymax])
67134 ];
67135 };
67136 } else if(dragMode === 'lasso') {
67137 fillRangeItems = function(eventData, poly, pts) {
67138 var dataPts = eventData.lassoPoints = {};
67139 dataPts[_this.id] = pts.filtered.map(invert);
67140 };
67141 }
67142
67143 // Note: dragOptions is needed to be declared for all dragmodes because
67144 // it's the object that holds persistent selection state.
67145 var dragOptions = {
67146 element: _this.bgRect.node(),
67147 gd: gd,
67148 plotinfo: {
67149 id: _this.id,
67150 xaxis: _this.xaxis,
67151 yaxis: _this.yaxis,
67152 fillRangeItems: fillRangeItems
67153 },
67154 xaxes: [_this.xaxis],
67155 yaxes: [_this.yaxis],
67156 subplot: _this.id,
67157 clickFn: function(numClicks) {
67158 if(numClicks === 2) {
67159 clearSelect(gd);
67160 }
67161 }
67162 };
67163
67164 if(dragMode === 'pan') {
67165 bgRect.node().onmousedown = null;
67166 bgRect.call(createGeoZoom(_this, geoLayout));
67167 bgRect.on('dblclick.zoom', zoomReset);
67168 if(!gd._context._scrollZoom.geo) {
67169 bgRect.on('wheel.zoom', null);
67170 }
67171 } else if(dragMode === 'select' || dragMode === 'lasso') {
67172 bgRect.on('.zoom', null);
67173
67174 dragOptions.prepFn = function(e, startX, startY) {
67175 prepSelect(e, startX, startY, dragOptions, dragMode);
67176 };
67177
67178 dragElement.init(dragOptions);
67179 }
67180
67181 bgRect.on('mousemove', function() {
67182 var lonlat = _this.projection.invert(d3.mouse(this));
67183
67184 if(!lonlat || isNaN(lonlat[0]) || isNaN(lonlat[1])) {
67185 return dragElement.unhover(gd, d3.event);
67186 }
67187
67188 _this.xaxis.p2c = function() { return lonlat[0]; };
67189 _this.yaxis.p2c = function() { return lonlat[1]; };
67190
67191 Fx.hover(gd, d3.event, _this.id);
67192 });
67193
67194 bgRect.on('mouseout', function() {
67195 if(gd._dragging) return;
67196 dragElement.unhover(gd, d3.event);
67197 });
67198
67199 bgRect.on('click', function() {
67200 // For select and lasso the dragElement is handling clicks
67201 if(dragMode !== 'select' && dragMode !== 'lasso') {
67202 if(clickMode.indexOf('select') > -1) {
67203 selectOnClick(d3.event, gd, [_this.xaxis], [_this.yaxis],
67204 _this.id, dragOptions);
67205 }
67206
67207 if(clickMode.indexOf('event') > -1) {
67208 // TODO: like pie and mapbox, this doesn't support right-click
67209 // actually this one is worse, as right-click starts a pan, or leaves
67210 // select in a weird state.
67211 // Also, only tangentially related, we should cancel hover during pan
67212 Fx.click(gd, d3.event);
67213 }
67214 }
67215 });
67216};
67217
67218proto.makeFramework = function() {
67219 var _this = this;
67220 var gd = _this.graphDiv;
67221 var fullLayout = gd._fullLayout;
67222 var clipId = 'clip' + fullLayout._uid + _this.id;
67223
67224 _this.clipDef = fullLayout._clips.append('clipPath')
67225 .attr('id', clipId);
67226
67227 _this.clipRect = _this.clipDef.append('rect');
67228
67229 _this.framework = d3.select(_this.container).append('g')
67230 .attr('class', 'geo ' + _this.id)
67231 .call(Drawing.setClipUrl, clipId, gd);
67232
67233 // sane lonlat to px
67234 _this.project = function(v) {
67235 var px = _this.projection(v);
67236 return px ?
67237 [px[0] - _this.xaxis._offset, px[1] - _this.yaxis._offset] :
67238 [null, null];
67239 };
67240
67241 _this.xaxis = {
67242 _id: 'x',
67243 c2p: function(v) { return _this.project(v)[0]; }
67244 };
67245
67246 _this.yaxis = {
67247 _id: 'y',
67248 c2p: function(v) { return _this.project(v)[1]; }
67249 };
67250
67251 // mock axis for hover formatting
67252 _this.mockAxis = {
67253 type: 'linear',
67254 showexponent: 'all',
67255 exponentformat: 'B'
67256 };
67257 Axes.setConvert(_this.mockAxis, fullLayout);
67258};
67259
67260proto.saveViewInitial = function(geoLayout) {
67261 var center = geoLayout.center || {};
67262 var projLayout = geoLayout.projection;
67263 var rotation = projLayout.rotation || {};
67264
67265 this.viewInitial = {
67266 'fitbounds': geoLayout.fitbounds,
67267 'projection.scale': projLayout.scale
67268 };
67269
67270 var extra;
67271 if(geoLayout._isScoped) {
67272 extra = {
67273 'center.lon': center.lon,
67274 'center.lat': center.lat,
67275 };
67276 } else if(geoLayout._isClipped) {
67277 extra = {
67278 'projection.rotation.lon': rotation.lon,
67279 'projection.rotation.lat': rotation.lat
67280 };
67281 } else {
67282 extra = {
67283 'center.lon': center.lon,
67284 'center.lat': center.lat,
67285 'projection.rotation.lon': rotation.lon
67286 };
67287 }
67288
67289 Lib.extendFlat(this.viewInitial, extra);
67290};
67291
67292// [hot code path] (re)draw all paths which depend on the projection
67293proto.render = function() {
67294 var projection = this.projection;
67295 var pathFn = projection.getPath();
67296 var k;
67297
67298 function translatePoints(d) {
67299 var lonlatPx = projection(d.lonlat);
67300 return lonlatPx ?
67301 'translate(' + lonlatPx[0] + ',' + lonlatPx[1] + ')' :
67302 null;
67303 }
67304
67305 function hideShowPoints(d) {
67306 return projection.isLonLatOverEdges(d.lonlat) ? 'none' : null;
67307 }
67308
67309 for(k in this.basePaths) {
67310 this.basePaths[k].attr('d', pathFn);
67311 }
67312
67313 for(k in this.dataPaths) {
67314 this.dataPaths[k].attr('d', function(d) { return pathFn(d.geojson); });
67315 }
67316
67317 for(k in this.dataPoints) {
67318 this.dataPoints[k]
67319 .attr('display', hideShowPoints)
67320 .attr('transform', translatePoints);
67321 }
67322};
67323
67324// Helper that wraps d3.geo[/* projection name /*]() which:
67325//
67326// - adds 'fitExtent' (available in d3 v4)
67327// - adds 'getPath', 'getBounds' convenience methods
67328// - scopes logic related to 'clipAngle'
67329// - adds 'isLonLatOverEdges' method
67330// - sets projection precision
67331// - sets methods that aren't always defined depending
67332// on the projection type to a dummy 'd3-esque' function,
67333//
67334// This wrapper alleviates subsequent code of (many) annoying if-statements.
67335function getProjection(geoLayout) {
67336 var projLayout = geoLayout.projection;
67337 var projType = projLayout.type;
67338
67339 var projection = d3.geo[constants.projNames[projType]]();
67340
67341 var clipAngle = geoLayout._isClipped ?
67342 constants.lonaxisSpan[projType] / 2 :
67343 null;
67344
67345 var methods = ['center', 'rotate', 'parallels', 'clipExtent'];
67346 var dummyFn = function(_) { return _ ? projection : []; };
67347
67348 for(var i = 0; i < methods.length; i++) {
67349 var m = methods[i];
67350 if(typeof projection[m] !== 'function') {
67351 projection[m] = dummyFn;
67352 }
67353 }
67354
67355 projection.isLonLatOverEdges = function(lonlat) {
67356 if(projection(lonlat) === null) {
67357 return true;
67358 }
67359
67360 if(clipAngle) {
67361 var r = projection.rotate();
67362 var angle = d3.geo.distance(lonlat, [-r[0], -r[1]]);
67363 var maxAngle = clipAngle * Math.PI / 180;
67364 return angle > maxAngle;
67365 } else {
67366 return false;
67367 }
67368 };
67369
67370 projection.getPath = function() {
67371 return d3.geo.path().projection(projection);
67372 };
67373
67374 projection.getBounds = function(object) {
67375 return projection.getPath().bounds(object);
67376 };
67377
67378 // adapted from d3 v4:
67379 // https://github.com/d3/d3-geo/blob/master/src/projection/fit.js
67380 projection.fitExtent = function(extent, object) {
67381 var w = extent[1][0] - extent[0][0];
67382 var h = extent[1][1] - extent[0][1];
67383 var clip = projection.clipExtent && projection.clipExtent();
67384
67385 projection
67386 .scale(150)
67387 .translate([0, 0]);
67388
67389 if(clip) projection.clipExtent(null);
67390
67391 var b = projection.getBounds(object);
67392 var k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1]));
67393 var x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2;
67394 var y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
67395
67396 if(clip) projection.clipExtent(clip);
67397
67398 return projection
67399 .scale(k * 150)
67400 .translate([x, y]);
67401 };
67402
67403 projection.precision(constants.precision);
67404
67405 if(clipAngle) {
67406 projection.clipAngle(clipAngle - constants.clipPad);
67407 }
67408
67409 return projection;
67410}
67411
67412function makeGraticule(axisName, geoLayout, fullLayout) {
67413 // equivalent to the d3 "ε"
67414 var epsilon = 1e-6;
67415 // same as the geoGraticule default
67416 var precision = 2.5;
67417
67418 var axLayout = geoLayout[axisName];
67419 var scopeDefaults = constants.scopeDefaults[geoLayout.scope];
67420 var rng;
67421 var oppRng;
67422 var coordFn;
67423
67424 if(axisName === 'lonaxis') {
67425 rng = scopeDefaults.lonaxisRange;
67426 oppRng = scopeDefaults.lataxisRange;
67427 coordFn = function(v, l) { return [v, l]; };
67428 } else if(axisName === 'lataxis') {
67429 rng = scopeDefaults.lataxisRange;
67430 oppRng = scopeDefaults.lonaxisRange;
67431 coordFn = function(v, l) { return [l, v]; };
67432 }
67433
67434 var dummyAx = {
67435 type: 'linear',
67436 range: [rng[0], rng[1] - epsilon],
67437 tick0: axLayout.tick0,
67438 dtick: axLayout.dtick
67439 };
67440
67441 Axes.setConvert(dummyAx, fullLayout);
67442 var vals = Axes.calcTicks(dummyAx);
67443
67444 // remove duplicate on antimeridian
67445 if(!geoLayout.isScoped && axisName === 'lonaxis') {
67446 vals.pop();
67447 }
67448
67449 var len = vals.length;
67450 var coords = new Array(len);
67451
67452 for(var i = 0; i < len; i++) {
67453 var v = vals[i].x;
67454 var line = coords[i] = [];
67455 for(var l = oppRng[0]; l < oppRng[1] + precision; l += precision) {
67456 line.push(coordFn(v, l));
67457 }
67458 }
67459
67460 return {
67461 type: 'MultiLineString',
67462 coordinates: coords
67463 };
67464}
67465
67466// Returns polygon GeoJSON corresponding to lon/lat range box
67467// with well-defined direction
67468//
67469// Note that clipPad padding is added around range to avoid aliasing.
67470function makeRangeBox(lon, lat) {
67471 var clipPad = constants.clipPad;
67472 var lon0 = lon[0] + clipPad;
67473 var lon1 = lon[1] - clipPad;
67474 var lat0 = lat[0] + clipPad;
67475 var lat1 = lat[1] - clipPad;
67476
67477 // to cross antimeridian w/o ambiguity
67478 if(lon0 > 0 && lon1 < 0) lon1 += 360;
67479
67480 var dlon4 = (lon1 - lon0) / 4;
67481
67482 return {
67483 type: 'Polygon',
67484 coordinates: [[
67485 [lon0, lat0],
67486 [lon0, lat1],
67487 [lon0 + dlon4, lat1],
67488 [lon0 + 2 * dlon4, lat1],
67489 [lon0 + 3 * dlon4, lat1],
67490 [lon1, lat1],
67491 [lon1, lat0],
67492 [lon1 - dlon4, lat0],
67493 [lon1 - 2 * dlon4, lat0],
67494 [lon1 - 3 * dlon4, lat0],
67495 [lon0, lat0]
67496 ]]
67497 };
67498}
67499
67500},{"../../components/color":50,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../lib":177,"../../lib/geo_location_utils":173,"../../lib/topojson_utils":201,"../../registry":272,"../cartesian/autorange":221,"../cartesian/axes":222,"../cartesian/select":241,"../plots":263,"./constants":252,"./projections":257,"./zoom":258,"d3":13,"topojson-client":33}],254:[function(_dereq_,module,exports){
67501/**
67502* Copyright 2012-2020, Plotly, Inc.
67503* All rights reserved.
67504*
67505* This source code is licensed under the MIT license found in the
67506* LICENSE file in the root directory of this source tree.
67507*/
67508
67509'use strict';
67510
67511var getSubplotCalcData = _dereq_('../../plots/get_data').getSubplotCalcData;
67512var counterRegex = _dereq_('../../lib').counterRegex;
67513
67514var createGeo = _dereq_('./geo');
67515
67516var GEO = 'geo';
67517var counter = counterRegex(GEO);
67518
67519var attributes = {};
67520attributes[GEO] = {
67521 valType: 'subplotid',
67522
67523 dflt: GEO,
67524 editType: 'calc',
67525
67526};
67527
67528function plotGeo(gd) {
67529 var fullLayout = gd._fullLayout;
67530 var calcData = gd.calcdata;
67531 var geoIds = fullLayout._subplots[GEO];
67532
67533 for(var i = 0; i < geoIds.length; i++) {
67534 var geoId = geoIds[i];
67535 var geoCalcData = getSubplotCalcData(calcData, GEO, geoId);
67536 var geoLayout = fullLayout[geoId];
67537 var geo = geoLayout._subplot;
67538
67539 if(!geo) {
67540 geo = createGeo({
67541 id: geoId,
67542 graphDiv: gd,
67543 container: fullLayout._geolayer.node(),
67544 topojsonURL: gd._context.topojsonURL,
67545 staticPlot: gd._context.staticPlot
67546 });
67547
67548 fullLayout[geoId]._subplot = geo;
67549 }
67550
67551 geo.plot(geoCalcData, fullLayout, gd._promises);
67552 }
67553}
67554
67555function clean(newFullData, newFullLayout, oldFullData, oldFullLayout) {
67556 var oldGeoKeys = oldFullLayout._subplots[GEO] || [];
67557
67558 for(var i = 0; i < oldGeoKeys.length; i++) {
67559 var oldGeoKey = oldGeoKeys[i];
67560 var oldGeo = oldFullLayout[oldGeoKey]._subplot;
67561
67562 if(!newFullLayout[oldGeoKey] && !!oldGeo) {
67563 oldGeo.framework.remove();
67564 oldGeo.clipDef.remove();
67565 }
67566 }
67567}
67568
67569function updateFx(gd) {
67570 var fullLayout = gd._fullLayout;
67571 var subplotIds = fullLayout._subplots[GEO];
67572
67573 for(var i = 0; i < subplotIds.length; i++) {
67574 var subplotLayout = fullLayout[subplotIds[i]];
67575 var subplotObj = subplotLayout._subplot;
67576 subplotObj.updateFx(fullLayout, subplotLayout);
67577 }
67578}
67579
67580module.exports = {
67581 attr: GEO,
67582 name: GEO,
67583 idRoot: GEO,
67584 idRegex: counter,
67585 attrRegex: counter,
67586 attributes: attributes,
67587 layoutAttributes: _dereq_('./layout_attributes'),
67588 supplyLayoutDefaults: _dereq_('./layout_defaults'),
67589 plot: plotGeo,
67590 updateFx: updateFx,
67591 clean: clean
67592};
67593
67594},{"../../lib":177,"../../plots/get_data":259,"./geo":253,"./layout_attributes":255,"./layout_defaults":256}],255:[function(_dereq_,module,exports){
67595/**
67596* Copyright 2012-2020, Plotly, Inc.
67597* All rights reserved.
67598*
67599* This source code is licensed under the MIT license found in the
67600* LICENSE file in the root directory of this source tree.
67601*/
67602
67603'use strict';
67604
67605var colorAttrs = _dereq_('../../components/color/attributes');
67606var domainAttrs = _dereq_('../domain').attributes;
67607var constants = _dereq_('./constants');
67608var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
67609
67610var geoAxesAttrs = {
67611 range: {
67612 valType: 'info_array',
67613
67614 items: [
67615 {valType: 'number'},
67616 {valType: 'number'}
67617 ],
67618
67619 },
67620 showgrid: {
67621 valType: 'boolean',
67622
67623 dflt: false,
67624
67625 },
67626 tick0: {
67627 valType: 'number',
67628
67629 dflt: 0,
67630
67631 },
67632 dtick: {
67633 valType: 'number',
67634
67635
67636 },
67637 gridcolor: {
67638 valType: 'color',
67639
67640 dflt: colorAttrs.lightLine,
67641
67642 },
67643 gridwidth: {
67644 valType: 'number',
67645
67646 min: 0,
67647 dflt: 1,
67648
67649 }
67650};
67651
67652var attrs = module.exports = overrideAll({
67653 domain: domainAttrs({name: 'geo'}, {
67654
67655 }),
67656
67657 fitbounds: {
67658 valType: 'enumerated',
67659 values: [false, 'locations', 'geojson'],
67660 dflt: false,
67661
67662 editType: 'plot',
67663
67664 },
67665
67666 resolution: {
67667 valType: 'enumerated',
67668 values: [110, 50],
67669
67670 dflt: 110,
67671 coerceNumber: true,
67672
67673 },
67674 scope: {
67675 valType: 'enumerated',
67676
67677 values: Object.keys(constants.scopeDefaults),
67678 dflt: 'world',
67679
67680 },
67681 projection: {
67682 type: {
67683 valType: 'enumerated',
67684
67685 values: Object.keys(constants.projNames),
67686
67687 },
67688 rotation: {
67689 lon: {
67690 valType: 'number',
67691
67692
67693 },
67694 lat: {
67695 valType: 'number',
67696
67697
67698 },
67699 roll: {
67700 valType: 'number',
67701
67702
67703 }
67704 },
67705 parallels: {
67706 valType: 'info_array',
67707
67708 items: [
67709 {valType: 'number'},
67710 {valType: 'number'}
67711 ],
67712
67713 },
67714 scale: {
67715 valType: 'number',
67716
67717 min: 0,
67718 dflt: 1,
67719
67720 },
67721 },
67722 center: {
67723 lon: {
67724 valType: 'number',
67725
67726
67727 },
67728 lat: {
67729 valType: 'number',
67730
67731
67732 }
67733 },
67734 visible: {
67735 valType: 'boolean',
67736
67737 dflt: true,
67738
67739 },
67740 showcoastlines: {
67741 valType: 'boolean',
67742
67743
67744 },
67745 coastlinecolor: {
67746 valType: 'color',
67747
67748 dflt: colorAttrs.defaultLine,
67749
67750 },
67751 coastlinewidth: {
67752 valType: 'number',
67753
67754 min: 0,
67755 dflt: 1,
67756
67757 },
67758 showland: {
67759 valType: 'boolean',
67760
67761 dflt: false,
67762
67763 },
67764 landcolor: {
67765 valType: 'color',
67766
67767 dflt: constants.landColor,
67768
67769 },
67770 showocean: {
67771 valType: 'boolean',
67772
67773 dflt: false,
67774
67775 },
67776 oceancolor: {
67777 valType: 'color',
67778
67779 dflt: constants.waterColor,
67780
67781 },
67782 showlakes: {
67783 valType: 'boolean',
67784
67785 dflt: false,
67786
67787 },
67788 lakecolor: {
67789 valType: 'color',
67790
67791 dflt: constants.waterColor,
67792
67793 },
67794 showrivers: {
67795 valType: 'boolean',
67796
67797 dflt: false,
67798
67799 },
67800 rivercolor: {
67801 valType: 'color',
67802
67803 dflt: constants.waterColor,
67804
67805 },
67806 riverwidth: {
67807 valType: 'number',
67808
67809 min: 0,
67810 dflt: 1,
67811
67812 },
67813 showcountries: {
67814 valType: 'boolean',
67815
67816
67817 },
67818 countrycolor: {
67819 valType: 'color',
67820
67821 dflt: colorAttrs.defaultLine,
67822
67823 },
67824 countrywidth: {
67825 valType: 'number',
67826
67827 min: 0,
67828 dflt: 1,
67829
67830 },
67831 showsubunits: {
67832 valType: 'boolean',
67833
67834
67835 },
67836 subunitcolor: {
67837 valType: 'color',
67838
67839 dflt: colorAttrs.defaultLine,
67840
67841 },
67842 subunitwidth: {
67843 valType: 'number',
67844
67845 min: 0,
67846 dflt: 1,
67847
67848 },
67849 showframe: {
67850 valType: 'boolean',
67851
67852
67853 },
67854 framecolor: {
67855 valType: 'color',
67856
67857 dflt: colorAttrs.defaultLine,
67858
67859 },
67860 framewidth: {
67861 valType: 'number',
67862
67863 min: 0,
67864 dflt: 1,
67865
67866 },
67867 bgcolor: {
67868 valType: 'color',
67869
67870 dflt: colorAttrs.background,
67871
67872 },
67873 lonaxis: geoAxesAttrs,
67874 lataxis: geoAxesAttrs
67875}, 'plot', 'from-root');
67876
67877// set uirevision outside of overrideAll so it can be `editType: 'none'`
67878attrs.uirevision = {
67879 valType: 'any',
67880
67881 editType: 'none',
67882
67883};
67884
67885},{"../../components/color/attributes":49,"../../plot_api/edit_types":205,"../domain":249,"./constants":252}],256:[function(_dereq_,module,exports){
67886/**
67887* Copyright 2012-2020, Plotly, Inc.
67888* All rights reserved.
67889*
67890* This source code is licensed under the MIT license found in the
67891* LICENSE file in the root directory of this source tree.
67892*/
67893
67894'use strict';
67895
67896var Lib = _dereq_('../../lib');
67897var handleSubplotDefaults = _dereq_('../subplot_defaults');
67898var getSubplotData = _dereq_('../get_data').getSubplotData;
67899
67900var constants = _dereq_('./constants');
67901var layoutAttributes = _dereq_('./layout_attributes');
67902
67903var axesNames = constants.axesNames;
67904
67905module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
67906 handleSubplotDefaults(layoutIn, layoutOut, fullData, {
67907 type: 'geo',
67908 attributes: layoutAttributes,
67909 handleDefaults: handleGeoDefaults,
67910 fullData: fullData,
67911 partition: 'y'
67912 });
67913};
67914
67915function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) {
67916 var subplotData = getSubplotData(opts.fullData, 'geo', opts.id);
67917 var traceIndices = subplotData.map(function(t) { return t._expandedIndex; });
67918
67919 var resolution = coerce('resolution');
67920 var scope = coerce('scope');
67921 var scopeParams = constants.scopeDefaults[scope];
67922
67923 var projType = coerce('projection.type', scopeParams.projType);
67924 var isAlbersUsa = geoLayoutOut._isAlbersUsa = projType === 'albers usa';
67925
67926 // no other scopes are allowed for 'albers usa' projection
67927 if(isAlbersUsa) scope = geoLayoutOut.scope = 'usa';
67928
67929 var isScoped = geoLayoutOut._isScoped = (scope !== 'world');
67930 var isConic = geoLayoutOut._isConic = projType.indexOf('conic') !== -1;
67931 var isClipped = geoLayoutOut._isClipped = !!constants.lonaxisSpan[projType];
67932
67933 if(geoLayoutIn.visible === false) {
67934 // should override template.layout.geo.show* - see issue 4482
67935
67936 // make a copy
67937 var newTemplate = Lib.extendDeep({}, geoLayoutOut._template);
67938
67939 // override show*
67940 newTemplate.showcoastlines = false;
67941 newTemplate.showcountries = false;
67942 newTemplate.showframe = false;
67943 newTemplate.showlakes = false;
67944 newTemplate.showland = false;
67945 newTemplate.showocean = false;
67946 newTemplate.showrivers = false;
67947 newTemplate.showsubunits = false;
67948 if(newTemplate.lonaxis) newTemplate.lonaxis.showgrid = false;
67949 if(newTemplate.lataxis) newTemplate.lataxis.showgrid = false;
67950
67951 // set ref to copy
67952 geoLayoutOut._template = newTemplate;
67953 }
67954 var visible = coerce('visible');
67955
67956 var show;
67957 for(var i = 0; i < axesNames.length; i++) {
67958 var axisName = axesNames[i];
67959 var dtickDflt = [30, 10][i];
67960 var rangeDflt;
67961
67962 if(isScoped) {
67963 rangeDflt = scopeParams[axisName + 'Range'];
67964 } else {
67965 var dfltSpans = constants[axisName + 'Span'];
67966 var hSpan = (dfltSpans[projType] || dfltSpans['*']) / 2;
67967 var rot = coerce(
67968 'projection.rotation.' + axisName.substr(0, 3),
67969 scopeParams.projRotate[i]
67970 );
67971 rangeDflt = [rot - hSpan, rot + hSpan];
67972 }
67973
67974 var range = coerce(axisName + '.range', rangeDflt);
67975 coerce(axisName + '.tick0');
67976 coerce(axisName + '.dtick', dtickDflt);
67977
67978 show = coerce(axisName + '.showgrid', !visible ? false : undefined);
67979 if(show) {
67980 coerce(axisName + '.gridcolor');
67981 coerce(axisName + '.gridwidth');
67982 }
67983
67984 // mock axis for autorange computations
67985 geoLayoutOut[axisName]._ax = {
67986 type: 'linear',
67987 _id: axisName.slice(0, 3),
67988 _traceIndices: traceIndices,
67989 setScale: Lib.identity,
67990 c2l: Lib.identity,
67991 r2l: Lib.identity,
67992 autorange: true,
67993 range: range.slice(),
67994 _m: 1,
67995 _input: {}
67996 };
67997 }
67998
67999 var lonRange = geoLayoutOut.lonaxis.range;
68000 var latRange = geoLayoutOut.lataxis.range;
68001
68002 // to cross antimeridian w/o ambiguity
68003 var lon0 = lonRange[0];
68004 var lon1 = lonRange[1];
68005 if(lon0 > 0 && lon1 < 0) lon1 += 360;
68006
68007 var centerLon = (lon0 + lon1) / 2;
68008 var projLon;
68009
68010 if(!isAlbersUsa) {
68011 var dfltProjRotate = isScoped ? scopeParams.projRotate : [centerLon, 0, 0];
68012
68013 projLon = coerce('projection.rotation.lon', dfltProjRotate[0]);
68014 coerce('projection.rotation.lat', dfltProjRotate[1]);
68015 coerce('projection.rotation.roll', dfltProjRotate[2]);
68016
68017 show = coerce('showcoastlines', !isScoped && visible);
68018 if(show) {
68019 coerce('coastlinecolor');
68020 coerce('coastlinewidth');
68021 }
68022
68023 show = coerce('showocean', !visible ? false : undefined);
68024 if(show) coerce('oceancolor');
68025 }
68026
68027 var centerLonDflt;
68028 var centerLatDflt;
68029
68030 if(isAlbersUsa) {
68031 // 'albers usa' does not have a 'center',
68032 // these values were found using via:
68033 // projection.invert([geoLayout.center.lon, geoLayoutIn.center.lat])
68034 centerLonDflt = -96.6;
68035 centerLatDflt = 38.7;
68036 } else {
68037 centerLonDflt = isScoped ? centerLon : projLon;
68038 centerLatDflt = (latRange[0] + latRange[1]) / 2;
68039 }
68040
68041 coerce('center.lon', centerLonDflt);
68042 coerce('center.lat', centerLatDflt);
68043
68044 if(isConic) {
68045 var dfltProjParallels = scopeParams.projParallels || [0, 60];
68046 coerce('projection.parallels', dfltProjParallels);
68047 }
68048
68049 coerce('projection.scale');
68050
68051 show = coerce('showland', !visible ? false : undefined);
68052 if(show) coerce('landcolor');
68053
68054 show = coerce('showlakes', !visible ? false : undefined);
68055 if(show) coerce('lakecolor');
68056
68057 show = coerce('showrivers', !visible ? false : undefined);
68058 if(show) {
68059 coerce('rivercolor');
68060 coerce('riverwidth');
68061 }
68062
68063 show = coerce('showcountries', isScoped && scope !== 'usa' && visible);
68064 if(show) {
68065 coerce('countrycolor');
68066 coerce('countrywidth');
68067 }
68068
68069 if(scope === 'usa' || (scope === 'north america' && resolution === 50)) {
68070 // Only works for:
68071 // USA states at 110m
68072 // USA states + Canada provinces at 50m
68073 coerce('showsubunits', visible);
68074 coerce('subunitcolor');
68075 coerce('subunitwidth');
68076 }
68077
68078 if(!isScoped) {
68079 // Does not work in non-world scopes
68080 show = coerce('showframe', visible);
68081 if(show) {
68082 coerce('framecolor');
68083 coerce('framewidth');
68084 }
68085 }
68086
68087 coerce('bgcolor');
68088
68089 var fitBounds = coerce('fitbounds');
68090
68091 // clear attributes that will get auto-filled later
68092 if(fitBounds) {
68093 delete geoLayoutOut.projection.scale;
68094
68095 if(isScoped) {
68096 delete geoLayoutOut.center.lon;
68097 delete geoLayoutOut.center.lat;
68098 } else if(isClipped) {
68099 delete geoLayoutOut.center.lon;
68100 delete geoLayoutOut.center.lat;
68101 delete geoLayoutOut.projection.rotation.lon;
68102 delete geoLayoutOut.projection.rotation.lat;
68103 delete geoLayoutOut.lonaxis.range;
68104 delete geoLayoutOut.lataxis.range;
68105 } else {
68106 delete geoLayoutOut.center.lon;
68107 delete geoLayoutOut.center.lat;
68108 delete geoLayoutOut.projection.rotation.lon;
68109 }
68110 }
68111}
68112
68113},{"../../lib":177,"../get_data":259,"../subplot_defaults":270,"./constants":252,"./layout_attributes":255}],257:[function(_dereq_,module,exports){
68114/**
68115* Copyright 2012-2020, Plotly, Inc.
68116* All rights reserved.
68117*
68118* This source code is licensed under the MIT license found in the
68119* LICENSE file in the root directory of this source tree.
68120*/
68121
68122/*
68123 * Generated by https://github.com/etpinard/d3-geo-projection-picker
68124 *
68125 * which is hand-picks projection from https://github.com/d3/d3-geo-projection
68126 *
68127 * into a CommonJS require-able module.
68128 */
68129
68130'use strict';
68131
68132/* eslint-disable */
68133
68134function addProjectionsToD3(d3) {
68135 d3.geo.project = function(object, projection) {
68136 var stream = projection.stream;
68137 if (!stream) throw new Error("not yet supported");
68138 return (object && d3_geo_projectObjectType.hasOwnProperty(object.type) ? d3_geo_projectObjectType[object.type] : d3_geo_projectGeometry)(object, stream);
68139 };
68140 function d3_geo_projectFeature(object, stream) {
68141 return {
68142 type: "Feature",
68143 id: object.id,
68144 properties: object.properties,
68145 geometry: d3_geo_projectGeometry(object.geometry, stream)
68146 };
68147 }
68148 function d3_geo_projectGeometry(geometry, stream) {
68149 if (!geometry) return null;
68150 if (geometry.type === "GeometryCollection") return {
68151 type: "GeometryCollection",
68152 geometries: object.geometries.map(function(geometry) {
68153 return d3_geo_projectGeometry(geometry, stream);
68154 })
68155 };
68156 if (!d3_geo_projectGeometryType.hasOwnProperty(geometry.type)) return null;
68157 var sink = d3_geo_projectGeometryType[geometry.type];
68158 d3.geo.stream(geometry, stream(sink));
68159 return sink.result();
68160 }
68161 var d3_geo_projectObjectType = {
68162 Feature: d3_geo_projectFeature,
68163 FeatureCollection: function(object, stream) {
68164 return {
68165 type: "FeatureCollection",
68166 features: object.features.map(function(feature) {
68167 return d3_geo_projectFeature(feature, stream);
68168 })
68169 };
68170 }
68171 };
68172 var d3_geo_projectPoints = [], d3_geo_projectLines = [];
68173 var d3_geo_projectPoint = {
68174 point: function(x, y) {
68175 d3_geo_projectPoints.push([ x, y ]);
68176 },
68177 result: function() {
68178 var result = !d3_geo_projectPoints.length ? null : d3_geo_projectPoints.length < 2 ? {
68179 type: "Point",
68180 coordinates: d3_geo_projectPoints[0]
68181 } : {
68182 type: "MultiPoint",
68183 coordinates: d3_geo_projectPoints
68184 };
68185 d3_geo_projectPoints = [];
68186 return result;
68187 }
68188 };
68189 var d3_geo_projectLine = {
68190 lineStart: d3_geo_projectNoop,
68191 point: function(x, y) {
68192 d3_geo_projectPoints.push([ x, y ]);
68193 },
68194 lineEnd: function() {
68195 if (d3_geo_projectPoints.length) d3_geo_projectLines.push(d3_geo_projectPoints),
68196 d3_geo_projectPoints = [];
68197 },
68198 result: function() {
68199 var result = !d3_geo_projectLines.length ? null : d3_geo_projectLines.length < 2 ? {
68200 type: "LineString",
68201 coordinates: d3_geo_projectLines[0]
68202 } : {
68203 type: "MultiLineString",
68204 coordinates: d3_geo_projectLines
68205 };
68206 d3_geo_projectLines = [];
68207 return result;
68208 }
68209 };
68210 var d3_geo_projectPolygon = {
68211 polygonStart: d3_geo_projectNoop,
68212 lineStart: d3_geo_projectNoop,
68213 point: function(x, y) {
68214 d3_geo_projectPoints.push([ x, y ]);
68215 },
68216 lineEnd: function() {
68217 var n = d3_geo_projectPoints.length;
68218 if (n) {
68219 do d3_geo_projectPoints.push(d3_geo_projectPoints[0].slice()); while (++n < 4);
68220 d3_geo_projectLines.push(d3_geo_projectPoints), d3_geo_projectPoints = [];
68221 }
68222 },
68223 polygonEnd: d3_geo_projectNoop,
68224 result: function() {
68225 if (!d3_geo_projectLines.length) return null;
68226 var polygons = [], holes = [];
68227 d3_geo_projectLines.forEach(function(ring) {
68228 if (d3_geo_projectClockwise(ring)) polygons.push([ ring ]); else holes.push(ring);
68229 });
68230 holes.forEach(function(hole) {
68231 var point = hole[0];
68232 polygons.some(function(polygon) {
68233 if (d3_geo_projectContains(polygon[0], point)) {
68234 polygon.push(hole);
68235 return true;
68236 }
68237 }) || polygons.push([ hole ]);
68238 });
68239 d3_geo_projectLines = [];
68240 return !polygons.length ? null : polygons.length > 1 ? {
68241 type: "MultiPolygon",
68242 coordinates: polygons
68243 } : {
68244 type: "Polygon",
68245 coordinates: polygons[0]
68246 };
68247 }
68248 };
68249 var d3_geo_projectGeometryType = {
68250 Point: d3_geo_projectPoint,
68251 MultiPoint: d3_geo_projectPoint,
68252 LineString: d3_geo_projectLine,
68253 MultiLineString: d3_geo_projectLine,
68254 Polygon: d3_geo_projectPolygon,
68255 MultiPolygon: d3_geo_projectPolygon,
68256 Sphere: d3_geo_projectPolygon
68257 };
68258 function d3_geo_projectNoop() {}
68259 function d3_geo_projectClockwise(ring) {
68260 if ((n = ring.length) < 4) return false;
68261 var i = 0, n, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
68262 while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
68263 return area <= 0;
68264 }
68265 function d3_geo_projectContains(ring, point) {
68266 var x = point[0], y = point[1], contains = false;
68267 for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
68268 var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];
68269 if (yi > y ^ yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) contains = !contains;
68270 }
68271 return contains;
68272 }
68273 var ε = 1e-6, ε2 = ε * ε, π = Math.PI, halfπ = π / 2, sqrtπ = Math.sqrt(π), radians = π / 180, degrees = 180 / π;
68274 function sinci(x) {
68275 return x ? x / Math.sin(x) : 1;
68276 }
68277 function sgn(x) {
68278 return x > 0 ? 1 : x < 0 ? -1 : 0;
68279 }
68280 function asin(x) {
68281 return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
68282 }
68283 function acos(x) {
68284 return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
68285 }
68286 function asqrt(x) {
68287 return x > 0 ? Math.sqrt(x) : 0;
68288 }
68289 var projection = d3.geo.projection, projectionMutator = d3.geo.projectionMutator;
68290 d3.geo.interrupt = function(project) {
68291 var lobes = [ [ [ [ -π, 0 ], [ 0, halfπ ], [ π, 0 ] ] ], [ [ [ -π, 0 ], [ 0, -halfπ ], [ π, 0 ] ] ] ];
68292 var bounds;
68293 function forward(λ, φ) {
68294 var sign = φ < 0 ? -1 : +1, hemilobes = lobes[+(φ < 0)];
68295 for (var i = 0, n = hemilobes.length - 1; i < n && λ > hemilobes[i][2][0]; ++i) ;
68296 var coordinates = project(λ - hemilobes[i][1][0], φ);
68297 coordinates[0] += project(hemilobes[i][1][0], sign * φ > sign * hemilobes[i][0][1] ? hemilobes[i][0][1] : φ)[0];
68298 return coordinates;
68299 }
68300 function reset() {
68301 bounds = lobes.map(function(hemilobes) {
68302 return hemilobes.map(function(lobe) {
68303 var x0 = project(lobe[0][0], lobe[0][1])[0], x1 = project(lobe[2][0], lobe[2][1])[0], y0 = project(lobe[1][0], lobe[0][1])[1], y1 = project(lobe[1][0], lobe[1][1])[1], t;
68304 if (y0 > y1) t = y0, y0 = y1, y1 = t;
68305 return [ [ x0, y0 ], [ x1, y1 ] ];
68306 });
68307 });
68308 }
68309 if (project.invert) forward.invert = function(x, y) {
68310 var hemibounds = bounds[+(y < 0)], hemilobes = lobes[+(y < 0)];
68311 for (var i = 0, n = hemibounds.length; i < n; ++i) {
68312 var b = hemibounds[i];
68313 if (b[0][0] <= x && x < b[1][0] && b[0][1] <= y && y < b[1][1]) {
68314 var coordinates = project.invert(x - project(hemilobes[i][1][0], 0)[0], y);
68315 coordinates[0] += hemilobes[i][1][0];
68316 return pointEqual(forward(coordinates[0], coordinates[1]), [ x, y ]) ? coordinates : null;
68317 }
68318 }
68319 };
68320 var projection = d3.geo.projection(forward), stream_ = projection.stream;
68321 projection.stream = function(stream) {
68322 var rotate = projection.rotate(), rotateStream = stream_(stream), sphereStream = (projection.rotate([ 0, 0 ]),
68323 stream_(stream));
68324 projection.rotate(rotate);
68325 rotateStream.sphere = function() {
68326 d3.geo.stream(sphere(), sphereStream);
68327 };
68328 return rotateStream;
68329 };
68330 projection.lobes = function(_) {
68331 if (!arguments.length) return lobes.map(function(lobes) {
68332 return lobes.map(function(lobe) {
68333 return [ [ lobe[0][0] * 180 / π, lobe[0][1] * 180 / π ], [ lobe[1][0] * 180 / π, lobe[1][1] * 180 / π ], [ lobe[2][0] * 180 / π, lobe[2][1] * 180 / π ] ];
68334 });
68335 });
68336 lobes = _.map(function(lobes) {
68337 return lobes.map(function(lobe) {
68338 return [ [ lobe[0][0] * π / 180, lobe[0][1] * π / 180 ], [ lobe[1][0] * π / 180, lobe[1][1] * π / 180 ], [ lobe[2][0] * π / 180, lobe[2][1] * π / 180 ] ];
68339 });
68340 });
68341 reset();
68342 return projection;
68343 };
68344 function sphere() {
68345 var ε = 1e-6, coordinates = [];
68346 for (var i = 0, n = lobes[0].length; i < n; ++i) {
68347 var lobe = lobes[0][i], λ0 = lobe[0][0] * 180 / π, φ0 = lobe[0][1] * 180 / π, φ1 = lobe[1][1] * 180 / π, λ2 = lobe[2][0] * 180 / π, φ2 = lobe[2][1] * 180 / π;
68348 coordinates.push(resample([ [ λ0 + ε, φ0 + ε ], [ λ0 + ε, φ1 - ε ], [ λ2 - ε, φ1 - ε ], [ λ2 - ε, φ2 + ε ] ], 30));
68349 }
68350 for (var i = lobes[1].length - 1; i >= 0; --i) {
68351 var lobe = lobes[1][i], λ0 = lobe[0][0] * 180 / π, φ0 = lobe[0][1] * 180 / π, φ1 = lobe[1][1] * 180 / π, λ2 = lobe[2][0] * 180 / π, φ2 = lobe[2][1] * 180 / π;
68352 coordinates.push(resample([ [ λ2 - ε, φ2 - ε ], [ λ2 - ε, φ1 + ε ], [ λ0 + ε, φ1 + ε ], [ λ0 + ε, φ0 - ε ] ], 30));
68353 }
68354 return {
68355 type: "Polygon",
68356 coordinates: [ d3.merge(coordinates) ]
68357 };
68358 }
68359 function resample(coordinates, m) {
68360 var i = -1, n = coordinates.length, p0 = coordinates[0], p1, dx, dy, resampled = [];
68361 while (++i < n) {
68362 p1 = coordinates[i];
68363 dx = (p1[0] - p0[0]) / m;
68364 dy = (p1[1] - p0[1]) / m;
68365 for (var j = 0; j < m; ++j) resampled.push([ p0[0] + j * dx, p0[1] + j * dy ]);
68366 p0 = p1;
68367 }
68368 resampled.push(p1);
68369 return resampled;
68370 }
68371 function pointEqual(a, b) {
68372 return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
68373 }
68374 return projection;
68375 };
68376 function eckert4(λ, φ) {
68377 var k = (2 + halfπ) * Math.sin(φ);
68378 φ /= 2;
68379 for (var i = 0, δ = Infinity; i < 10 && Math.abs(δ) > ε; i++) {
68380 var cosφ = Math.cos(φ);
68381 φ -= δ = (φ + Math.sin(φ) * (cosφ + 2) - k) / (2 * cosφ * (1 + cosφ));
68382 }
68383 return [ 2 / Math.sqrt(π * (4 + π)) * λ * (1 + Math.cos(φ)), 2 * Math.sqrt(π / (4 + π)) * Math.sin(φ) ];
68384 }
68385 eckert4.invert = function(x, y) {
68386 var A = .5 * y * Math.sqrt((4 + π) / π), k = asin(A), c = Math.cos(k);
68387 return [ x / (2 / Math.sqrt(π * (4 + π)) * (1 + c)), asin((k + A * (c + 2)) / (2 + halfπ)) ];
68388 };
68389 (d3.geo.eckert4 = function() {
68390 return projection(eckert4);
68391 }).raw = eckert4;
68392 var hammerAzimuthalEqualArea = d3.geo.azimuthalEqualArea.raw;
68393 function hammer(A, B) {
68394 if (arguments.length < 2) B = A;
68395 if (B === 1) return hammerAzimuthalEqualArea;
68396 if (B === Infinity) return hammerQuarticAuthalic;
68397 function forward(λ, φ) {
68398 var coordinates = hammerAzimuthalEqualArea(λ / B, φ);
68399 coordinates[0] *= A;
68400 return coordinates;
68401 }
68402 forward.invert = function(x, y) {
68403 var coordinates = hammerAzimuthalEqualArea.invert(x / A, y);
68404 coordinates[0] *= B;
68405 return coordinates;
68406 };
68407 return forward;
68408 }
68409 function hammerProjection() {
68410 var B = 2, m = projectionMutator(hammer), p = m(B);
68411 p.coefficient = function(_) {
68412 if (!arguments.length) return B;
68413 return m(B = +_);
68414 };
68415 return p;
68416 }
68417 function hammerQuarticAuthalic(λ, φ) {
68418 return [ λ * Math.cos(φ) / Math.cos(φ /= 2), 2 * Math.sin(φ) ];
68419 }
68420 hammerQuarticAuthalic.invert = function(x, y) {
68421 var φ = 2 * asin(y / 2);
68422 return [ x * Math.cos(φ / 2) / Math.cos(φ), φ ];
68423 };
68424 (d3.geo.hammer = hammerProjection).raw = hammer;
68425 function kavrayskiy7(λ, φ) {
68426 return [ 3 * λ / (2 * π) * Math.sqrt(π * π / 3 - φ * φ), φ ];
68427 }
68428 kavrayskiy7.invert = function(x, y) {
68429 return [ 2 / 3 * π * x / Math.sqrt(π * π / 3 - y * y), y ];
68430 };
68431 (d3.geo.kavrayskiy7 = function() {
68432 return projection(kavrayskiy7);
68433 }).raw = kavrayskiy7;
68434 function miller(λ, φ) {
68435 return [ λ, 1.25 * Math.log(Math.tan(π / 4 + .4 * φ)) ];
68436 }
68437 miller.invert = function(x, y) {
68438 return [ x, 2.5 * Math.atan(Math.exp(.8 * y)) - .625 * π ];
68439 };
68440 (d3.geo.miller = function() {
68441 return projection(miller);
68442 }).raw = miller;
68443 function mollweideBromleyθ(Cp) {
68444 return function(θ) {
68445 var Cpsinθ = Cp * Math.sin(θ), i = 30, δ;
68446 do θ -= δ = (θ + Math.sin(θ) - Cpsinθ) / (1 + Math.cos(θ)); while (Math.abs(δ) > ε && --i > 0);
68447 return θ / 2;
68448 };
68449 }
68450 function mollweideBromley(Cx, Cy, Cp) {
68451 var θ = mollweideBromleyθ(Cp);
68452 function forward(λ, φ) {
68453 return [ Cx * λ * Math.cos(φ = θ(φ)), Cy * Math.sin(φ) ];
68454 }
68455 forward.invert = function(x, y) {
68456 var θ = asin(y / Cy);
68457 return [ x / (Cx * Math.cos(θ)), asin((2 * θ + Math.sin(2 * θ)) / Cp) ];
68458 };
68459 return forward;
68460 }
68461 var mollweideθ = mollweideBromleyθ(π), mollweide = mollweideBromley(Math.SQRT2 / halfπ, Math.SQRT2, π);
68462 (d3.geo.mollweide = function() {
68463 return projection(mollweide);
68464 }).raw = mollweide;
68465 function naturalEarth(λ, φ) {
68466 var φ2 = φ * φ, φ4 = φ2 * φ2;
68467 return [ λ * (.8707 - .131979 * φ2 + φ4 * (-.013791 + φ4 * (.003971 * φ2 - .001529 * φ4))), φ * (1.007226 + φ2 * (.015085 + φ4 * (-.044475 + .028874 * φ2 - .005916 * φ4))) ];
68468 }
68469 naturalEarth.invert = function(x, y) {
68470 var φ = y, i = 25, δ;
68471 do {
68472 var φ2 = φ * φ, φ4 = φ2 * φ2;
68473 φ -= δ = (φ * (1.007226 + φ2 * (.015085 + φ4 * (-.044475 + .028874 * φ2 - .005916 * φ4))) - y) / (1.007226 + φ2 * (.015085 * 3 + φ4 * (-.044475 * 7 + .028874 * 9 * φ2 - .005916 * 11 * φ4)));
68474 } while (Math.abs(δ) > ε && --i > 0);
68475 return [ x / (.8707 + (φ2 = φ * φ) * (-.131979 + φ2 * (-.013791 + φ2 * φ2 * φ2 * (.003971 - .001529 * φ2)))), φ ];
68476 };
68477 (d3.geo.naturalEarth = function() {
68478 return projection(naturalEarth);
68479 }).raw = naturalEarth;
68480 var robinsonConstants = [ [ .9986, -.062 ], [ 1, 0 ], [ .9986, .062 ], [ .9954, .124 ], [ .99, .186 ], [ .9822, .248 ], [ .973, .31 ], [ .96, .372 ], [ .9427, .434 ], [ .9216, .4958 ], [ .8962, .5571 ], [ .8679, .6176 ], [ .835, .6769 ], [ .7986, .7346 ], [ .7597, .7903 ], [ .7186, .8435 ], [ .6732, .8936 ], [ .6213, .9394 ], [ .5722, .9761 ], [ .5322, 1 ] ];
68481 robinsonConstants.forEach(function(d) {
68482 d[1] *= 1.0144;
68483 });
68484 function robinson(λ, φ) {
68485 var i = Math.min(18, Math.abs(φ) * 36 / π), i0 = Math.floor(i), di = i - i0, ax = (k = robinsonConstants[i0])[0], ay = k[1], bx = (k = robinsonConstants[++i0])[0], by = k[1], cx = (k = robinsonConstants[Math.min(19, ++i0)])[0], cy = k[1], k;
68486 return [ λ * (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2), (φ > 0 ? halfπ : -halfπ) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2) ];
68487 }
68488 robinson.invert = function(x, y) {
68489 var yy = y / halfπ, φ = yy * 90, i = Math.min(18, Math.abs(φ / 5)), i0 = Math.max(0, Math.floor(i));
68490 do {
68491 var ay = robinsonConstants[i0][1], by = robinsonConstants[i0 + 1][1], cy = robinsonConstants[Math.min(19, i0 + 2)][1], u = cy - ay, v = cy - 2 * by + ay, t = 2 * (Math.abs(yy) - by) / u, c = v / u, di = t * (1 - c * t * (1 - 2 * c * t));
68492 if (di >= 0 || i0 === 1) {
68493 φ = (y >= 0 ? 5 : -5) * (di + i);
68494 var j = 50, δ;
68495 do {
68496 i = Math.min(18, Math.abs(φ) / 5);
68497 i0 = Math.floor(i);
68498 di = i - i0;
68499 ay = robinsonConstants[i0][1];
68500 by = robinsonConstants[i0 + 1][1];
68501 cy = robinsonConstants[Math.min(19, i0 + 2)][1];
68502 φ -= (δ = (y >= 0 ? halfπ : -halfπ) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2) - y) * degrees;
68503 } while (Math.abs(δ) > ε2 && --j > 0);
68504 break;
68505 }
68506 } while (--i0 >= 0);
68507 var ax = robinsonConstants[i0][0], bx = robinsonConstants[i0 + 1][0], cx = robinsonConstants[Math.min(19, i0 + 2)][0];
68508 return [ x / (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2), φ * radians ];
68509 };
68510 (d3.geo.robinson = function() {
68511 return projection(robinson);
68512 }).raw = robinson;
68513 function sinusoidal(λ, φ) {
68514 return [ λ * Math.cos(φ), φ ];
68515 }
68516 sinusoidal.invert = function(x, y) {
68517 return [ x / Math.cos(y), y ];
68518 };
68519 (d3.geo.sinusoidal = function() {
68520 return projection(sinusoidal);
68521 }).raw = sinusoidal;
68522 function aitoff(λ, φ) {
68523 var cosφ = Math.cos(φ), sinciα = sinci(acos(cosφ * Math.cos(λ /= 2)));
68524 return [ 2 * cosφ * Math.sin(λ) * sinciα, Math.sin(φ) * sinciα ];
68525 }
68526 aitoff.invert = function(x, y) {
68527 if (x * x + 4 * y * y > π * π + ε) return;
68528 var λ = x, φ = y, i = 25;
68529 do {
68530 var sinλ = Math.sin(λ), sinλ_2 = Math.sin(λ / 2), cosλ_2 = Math.cos(λ / 2), sinφ = Math.sin(φ), cosφ = Math.cos(φ), sin_2φ = Math.sin(2 * φ), sin2φ = sinφ * sinφ, cos2φ = cosφ * cosφ, sin2λ_2 = sinλ_2 * sinλ_2, C = 1 - cos2φ * cosλ_2 * cosλ_2, E = C ? acos(cosφ * cosλ_2) * Math.sqrt(F = 1 / C) : F = 0, F, fx = 2 * E * cosφ * sinλ_2 - x, fy = E * sinφ - y, δxδλ = F * (cos2φ * sin2λ_2 + E * cosφ * cosλ_2 * sin2φ), δxδφ = F * (.5 * sinλ * sin_2φ - E * 2 * sinφ * sinλ_2), δyδλ = F * .25 * (sin_2φ * sinλ_2 - E * sinφ * cos2φ * sinλ), δyδφ = F * (sin2φ * cosλ_2 + E * sin2λ_2 * cosφ), denominator = δxδφ * δyδλ - δyδφ * δxδλ;
68531 if (!denominator) break;
68532 var δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator;
68533 λ -= δλ, φ -= δφ;
68534 } while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0);
68535 return [ λ, φ ];
68536 };
68537 (d3.geo.aitoff = function() {
68538 return projection(aitoff);
68539 }).raw = aitoff;
68540 function winkel3(λ, φ) {
68541 var coordinates = aitoff(λ, φ);
68542 return [ (coordinates[0] + λ / halfπ) / 2, (coordinates[1] + φ) / 2 ];
68543 }
68544 winkel3.invert = function(x, y) {
68545 var λ = x, φ = y, i = 25;
68546 do {
68547 var cosφ = Math.cos(φ), sinφ = Math.sin(φ), sin_2φ = Math.sin(2 * φ), sin2φ = sinφ * sinφ, cos2φ = cosφ * cosφ, sinλ = Math.sin(λ), cosλ_2 = Math.cos(λ / 2), sinλ_2 = Math.sin(λ / 2), sin2λ_2 = sinλ_2 * sinλ_2, C = 1 - cos2φ * cosλ_2 * cosλ_2, E = C ? acos(cosφ * cosλ_2) * Math.sqrt(F = 1 / C) : F = 0, F, fx = .5 * (2 * E * cosφ * sinλ_2 + λ / halfπ) - x, fy = .5 * (E * sinφ + φ) - y, δxδλ = .5 * F * (cos2φ * sin2λ_2 + E * cosφ * cosλ_2 * sin2φ) + .5 / halfπ, δxδφ = F * (sinλ * sin_2φ / 4 - E * sinφ * sinλ_2), δyδλ = .125 * F * (sin_2φ * sinλ_2 - E * sinφ * cos2φ * sinλ), δyδφ = .5 * F * (sin2φ * cosλ_2 + E * sin2λ_2 * cosφ) + .5, denominator = δxδφ * δyδλ - δyδφ * δxδλ, δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator;
68548 λ -= δλ, φ -= δφ;
68549 } while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0);
68550 return [ λ, φ ];
68551 };
68552 (d3.geo.winkel3 = function() {
68553 return projection(winkel3);
68554 }).raw = winkel3;
68555}
68556
68557module.exports = addProjectionsToD3;
68558
68559},{}],258:[function(_dereq_,module,exports){
68560/**
68561* Copyright 2012-2020, Plotly, Inc.
68562* All rights reserved.
68563*
68564* This source code is licensed under the MIT license found in the
68565* LICENSE file in the root directory of this source tree.
68566*/
68567
68568
68569'use strict';
68570
68571var d3 = _dereq_('d3');
68572var Lib = _dereq_('../../lib');
68573var Registry = _dereq_('../../registry');
68574
68575var radians = Math.PI / 180;
68576var degrees = 180 / Math.PI;
68577var zoomstartStyle = {cursor: 'pointer'};
68578var zoomendStyle = {cursor: 'auto'};
68579
68580function createGeoZoom(geo, geoLayout) {
68581 var projection = geo.projection;
68582 var zoomConstructor;
68583
68584 if(geoLayout._isScoped) {
68585 zoomConstructor = zoomScoped;
68586 } else if(geoLayout._isClipped) {
68587 zoomConstructor = zoomClipped;
68588 } else {
68589 zoomConstructor = zoomNonClipped;
68590 }
68591
68592 // TODO add a conic-specific zoom
68593
68594 return zoomConstructor(geo, projection);
68595}
68596
68597module.exports = createGeoZoom;
68598
68599// common to all zoom types
68600function initZoom(geo, projection) {
68601 return d3.behavior.zoom()
68602 .translate(projection.translate())
68603 .scale(projection.scale());
68604}
68605
68606// sync zoom updates with user & full layout
68607function sync(geo, projection, cb) {
68608 var id = geo.id;
68609 var gd = geo.graphDiv;
68610 var layout = gd.layout;
68611 var userOpts = layout[id];
68612 var fullLayout = gd._fullLayout;
68613 var fullOpts = fullLayout[id];
68614
68615 var preGUI = {};
68616 var eventData = {};
68617
68618 function set(propStr, val) {
68619 preGUI[id + '.' + propStr] = Lib.nestedProperty(userOpts, propStr).get();
68620 Registry.call('_storeDirectGUIEdit', layout, fullLayout._preGUI, preGUI);
68621
68622 var fullNp = Lib.nestedProperty(fullOpts, propStr);
68623 if(fullNp.get() !== val) {
68624 fullNp.set(val);
68625 Lib.nestedProperty(userOpts, propStr).set(val);
68626 eventData[id + '.' + propStr] = val;
68627 }
68628 }
68629
68630 cb(set);
68631 set('projection.scale', projection.scale() / geo.fitScale);
68632 set('fitbounds', false);
68633 gd.emit('plotly_relayout', eventData);
68634}
68635
68636// zoom for scoped projections
68637function zoomScoped(geo, projection) {
68638 var zoom = initZoom(geo, projection);
68639
68640 function handleZoomstart() {
68641 d3.select(this).style(zoomstartStyle);
68642 }
68643
68644 function handleZoom() {
68645 projection
68646 .scale(d3.event.scale)
68647 .translate(d3.event.translate);
68648 geo.render();
68649
68650 var center = projection.invert(geo.midPt);
68651 geo.graphDiv.emit('plotly_relayouting', {
68652 'geo.projection.scale': projection.scale() / geo.fitScale,
68653 'geo.center.lon': center[0],
68654 'geo.center.lat': center[1]
68655 });
68656 }
68657
68658 function syncCb(set) {
68659 var center = projection.invert(geo.midPt);
68660
68661 set('center.lon', center[0]);
68662 set('center.lat', center[1]);
68663 }
68664
68665 function handleZoomend() {
68666 d3.select(this).style(zoomendStyle);
68667 sync(geo, projection, syncCb);
68668 }
68669
68670 zoom
68671 .on('zoomstart', handleZoomstart)
68672 .on('zoom', handleZoom)
68673 .on('zoomend', handleZoomend);
68674
68675 return zoom;
68676}
68677
68678// zoom for non-clipped projections
68679function zoomNonClipped(geo, projection) {
68680 var zoom = initZoom(geo, projection);
68681
68682 var INSIDETOLORANCEPXS = 2;
68683
68684 var mouse0, rotate0, translate0, lastRotate, zoomPoint,
68685 mouse1, rotate1, point1, didZoom;
68686
68687 function position(x) { return projection.invert(x); }
68688
68689 function outside(x) {
68690 var pos = position(x);
68691 if(!pos) return true;
68692
68693 var pt = projection(pos);
68694 return (
68695 Math.abs(pt[0] - x[0]) > INSIDETOLORANCEPXS ||
68696 Math.abs(pt[1] - x[1]) > INSIDETOLORANCEPXS
68697 );
68698 }
68699
68700 function handleZoomstart() {
68701 d3.select(this).style(zoomstartStyle);
68702
68703 mouse0 = d3.mouse(this);
68704 rotate0 = projection.rotate();
68705 translate0 = projection.translate();
68706 lastRotate = rotate0;
68707 zoomPoint = position(mouse0);
68708 }
68709
68710 function handleZoom() {
68711 mouse1 = d3.mouse(this);
68712
68713 if(outside(mouse0)) {
68714 zoom.scale(projection.scale());
68715 zoom.translate(projection.translate());
68716 return;
68717 }
68718
68719 projection.scale(d3.event.scale);
68720 projection.translate([translate0[0], d3.event.translate[1]]);
68721
68722 if(!zoomPoint) {
68723 mouse0 = mouse1;
68724 zoomPoint = position(mouse0);
68725 } else if(position(mouse1)) {
68726 point1 = position(mouse1);
68727 rotate1 = [lastRotate[0] + (point1[0] - zoomPoint[0]), rotate0[1], rotate0[2]];
68728 projection.rotate(rotate1);
68729 lastRotate = rotate1;
68730 }
68731
68732 didZoom = true;
68733 geo.render();
68734
68735 var rotate = projection.rotate();
68736 var center = projection.invert(geo.midPt);
68737 geo.graphDiv.emit('plotly_relayouting', {
68738 'geo.projection.scale': projection.scale() / geo.fitScale,
68739 'geo.center.lon': center[0],
68740 'geo.center.lat': center[1],
68741 'geo.projection.rotation.lon': -rotate[0]
68742 });
68743 }
68744
68745 function handleZoomend() {
68746 d3.select(this).style(zoomendStyle);
68747 if(didZoom) sync(geo, projection, syncCb);
68748 }
68749
68750 function syncCb(set) {
68751 var rotate = projection.rotate();
68752 var center = projection.invert(geo.midPt);
68753
68754 set('projection.rotation.lon', -rotate[0]);
68755 set('center.lon', center[0]);
68756 set('center.lat', center[1]);
68757 }
68758
68759 zoom
68760 .on('zoomstart', handleZoomstart)
68761 .on('zoom', handleZoom)
68762 .on('zoomend', handleZoomend);
68763
68764 return zoom;
68765}
68766
68767// zoom for clipped projections
68768// inspired by https://www.jasondavies.com/maps/d3.geo.zoom.js
68769function zoomClipped(geo, projection) {
68770 var view = {r: projection.rotate(), k: projection.scale()};
68771 var zoom = initZoom(geo, projection);
68772 var event = d3eventDispatch(zoom, 'zoomstart', 'zoom', 'zoomend');
68773 var zooming = 0;
68774 var zoomOn = zoom.on;
68775
68776 var zoomPoint;
68777
68778 zoom.on('zoomstart', function() {
68779 d3.select(this).style(zoomstartStyle);
68780
68781 var mouse0 = d3.mouse(this);
68782 var rotate0 = projection.rotate();
68783 var lastRotate = rotate0;
68784 var translate0 = projection.translate();
68785 var q = quaternionFromEuler(rotate0);
68786
68787 zoomPoint = position(projection, mouse0);
68788
68789 zoomOn.call(zoom, 'zoom', function() {
68790 var mouse1 = d3.mouse(this);
68791
68792 projection.scale(view.k = d3.event.scale);
68793
68794 if(!zoomPoint) {
68795 // if no zoomPoint, the mouse wasn't over the actual geography yet
68796 // maybe this point is the start... we'll find out next time!
68797 mouse0 = mouse1;
68798 zoomPoint = position(projection, mouse0);
68799 } else if(position(projection, mouse1)) {
68800 // check if the point is on the map
68801 // if not, don't do anything new but scale
68802 // if it is, then we can assume between will exist below
68803 // so we don't need the 'bank' function, whatever that is.
68804
68805 // go back to original projection temporarily
68806 // except for scale... that's kind of independent?
68807 projection
68808 .rotate(rotate0)
68809 .translate(translate0);
68810
68811 // calculate the new params
68812 var point1 = position(projection, mouse1);
68813 var between = rotateBetween(zoomPoint, point1);
68814 var newEuler = eulerFromQuaternion(multiply(q, between));
68815 var rotateAngles = view.r = unRoll(newEuler, zoomPoint, lastRotate);
68816
68817 if(!isFinite(rotateAngles[0]) || !isFinite(rotateAngles[1]) ||
68818 !isFinite(rotateAngles[2])) {
68819 rotateAngles = lastRotate;
68820 }
68821
68822 // update the projection
68823 projection.rotate(rotateAngles);
68824 lastRotate = rotateAngles;
68825 }
68826
68827 zoomed(event.of(this, arguments));
68828 });
68829
68830 zoomstarted(event.of(this, arguments));
68831 })
68832 .on('zoomend', function() {
68833 d3.select(this).style(zoomendStyle);
68834 zoomOn.call(zoom, 'zoom', null);
68835 zoomended(event.of(this, arguments));
68836 sync(geo, projection, syncCb);
68837 })
68838 .on('zoom.redraw', function() {
68839 geo.render();
68840
68841 var _rotate = projection.rotate();
68842 geo.graphDiv.emit('plotly_relayouting', {
68843 'geo.projection.scale': projection.scale() / geo.fitScale,
68844 'geo.projection.rotation.lon': -_rotate[0],
68845 'geo.projection.rotation.lat': -_rotate[1]
68846 });
68847 });
68848
68849 function zoomstarted(dispatch) {
68850 if(!zooming++) dispatch({type: 'zoomstart'});
68851 }
68852
68853 function zoomed(dispatch) {
68854 dispatch({type: 'zoom'});
68855 }
68856
68857 function zoomended(dispatch) {
68858 if(!--zooming) dispatch({type: 'zoomend'});
68859 }
68860
68861 function syncCb(set) {
68862 var _rotate = projection.rotate();
68863 set('projection.rotation.lon', -_rotate[0]);
68864 set('projection.rotation.lat', -_rotate[1]);
68865 }
68866
68867 return d3.rebind(zoom, event, 'on');
68868}
68869
68870// -- helper functions for zoomClipped
68871
68872function position(projection, point) {
68873 var spherical = projection.invert(point);
68874 return spherical && isFinite(spherical[0]) && isFinite(spherical[1]) && cartesian(spherical);
68875}
68876
68877function quaternionFromEuler(euler) {
68878 var lambda = 0.5 * euler[0] * radians;
68879 var phi = 0.5 * euler[1] * radians;
68880 var gamma = 0.5 * euler[2] * radians;
68881 var sinLambda = Math.sin(lambda);
68882 var cosLambda = Math.cos(lambda);
68883 var sinPhi = Math.sin(phi);
68884 var cosPhi = Math.cos(phi);
68885 var sinGamma = Math.sin(gamma);
68886 var cosGamma = Math.cos(gamma);
68887 return [
68888 cosLambda * cosPhi * cosGamma + sinLambda * sinPhi * sinGamma,
68889 sinLambda * cosPhi * cosGamma - cosLambda * sinPhi * sinGamma,
68890 cosLambda * sinPhi * cosGamma + sinLambda * cosPhi * sinGamma,
68891 cosLambda * cosPhi * sinGamma - sinLambda * sinPhi * cosGamma
68892 ];
68893}
68894
68895function multiply(a, b) {
68896 var a0 = a[0];
68897 var a1 = a[1];
68898 var a2 = a[2];
68899 var a3 = a[3];
68900 var b0 = b[0];
68901 var b1 = b[1];
68902 var b2 = b[2];
68903 var b3 = b[3];
68904 return [
68905 a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
68906 a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
68907 a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
68908 a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
68909 ];
68910}
68911
68912function rotateBetween(a, b) {
68913 if(!a || !b) return;
68914 var axis = cross(a, b);
68915 var norm = Math.sqrt(dot(axis, axis));
68916 var halfgamma = 0.5 * Math.acos(Math.max(-1, Math.min(1, dot(a, b))));
68917 var k = Math.sin(halfgamma) / norm;
68918 return norm && [Math.cos(halfgamma), axis[2] * k, -axis[1] * k, axis[0] * k];
68919}
68920
68921// input:
68922// rotateAngles: a calculated set of Euler angles
68923// pt: a point (cartesian in 3-space) to keep fixed
68924// roll0: an initial roll, to be preserved
68925// output:
68926// a set of Euler angles that preserve the projection of pt
68927// but set roll (output[2]) equal to roll0
68928// note that this doesn't depend on the particular projection,
68929// just on the rotation angles
68930function unRoll(rotateAngles, pt, lastRotate) {
68931 // calculate the fixed point transformed by these Euler angles
68932 // but with the desired roll undone
68933 var ptRotated = rotateCartesian(pt, 2, rotateAngles[0]);
68934 ptRotated = rotateCartesian(ptRotated, 1, rotateAngles[1]);
68935 ptRotated = rotateCartesian(ptRotated, 0, rotateAngles[2] - lastRotate[2]);
68936
68937 var x = pt[0];
68938 var y = pt[1];
68939 var z = pt[2];
68940 var f = ptRotated[0];
68941 var g = ptRotated[1];
68942 var h = ptRotated[2];
68943
68944 // the following essentially solves:
68945 // ptRotated = rotateCartesian(rotateCartesian(pt, 2, newYaw), 1, newPitch)
68946 // for newYaw and newPitch, as best it can
68947 var theta = Math.atan2(y, x) * degrees;
68948 var a = Math.sqrt(x * x + y * y);
68949 var b;
68950 var newYaw1;
68951
68952 if(Math.abs(g) > a) {
68953 newYaw1 = (g > 0 ? 90 : -90) - theta;
68954 b = 0;
68955 } else {
68956 newYaw1 = Math.asin(g / a) * degrees - theta;
68957 b = Math.sqrt(a * a - g * g);
68958 }
68959
68960 var newYaw2 = 180 - newYaw1 - 2 * theta;
68961 var newPitch1 = (Math.atan2(h, f) - Math.atan2(z, b)) * degrees;
68962 var newPitch2 = (Math.atan2(h, f) - Math.atan2(z, -b)) * degrees;
68963
68964 // which is closest to lastRotate[0,1]: newYaw/Pitch or newYaw2/Pitch2?
68965 var dist1 = angleDistance(lastRotate[0], lastRotate[1], newYaw1, newPitch1);
68966 var dist2 = angleDistance(lastRotate[0], lastRotate[1], newYaw2, newPitch2);
68967
68968 if(dist1 <= dist2) return [newYaw1, newPitch1, lastRotate[2]];
68969 else return [newYaw2, newPitch2, lastRotate[2]];
68970}
68971
68972function angleDistance(yaw0, pitch0, yaw1, pitch1) {
68973 var dYaw = angleMod(yaw1 - yaw0);
68974 var dPitch = angleMod(pitch1 - pitch0);
68975 return Math.sqrt(dYaw * dYaw + dPitch * dPitch);
68976}
68977
68978// reduce an angle in degrees to [-180,180]
68979function angleMod(angle) {
68980 return (angle % 360 + 540) % 360 - 180;
68981}
68982
68983// rotate a cartesian vector
68984// axis is 0 (x), 1 (y), or 2 (z)
68985// angle is in degrees
68986function rotateCartesian(vector, axis, angle) {
68987 var angleRads = angle * radians;
68988 var vectorOut = vector.slice();
68989 var ax1 = (axis === 0) ? 1 : 0;
68990 var ax2 = (axis === 2) ? 1 : 2;
68991 var cosa = Math.cos(angleRads);
68992 var sina = Math.sin(angleRads);
68993
68994 vectorOut[ax1] = vector[ax1] * cosa - vector[ax2] * sina;
68995 vectorOut[ax2] = vector[ax2] * cosa + vector[ax1] * sina;
68996
68997 return vectorOut;
68998}
68999function eulerFromQuaternion(q) {
69000 return [
69001 Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
69002 Math.asin(Math.max(-1, Math.min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
69003 Math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
69004 ];
69005}
69006
69007function cartesian(spherical) {
69008 var lambda = spherical[0] * radians;
69009 var phi = spherical[1] * radians;
69010 var cosPhi = Math.cos(phi);
69011 return [
69012 cosPhi * Math.cos(lambda),
69013 cosPhi * Math.sin(lambda),
69014 Math.sin(phi)
69015 ];
69016}
69017
69018function dot(a, b) {
69019 var s = 0;
69020 for(var i = 0, n = a.length; i < n; ++i) s += a[i] * b[i];
69021 return s;
69022}
69023
69024function cross(a, b) {
69025 return [
69026 a[1] * b[2] - a[2] * b[1],
69027 a[2] * b[0] - a[0] * b[2],
69028 a[0] * b[1] - a[1] * b[0]
69029 ];
69030}
69031
69032// Like d3.dispatch, but for custom events abstracting native UI events. These
69033// events have a target component (such as a brush), a target element (such as
69034// the svg:g element containing the brush) and the standard arguments `d` (the
69035// target element's data) and `i` (the selection index of the target element).
69036function d3eventDispatch(target) {
69037 var i = 0;
69038 var n = arguments.length;
69039 var argumentz = [];
69040
69041 while(++i < n) argumentz.push(arguments[i]);
69042
69043 var dispatch = d3.dispatch.apply(null, argumentz);
69044
69045 // Creates a dispatch context for the specified `thiz` (typically, the target
69046 // DOM element that received the source event) and `argumentz` (typically, the
69047 // data `d` and index `i` of the target element). The returned function can be
69048 // used to dispatch an event to any registered listeners; the function takes a
69049 // single argument as input, being the event to dispatch. The event must have
69050 // a "type" attribute which corresponds to a type registered in the
69051 // constructor. This context will automatically populate the "sourceEvent" and
69052 // "target" attributes of the event, as well as setting the `d3.event` global
69053 // for the duration of the notification.
69054 dispatch.of = function(thiz, argumentz) {
69055 return function(e1) {
69056 var e0;
69057 try {
69058 e0 = e1.sourceEvent = d3.event;
69059 e1.target = target;
69060 d3.event = e1;
69061 dispatch[e1.type].apply(thiz, argumentz);
69062 } finally {
69063 d3.event = e0;
69064 }
69065 };
69066 };
69067
69068 return dispatch;
69069}
69070
69071},{"../../lib":177,"../../registry":272,"d3":13}],259:[function(_dereq_,module,exports){
69072/**
69073* Copyright 2012-2020, Plotly, Inc.
69074* All rights reserved.
69075*
69076* This source code is licensed under the MIT license found in the
69077* LICENSE file in the root directory of this source tree.
69078*/
69079
69080'use strict';
69081
69082var Registry = _dereq_('../registry');
69083var SUBPLOT_PATTERN = _dereq_('./cartesian/constants').SUBPLOT_PATTERN;
69084
69085/**
69086 * Get calcdata trace(s) associated with a given subplot
69087 *
69088 * @param {array} calcData: as in gd.calcdata
69089 * @param {string} type: subplot type
69090 * @param {string} subplotId: subplot id to look for
69091 *
69092 * @return {array} array of calcdata traces
69093 */
69094exports.getSubplotCalcData = function(calcData, type, subplotId) {
69095 var basePlotModule = Registry.subplotsRegistry[type];
69096 if(!basePlotModule) return [];
69097
69098 var attr = basePlotModule.attr;
69099 var subplotCalcData = [];
69100
69101 for(var i = 0; i < calcData.length; i++) {
69102 var calcTrace = calcData[i];
69103 var trace = calcTrace[0].trace;
69104
69105 if(trace[attr] === subplotId) subplotCalcData.push(calcTrace);
69106 }
69107
69108 return subplotCalcData;
69109};
69110/**
69111 * Get calcdata trace(s) that can be plotted with a given module
69112 * NOTE: this isn't necessarily just exactly matching trace type,
69113 * if multiple trace types use the same plotting routine, they will be
69114 * collected here.
69115 * In order to not plot the same thing multiple times, we return two arrays,
69116 * the calcdata we *will* plot with this module, and the ones we *won't*
69117 *
69118 * @param {array} calcdata: as in gd.calcdata
69119 * @param {object|string|fn} arg1:
69120 * the plotting module, or its name, or its plot method
69121 *
69122 * @return {array[array]} [foundCalcdata, remainingCalcdata]
69123 */
69124exports.getModuleCalcData = function(calcdata, arg1) {
69125 var moduleCalcData = [];
69126 var remainingCalcData = [];
69127
69128 var plotMethod;
69129 if(typeof arg1 === 'string') {
69130 plotMethod = Registry.getModule(arg1).plot;
69131 } else if(typeof arg1 === 'function') {
69132 plotMethod = arg1;
69133 } else {
69134 plotMethod = arg1.plot;
69135 }
69136 if(!plotMethod) {
69137 return [moduleCalcData, calcdata];
69138 }
69139
69140 for(var i = 0; i < calcdata.length; i++) {
69141 var cd = calcdata[i];
69142 var trace = cd[0].trace;
69143 // N.B.
69144 // - 'legendonly' traces do not make it past here
69145 // - skip over 'visible' traces that got trimmed completely during calc transforms
69146 if(trace.visible !== true || trace._length === 0) continue;
69147
69148 // group calcdata trace not by 'module' (as the name of this function
69149 // would suggest), but by 'module plot method' so that if some traces
69150 // share the same module plot method (e.g. bar and histogram), we
69151 // only call it one!
69152 if(trace._module.plot === plotMethod) {
69153 moduleCalcData.push(cd);
69154 } else {
69155 remainingCalcData.push(cd);
69156 }
69157 }
69158
69159 return [moduleCalcData, remainingCalcData];
69160};
69161
69162/**
69163 * Get the data trace(s) associated with a given subplot.
69164 *
69165 * @param {array} data plotly full data array.
69166 * @param {string} type subplot type to look for.
69167 * @param {string} subplotId subplot id to look for.
69168 *
69169 * @return {array} list of trace objects.
69170 *
69171 */
69172exports.getSubplotData = function getSubplotData(data, type, subplotId) {
69173 if(!Registry.subplotsRegistry[type]) return [];
69174
69175 var attr = Registry.subplotsRegistry[type].attr;
69176 var subplotData = [];
69177 var trace, subplotX, subplotY;
69178
69179 if(type === 'gl2d') {
69180 var spmatch = subplotId.match(SUBPLOT_PATTERN);
69181 subplotX = 'x' + spmatch[1];
69182 subplotY = 'y' + spmatch[2];
69183 }
69184
69185 for(var i = 0; i < data.length; i++) {
69186 trace = data[i];
69187
69188 if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) {
69189 if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) {
69190 subplotData.push(trace);
69191 }
69192 } else {
69193 if(trace[attr] === subplotId) subplotData.push(trace);
69194 }
69195 }
69196
69197 return subplotData;
69198};
69199
69200},{"../registry":272,"./cartesian/constants":228}],260:[function(_dereq_,module,exports){
69201/**
69202* Copyright 2012-2020, Plotly, Inc.
69203* All rights reserved.
69204*
69205* This source code is licensed under the MIT license found in the
69206* LICENSE file in the root directory of this source tree.
69207*/
69208
69209
69210'use strict';
69211
69212function xformMatrix(m, v) {
69213 var out = [0, 0, 0, 0];
69214 var i, j;
69215
69216 for(i = 0; i < 4; ++i) {
69217 for(j = 0; j < 4; ++j) {
69218 out[j] += m[4 * i + j] * v[i];
69219 }
69220 }
69221
69222 return out;
69223}
69224
69225function project(camera, v) {
69226 var p = xformMatrix(camera.projection,
69227 xformMatrix(camera.view,
69228 xformMatrix(camera.model, [v[0], v[1], v[2], 1])));
69229 return p;
69230}
69231
69232module.exports = project;
69233
69234},{}],261:[function(_dereq_,module,exports){
69235/**
69236* Copyright 2012-2020, Plotly, Inc.
69237* All rights reserved.
69238*
69239* This source code is licensed under the MIT license found in the
69240* LICENSE file in the root directory of this source tree.
69241*/
69242
69243'use strict';
69244
69245var fontAttrs = _dereq_('./font_attributes');
69246var animationAttrs = _dereq_('./animation_attributes');
69247var colorAttrs = _dereq_('../components/color/attributes');
69248var drawNewShapeAttrs = _dereq_('../components/shapes/draw_newshape/attributes');
69249var padAttrs = _dereq_('./pad_attributes');
69250var extendFlat = _dereq_('../lib/extend').extendFlat;
69251
69252var globalFont = fontAttrs({
69253 editType: 'calc',
69254
69255});
69256globalFont.family.dflt = '"Open Sans", verdana, arial, sans-serif';
69257globalFont.size.dflt = 12;
69258globalFont.color.dflt = colorAttrs.defaultLine;
69259
69260module.exports = {
69261 font: globalFont,
69262 title: {
69263 text: {
69264 valType: 'string',
69265
69266 editType: 'layoutstyle',
69267
69268 },
69269 font: fontAttrs({
69270 editType: 'layoutstyle',
69271
69272 }),
69273 xref: {
69274 valType: 'enumerated',
69275 dflt: 'container',
69276 values: ['container', 'paper'],
69277
69278 editType: 'layoutstyle',
69279
69280 },
69281 yref: {
69282 valType: 'enumerated',
69283 dflt: 'container',
69284 values: ['container', 'paper'],
69285
69286 editType: 'layoutstyle',
69287
69288 },
69289 x: {
69290 valType: 'number',
69291 min: 0,
69292 max: 1,
69293 dflt: 0.5,
69294
69295 editType: 'layoutstyle',
69296
69297 },
69298 y: {
69299 valType: 'number',
69300 min: 0,
69301 max: 1,
69302 dflt: 'auto',
69303
69304 editType: 'layoutstyle',
69305
69306 },
69307 xanchor: {
69308 valType: 'enumerated',
69309 dflt: 'auto',
69310 values: ['auto', 'left', 'center', 'right'],
69311
69312 editType: 'layoutstyle',
69313
69314 },
69315 yanchor: {
69316 valType: 'enumerated',
69317 dflt: 'auto',
69318 values: ['auto', 'top', 'middle', 'bottom'],
69319
69320 editType: 'layoutstyle',
69321
69322 },
69323 pad: extendFlat(padAttrs({editType: 'layoutstyle'}), {
69324
69325 }),
69326 editType: 'layoutstyle'
69327 },
69328 uniformtext: {
69329 mode: {
69330 valType: 'enumerated',
69331 values: [false, 'hide', 'show'],
69332 dflt: false,
69333
69334 editType: 'plot',
69335
69336 },
69337 minsize: {
69338 valType: 'number',
69339 min: 0,
69340 dflt: 0,
69341
69342 editType: 'plot',
69343
69344 },
69345 editType: 'plot'
69346 },
69347 autosize: {
69348 valType: 'boolean',
69349
69350 dflt: false,
69351 // autosize, width, and height get special editType treatment in _relayout
69352 // so we can handle noop resizes more efficiently
69353 editType: 'none',
69354
69355 },
69356 width: {
69357 valType: 'number',
69358
69359 min: 10,
69360 dflt: 700,
69361 editType: 'plot',
69362
69363 },
69364 height: {
69365 valType: 'number',
69366
69367 min: 10,
69368 dflt: 450,
69369 editType: 'plot',
69370
69371 },
69372 margin: {
69373 l: {
69374 valType: 'number',
69375
69376 min: 0,
69377 dflt: 80,
69378 editType: 'plot',
69379
69380 },
69381 r: {
69382 valType: 'number',
69383
69384 min: 0,
69385 dflt: 80,
69386 editType: 'plot',
69387
69388 },
69389 t: {
69390 valType: 'number',
69391
69392 min: 0,
69393 dflt: 100,
69394 editType: 'plot',
69395
69396 },
69397 b: {
69398 valType: 'number',
69399
69400 min: 0,
69401 dflt: 80,
69402 editType: 'plot',
69403
69404 },
69405 pad: {
69406 valType: 'number',
69407
69408 min: 0,
69409 dflt: 0,
69410 editType: 'plot',
69411
69412 },
69413 autoexpand: {
69414 valType: 'boolean',
69415
69416 dflt: true,
69417 editType: 'plot',
69418
69419 },
69420 editType: 'plot'
69421 },
69422 paper_bgcolor: {
69423 valType: 'color',
69424
69425 dflt: colorAttrs.background,
69426 editType: 'plot',
69427
69428 },
69429 plot_bgcolor: {
69430 // defined here, but set in cartesian.supplyLayoutDefaults
69431 // because it needs to know if there are (2D) axes or not
69432 valType: 'color',
69433
69434 dflt: colorAttrs.background,
69435 editType: 'layoutstyle',
69436
69437 },
69438 separators: {
69439 valType: 'string',
69440
69441 editType: 'plot',
69442
69443 },
69444 hidesources: {
69445 valType: 'boolean',
69446
69447 dflt: false,
69448 editType: 'plot',
69449
69450 },
69451 showlegend: {
69452 // handled in legend.supplyLayoutDefaults
69453 // but included here because it's not in the legend object
69454 valType: 'boolean',
69455
69456 editType: 'legend',
69457
69458 },
69459 colorway: {
69460 valType: 'colorlist',
69461 dflt: colorAttrs.defaults,
69462
69463 editType: 'calc',
69464
69465 },
69466 datarevision: {
69467 valType: 'any',
69468
69469 editType: 'calc',
69470
69471 },
69472 uirevision: {
69473 valType: 'any',
69474
69475 editType: 'none',
69476
69477 },
69478 editrevision: {
69479 valType: 'any',
69480
69481 editType: 'none',
69482
69483 },
69484 selectionrevision: {
69485 valType: 'any',
69486
69487 editType: 'none',
69488
69489 },
69490 template: {
69491 valType: 'any',
69492
69493 editType: 'calc',
69494
69495 },
69496 modebar: {
69497 orientation: {
69498 valType: 'enumerated',
69499 values: ['v', 'h'],
69500 dflt: 'h',
69501
69502 editType: 'modebar',
69503
69504 },
69505 bgcolor: {
69506 valType: 'color',
69507
69508 editType: 'modebar',
69509
69510 },
69511 color: {
69512 valType: 'color',
69513
69514 editType: 'modebar',
69515
69516 },
69517 activecolor: {
69518 valType: 'color',
69519
69520 editType: 'modebar',
69521
69522 },
69523 uirevision: {
69524 valType: 'any',
69525
69526 editType: 'none',
69527
69528 },
69529 editType: 'modebar'
69530 },
69531
69532 newshape: drawNewShapeAttrs.newshape,
69533 activeshape: drawNewShapeAttrs.activeshape,
69534
69535 meta: {
69536 valType: 'any',
69537 arrayOk: true,
69538
69539 editType: 'plot',
69540
69541 },
69542
69543 transition: extendFlat({}, animationAttrs.transition, {
69544
69545 editType: 'none'
69546 }),
69547 _deprecated: {
69548 title: {
69549 valType: 'string',
69550
69551 editType: 'layoutstyle',
69552
69553 },
69554 titlefont: fontAttrs({
69555 editType: 'layoutstyle',
69556
69557 })
69558 }
69559};
69560
69561},{"../components/color/attributes":49,"../components/shapes/draw_newshape/attributes":132,"../lib/extend":170,"./animation_attributes":217,"./font_attributes":250,"./pad_attributes":262}],262:[function(_dereq_,module,exports){
69562/**
69563* Copyright 2012-2020, Plotly, Inc.
69564* All rights reserved.
69565*
69566* This source code is licensed under the MIT license found in the
69567* LICENSE file in the root directory of this source tree.
69568*/
69569
69570'use strict';
69571
69572/**
69573 * Creates a set of padding attributes.
69574 *
69575 * @param {object} opts
69576 * @param {string} editType:
69577 * the editType for all pieces of this padding definition
69578 *
69579 * @return {object} attributes object containing {t, r, b, l} as specified
69580 */
69581module.exports = function(opts) {
69582 var editType = opts.editType;
69583 return {
69584 t: {
69585 valType: 'number',
69586 dflt: 0,
69587
69588 editType: editType,
69589
69590 },
69591 r: {
69592 valType: 'number',
69593 dflt: 0,
69594
69595 editType: editType,
69596
69597 },
69598 b: {
69599 valType: 'number',
69600 dflt: 0,
69601
69602 editType: editType,
69603
69604 },
69605 l: {
69606 valType: 'number',
69607 dflt: 0,
69608
69609 editType: editType,
69610
69611 },
69612 editType: editType
69613 };
69614};
69615
69616},{}],263:[function(_dereq_,module,exports){
69617/**
69618* Copyright 2012-2020, Plotly, Inc.
69619* All rights reserved.
69620*
69621* This source code is licensed under the MIT license found in the
69622* LICENSE file in the root directory of this source tree.
69623*/
69624
69625'use strict';
69626
69627var d3 = _dereq_('d3');
69628var isNumeric = _dereq_('fast-isnumeric');
69629
69630var Registry = _dereq_('../registry');
69631var PlotSchema = _dereq_('../plot_api/plot_schema');
69632var Template = _dereq_('../plot_api/plot_template');
69633var Lib = _dereq_('../lib');
69634var Color = _dereq_('../components/color');
69635var BADNUM = _dereq_('../constants/numerical').BADNUM;
69636
69637var axisIDs = _dereq_('./cartesian/axis_ids');
69638var clearSelect = _dereq_('./cartesian/handle_outline').clearSelect;
69639
69640var animationAttrs = _dereq_('./animation_attributes');
69641var frameAttrs = _dereq_('./frame_attributes');
69642
69643var getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData;
69644
69645var relinkPrivateKeys = Lib.relinkPrivateKeys;
69646var _ = Lib._;
69647
69648var plots = module.exports = {};
69649
69650// Expose registry methods on Plots for backward-compatibility
69651Lib.extendFlat(plots, Registry);
69652
69653plots.attributes = _dereq_('./attributes');
69654plots.attributes.type.values = plots.allTypes;
69655plots.fontAttrs = _dereq_('./font_attributes');
69656plots.layoutAttributes = _dereq_('./layout_attributes');
69657
69658// TODO make this a plot attribute?
69659plots.fontWeight = 'normal';
69660
69661var transformsRegistry = plots.transformsRegistry;
69662
69663var commandModule = _dereq_('./command');
69664plots.executeAPICommand = commandModule.executeAPICommand;
69665plots.computeAPICommandBindings = commandModule.computeAPICommandBindings;
69666plots.manageCommandObserver = commandModule.manageCommandObserver;
69667plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings;
69668
69669// in some cases the browser doesn't seem to know how big
69670// the text is at first, so it needs to draw it,
69671// then wait a little, then draw it again
69672plots.redrawText = function(gd) {
69673 gd = Lib.getGraphDiv(gd);
69674
69675 var fullLayout = gd._fullLayout || {};
69676 var hasPolar = fullLayout._has && fullLayout._has('polar');
69677 var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r;
69678
69679 // do not work if polar is present
69680 if(hasLegacyPolar) return;
69681
69682 return new Promise(function(resolve) {
69683 setTimeout(function() {
69684 Registry.getComponentMethod('annotations', 'draw')(gd);
69685 Registry.getComponentMethod('legend', 'draw')(gd);
69686 Registry.getComponentMethod('colorbar', 'draw')(gd);
69687 resolve(plots.previousPromises(gd));
69688 }, 300);
69689 });
69690};
69691
69692// resize plot about the container size
69693plots.resize = function(gd) {
69694 gd = Lib.getGraphDiv(gd);
69695
69696 var resolveLastResize;
69697 var p = new Promise(function(resolve, reject) {
69698 if(!gd || Lib.isHidden(gd)) {
69699 reject(new Error('Resize must be passed a displayed plot div element.'));
69700 }
69701
69702 if(gd._redrawTimer) clearTimeout(gd._redrawTimer);
69703 if(gd._resolveResize) resolveLastResize = gd._resolveResize;
69704 gd._resolveResize = resolve;
69705
69706 gd._redrawTimer = setTimeout(function() {
69707 // return if there is nothing to resize or is hidden
69708 if(!gd.layout || (gd.layout.width && gd.layout.height) || Lib.isHidden(gd)) {
69709 resolve(gd);
69710 return;
69711 }
69712
69713 delete gd.layout.width;
69714 delete gd.layout.height;
69715
69716 // autosizing doesn't count as a change that needs saving
69717 var oldchanged = gd.changed;
69718
69719 // nor should it be included in the undo queue
69720 gd.autoplay = true;
69721
69722 Registry.call('relayout', gd, {autosize: true}).then(function() {
69723 gd.changed = oldchanged;
69724 // Only resolve if a new call hasn't been made!
69725 if(gd._resolveResize === resolve) {
69726 delete gd._resolveResize;
69727 resolve(gd);
69728 }
69729 });
69730 }, 100);
69731 });
69732
69733 if(resolveLastResize) resolveLastResize(p);
69734 return p;
69735};
69736
69737
69738// for use in Lib.syncOrAsync, check if there are any
69739// pending promises in this plot and wait for them
69740plots.previousPromises = function(gd) {
69741 if((gd._promises || []).length) {
69742 return Promise.all(gd._promises)
69743 .then(function() { gd._promises = []; });
69744 }
69745};
69746
69747/**
69748 * Adds the 'Edit chart' link.
69749 * Note that now Plotly.plot() calls this so it can regenerate whenever it replots
69750 *
69751 * Add source links to your graph inside the 'showSources' config argument.
69752 */
69753plots.addLinks = function(gd) {
69754 // Do not do anything if showLink and showSources are not set to true in config
69755 if(!gd._context.showLink && !gd._context.showSources) return;
69756
69757 var fullLayout = gd._fullLayout;
69758
69759 var linkContainer = Lib.ensureSingle(fullLayout._paper, 'text', 'js-plot-link-container', function(s) {
69760 s.style({
69761 'font-family': '"Open Sans", Arial, sans-serif',
69762 'font-size': '12px',
69763 'fill': Color.defaultLine,
69764 'pointer-events': 'all'
69765 })
69766 .each(function() {
69767 var links = d3.select(this);
69768 links.append('tspan').classed('js-link-to-tool', true);
69769 links.append('tspan').classed('js-link-spacer', true);
69770 links.append('tspan').classed('js-sourcelinks', true);
69771 });
69772 });
69773
69774 // The text node inside svg
69775 var text = linkContainer.node();
69776 var attrs = {y: fullLayout._paper.attr('height') - 9};
69777
69778 // If text's width is bigger than the layout
69779 // Check that text is a child node or document.body
69780 // because otherwise IE/Edge might throw an exception
69781 // when calling getComputedTextLength().
69782 // Apparently offsetParent is null for invisibles.
69783 if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) {
69784 // Align the text at the left
69785 attrs['text-anchor'] = 'start';
69786 attrs.x = 5;
69787 } else {
69788 // Align the text at the right
69789 attrs['text-anchor'] = 'end';
69790 attrs.x = fullLayout._paper.attr('width') - 7;
69791 }
69792
69793 linkContainer.attr(attrs);
69794
69795 var toolspan = linkContainer.select('.js-link-to-tool');
69796 var spacespan = linkContainer.select('.js-link-spacer');
69797 var sourcespan = linkContainer.select('.js-sourcelinks');
69798
69799 if(gd._context.showSources) gd._context.showSources(gd);
69800
69801 // 'view in plotly' link for embedded plots
69802 if(gd._context.showLink) positionPlayWithData(gd, toolspan);
69803
69804 // separator if we have both sources and tool link
69805 spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');
69806};
69807
69808// note that now this function is only adding the brand in
69809// iframes and 3rd-party apps
69810function positionPlayWithData(gd, container) {
69811 container.text('');
69812 var link = container.append('a')
69813 .attr({
69814 'xlink:xlink:href': '#',
69815 'class': 'link--impt link--embedview',
69816 'font-weight': 'bold'
69817 })
69818 .text(gd._context.linkText + ' ' + String.fromCharCode(187));
69819
69820 if(gd._context.sendData) {
69821 link.on('click', function() {
69822 plots.sendDataToCloud(gd);
69823 });
69824 } else {
69825 var path = window.location.pathname.split('/');
69826 var query = window.location.search;
69827 link.attr({
69828 'xlink:xlink:show': 'new',
69829 'xlink:xlink:href': '/' + path[2].split('.')[0] + '/' + path[1] + query
69830 });
69831 }
69832}
69833
69834plots.sendDataToCloud = function(gd) {
69835 var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL;
69836 if(!baseUrl) return;
69837
69838 gd.emit('plotly_beforeexport');
69839
69840 var hiddenformDiv = d3.select(gd)
69841 .append('div')
69842 .attr('id', 'hiddenform')
69843 .style('display', 'none');
69844
69845 var hiddenform = hiddenformDiv
69846 .append('form')
69847 .attr({
69848 action: baseUrl + '/external',
69849 method: 'post',
69850 target: '_blank'
69851 });
69852
69853 var hiddenformInput = hiddenform
69854 .append('input')
69855 .attr({
69856 type: 'text',
69857 name: 'data'
69858 });
69859
69860 hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
69861 hiddenform.node().submit();
69862 hiddenformDiv.remove();
69863
69864 gd.emit('plotly_afterexport');
69865 return false;
69866};
69867
69868var d3FormatKeys = [
69869 'days', 'shortDays', 'months', 'shortMonths', 'periods',
69870 'dateTime', 'date', 'time',
69871 'decimal', 'thousands', 'grouping', 'currency'
69872];
69873
69874var extraFormatKeys = [
69875 'year', 'month', 'dayMonth', 'dayMonthYear'
69876];
69877
69878/*
69879 * Fill in default values
69880 * @param {DOM element} gd
69881 * @param {object} opts
69882 * @param {boolean} opts.skipUpdateCalc: normally if the existing gd.calcdata looks
69883 * compatible with the new gd._fullData we finish by linking the new _fullData traces
69884 * to the old gd.calcdata, so it's correctly set if we're not going to recalc. But also,
69885 * if there are calcTransforms on the trace, we first remap data arrays from the old full
69886 * trace into the new one. Use skipUpdateCalc to defer this (needed by Plotly.react)
69887 *
69888 * gd.data, gd.layout:
69889 * are precisely what the user specified (except as modified by cleanData/cleanLayout),
69890 * these fields shouldn't be modified (except for filling in some auto values)
69891 * nor used directly after the supply defaults step.
69892 *
69893 * gd._fullData, gd._fullLayout:
69894 * are complete descriptions of how to draw the plot,
69895 * use these fields in all required computations.
69896 *
69897 * gd._fullLayout._modules
69898 * is a list of all the trace modules required to draw the plot.
69899 *
69900 * gd._fullLayout._visibleModules
69901 * subset of _modules, a list of modules corresponding to visible:true traces.
69902 *
69903 * gd._fullLayout._basePlotModules
69904 * is a list of all the plot modules required to draw the plot.
69905 *
69906 * gd._fullLayout._transformModules
69907 * is a list of all the transform modules invoked.
69908 *
69909 */
69910plots.supplyDefaults = function(gd, opts) {
69911 var skipUpdateCalc = opts && opts.skipUpdateCalc;
69912 var oldFullLayout = gd._fullLayout || {};
69913
69914 if(oldFullLayout._skipDefaults) {
69915 delete oldFullLayout._skipDefaults;
69916 return;
69917 }
69918
69919 var newFullLayout = gd._fullLayout = {};
69920 var newLayout = gd.layout || {};
69921
69922 var oldFullData = gd._fullData || [];
69923 var newFullData = gd._fullData = [];
69924 var newData = gd.data || [];
69925
69926 var oldCalcdata = gd.calcdata || [];
69927
69928 var context = gd._context || {};
69929
69930 var i;
69931
69932 // Create all the storage space for frames, but only if doesn't already exist
69933 if(!gd._transitionData) plots.createTransitionData(gd);
69934
69935 // So we only need to do this once (and since we have gd here)
69936 // get the translated placeholder titles.
69937 // These ones get used as default values so need to be known at supplyDefaults
69938 // others keep their blank defaults but render the placeholder as desired later
69939 // TODO: make these work the same way, only inserting the placeholder text at draw time?
69940 // The challenge is that this has slightly different behavior right now in editable mode:
69941 // using the placeholder as default makes this text permanently (but lightly) visible,
69942 // but explicit '' for these titles gives you a placeholder that's hidden until you mouse
69943 // over it - so you're not distracted by it if you really don't want a title, but if you do
69944 // and you're new to plotly you may not be able to find it.
69945 // When editable=false the two behave the same, no title is drawn.
69946 newFullLayout._dfltTitle = {
69947 plot: _(gd, 'Click to enter Plot title'),
69948 x: _(gd, 'Click to enter X axis title'),
69949 y: _(gd, 'Click to enter Y axis title'),
69950 colorbar: _(gd, 'Click to enter Colorscale title'),
69951 annotation: _(gd, 'new text')
69952 };
69953 newFullLayout._traceWord = _(gd, 'trace');
69954
69955 var formatObj = getFormatObj(gd, d3FormatKeys);
69956
69957 // stash the token from context so mapbox subplots can use it as default
69958 newFullLayout._mapboxAccessToken = context.mapboxAccessToken;
69959
69960 // first fill in what we can of layout without looking at data
69961 // because fullData needs a few things from layout
69962 if(oldFullLayout._initialAutoSizeIsDone) {
69963 // coerce the updated layout while preserving width and height
69964 var oldWidth = oldFullLayout.width;
69965 var oldHeight = oldFullLayout.height;
69966
69967 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
69968
69969 if(!newLayout.width) newFullLayout.width = oldWidth;
69970 if(!newLayout.height) newFullLayout.height = oldHeight;
69971 plots.sanitizeMargins(newFullLayout);
69972 } else {
69973 // coerce the updated layout and autosize if needed
69974 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
69975
69976 var missingWidthOrHeight = (!newLayout.width || !newLayout.height);
69977 var autosize = newFullLayout.autosize;
69978 var autosizable = context.autosizable;
69979 var initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
69980
69981 if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
69982 else if(missingWidthOrHeight) plots.sanitizeMargins(newFullLayout);
69983
69984 // for backwards-compatibility with Plotly v1.x.x
69985 if(!autosize && missingWidthOrHeight) {
69986 newLayout.width = newFullLayout.width;
69987 newLayout.height = newFullLayout.height;
69988 }
69989 }
69990
69991 newFullLayout._d3locale = getFormatter(formatObj, newFullLayout.separators);
69992 newFullLayout._extraFormat = getFormatObj(gd, extraFormatKeys);
69993
69994 newFullLayout._initialAutoSizeIsDone = true;
69995
69996 // keep track of how many traces are inputted
69997 newFullLayout._dataLength = newData.length;
69998
69999 // clear the lists of trace and baseplot modules, and subplots
70000 newFullLayout._modules = [];
70001 newFullLayout._visibleModules = [];
70002 newFullLayout._basePlotModules = [];
70003 var subplots = newFullLayout._subplots = emptySubplotLists();
70004
70005 // initialize axis and subplot hash objects for splom-generated grids
70006 var splomAxes = newFullLayout._splomAxes = {x: {}, y: {}};
70007 var splomSubplots = newFullLayout._splomSubplots = {};
70008 // initialize splom grid defaults
70009 newFullLayout._splomGridDflt = {};
70010
70011 // for stacked area traces to share config across traces
70012 newFullLayout._scatterStackOpts = {};
70013 // for the first scatter trace on each subplot (so it knows tonext->tozero)
70014 newFullLayout._firstScatter = {};
70015 // for grouped bar/box/violin trace to share config across traces
70016 newFullLayout._alignmentOpts = {};
70017 // track color axes referenced in the data
70018 newFullLayout._colorAxes = {};
70019
70020 // for traces to request a default rangeslider on their x axes
70021 // eg set `_requestRangeslider.x2 = true` for xaxis2
70022 newFullLayout._requestRangeslider = {};
70023
70024 // pull uids from old data to use as new defaults
70025 newFullLayout._traceUids = getTraceUids(oldFullData, newData);
70026
70027 // then do the data
70028 newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
70029 plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
70030
70031 // redo grid size defaults with info about splom x/y axes,
70032 // and fill in generated cartesian axes and subplots
70033 var splomXa = Object.keys(splomAxes.x);
70034 var splomYa = Object.keys(splomAxes.y);
70035 if(splomXa.length > 1 && splomYa.length > 1) {
70036 Registry.getComponentMethod('grid', 'sizeDefaults')(newLayout, newFullLayout);
70037
70038 for(i = 0; i < splomXa.length; i++) {
70039 Lib.pushUnique(subplots.xaxis, splomXa[i]);
70040 }
70041 for(i = 0; i < splomYa.length; i++) {
70042 Lib.pushUnique(subplots.yaxis, splomYa[i]);
70043 }
70044 for(var k in splomSubplots) {
70045 Lib.pushUnique(subplots.cartesian, k);
70046 }
70047 }
70048
70049 // attach helper method to check whether a plot type is present on graph
70050 newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
70051
70052 if(oldFullData.length === newFullData.length) {
70053 for(i = 0; i < newFullData.length; i++) {
70054 relinkPrivateKeys(newFullData[i], oldFullData[i]);
70055 }
70056 }
70057
70058 // finally, fill in the pieces of layout that may need to look at data
70059 plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
70060
70061 // Special cases that introduce interactions between traces.
70062 // This is after relinkPrivateKeys so we can use those in crossTraceDefaults
70063 // and after layout module defaults, so we can use eg barmode
70064 var _modules = newFullLayout._visibleModules;
70065 var crossTraceDefaultsFuncs = [];
70066 for(i = 0; i < _modules.length; i++) {
70067 var funci = _modules[i].crossTraceDefaults;
70068 // some trace types share crossTraceDefaults (ie histogram2d, histogram2dcontour)
70069 if(funci) Lib.pushUnique(crossTraceDefaultsFuncs, funci);
70070 }
70071 for(i = 0; i < crossTraceDefaultsFuncs.length; i++) {
70072 crossTraceDefaultsFuncs[i](newFullData, newFullLayout);
70073 }
70074
70075 // turn on flag to optimize large splom-only graphs
70076 // mostly by omitting SVG layers during Cartesian.drawFramework
70077 newFullLayout._hasOnlyLargeSploms = (
70078 newFullLayout._basePlotModules.length === 1 &&
70079 newFullLayout._basePlotModules[0].name === 'splom' &&
70080 splomXa.length > 15 &&
70081 splomYa.length > 15 &&
70082 newFullLayout.shapes.length === 0 &&
70083 newFullLayout.images.length === 0
70084 );
70085
70086 // TODO remove in v2.0.0
70087 // add has-plot-type refs to fullLayout for backward compatibility
70088 newFullLayout._hasCartesian = newFullLayout._has('cartesian');
70089 newFullLayout._hasGeo = newFullLayout._has('geo');
70090 newFullLayout._hasGL3D = newFullLayout._has('gl3d');
70091 newFullLayout._hasGL2D = newFullLayout._has('gl2d');
70092 newFullLayout._hasTernary = newFullLayout._has('ternary');
70093 newFullLayout._hasPie = newFullLayout._has('pie');
70094
70095 // relink / initialize subplot axis objects
70096 plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
70097
70098 // clean subplots and other artifacts from previous plot calls
70099 plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
70100
70101 var hadGL2D = !!(oldFullLayout._has && oldFullLayout._has('gl2d'));
70102 var hasGL2D = !!(newFullLayout._has && newFullLayout._has('gl2d'));
70103 var hadCartesian = !!(oldFullLayout._has && oldFullLayout._has('cartesian'));
70104 var hasCartesian = !!(newFullLayout._has && newFullLayout._has('cartesian'));
70105 var hadBgLayer = hadCartesian || hadGL2D;
70106 var hasBgLayer = hasCartesian || hasGL2D;
70107 if(hadBgLayer && !hasBgLayer) {
70108 // remove bgLayer
70109 oldFullLayout._bgLayer.remove();
70110 } else if(hasBgLayer && !hadBgLayer) {
70111 // create bgLayer
70112 newFullLayout._shouldCreateBgLayer = true;
70113 }
70114
70115 // clear selection outline until we implement persistent selection,
70116 // don't clear them though when drag handlers (e.g. listening to
70117 // `plotly_selecting`) update the graph.
70118 // we should try to come up with a better solution when implementing
70119 // https://github.com/plotly/plotly.js/issues/1851
70120 if(oldFullLayout._zoomlayer && !gd._dragging) {
70121 clearSelect({ // mock old gd
70122 _fullLayout: oldFullLayout
70123 });
70124 }
70125
70126
70127 // fill in meta helpers
70128 fillMetaTextHelpers(newFullData, newFullLayout);
70129
70130 // relink functions and _ attributes to promote consistency between plots
70131 relinkPrivateKeys(newFullLayout, oldFullLayout);
70132
70133 // colorscale crossTraceDefaults needs newFullLayout with relinked keys
70134 Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);
70135
70136 // For persisting GUI-driven changes in layout
70137 // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys
70138 if(!newFullLayout._preGUI) newFullLayout._preGUI = {};
70139 // track trace GUI changes by uid rather than by trace index
70140 if(!newFullLayout._tracePreGUI) newFullLayout._tracePreGUI = {};
70141 var tracePreGUI = newFullLayout._tracePreGUI;
70142 var uids = {};
70143 var uid;
70144 for(uid in tracePreGUI) uids[uid] = 'old';
70145 for(i = 0; i < newFullData.length; i++) {
70146 uid = newFullData[i]._fullInput.uid;
70147 if(!uids[uid]) tracePreGUI[uid] = {};
70148 uids[uid] = 'new';
70149 }
70150 for(uid in uids) {
70151 if(uids[uid] === 'old') delete tracePreGUI[uid];
70152 }
70153
70154 // set up containers for margin calculations
70155 initMargins(newFullLayout);
70156
70157 // collect and do some initial calculations for rangesliders
70158 Registry.getComponentMethod('rangeslider', 'makeData')(newFullLayout);
70159
70160 // update object references in calcdata
70161 if(!skipUpdateCalc && oldCalcdata.length === newFullData.length) {
70162 plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData);
70163 }
70164};
70165
70166plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) {
70167 for(var i = 0; i < newFullData.length; i++) {
70168 var newTrace = newFullData[i];
70169 var cd0 = (oldCalcdata[i] || [])[0];
70170 if(cd0 && cd0.trace) {
70171 var oldTrace = cd0.trace;
70172 if(oldTrace._hasCalcTransform) {
70173 var arrayAttrs = oldTrace._arrayAttrs;
70174 var j, astr, oldArrayVal;
70175
70176 for(j = 0; j < arrayAttrs.length; j++) {
70177 astr = arrayAttrs[j];
70178 oldArrayVal = Lib.nestedProperty(oldTrace, astr).get().slice();
70179 Lib.nestedProperty(newTrace, astr).set(oldArrayVal);
70180 }
70181 }
70182 cd0.trace = newTrace;
70183 }
70184 }
70185};
70186
70187/**
70188 * Create a list of uid strings satisfying (in this order of importance):
70189 * 1. all unique, all strings
70190 * 2. matches input uids if provided
70191 * 3. matches previous data uids
70192 */
70193function getTraceUids(oldFullData, newData) {
70194 var len = newData.length;
70195 var oldFullInput = [];
70196 var i, prevFullInput;
70197 for(i = 0; i < oldFullData.length; i++) {
70198 var thisFullInput = oldFullData[i]._fullInput;
70199 if(thisFullInput !== prevFullInput) oldFullInput.push(thisFullInput);
70200 prevFullInput = thisFullInput;
70201 }
70202 var oldLen = oldFullInput.length;
70203 var out = new Array(len);
70204 var seenUids = {};
70205
70206 function setUid(uid, i) {
70207 out[i] = uid;
70208 seenUids[uid] = 1;
70209 }
70210
70211 function tryUid(uid, i) {
70212 if(uid && typeof uid === 'string' && !seenUids[uid]) {
70213 setUid(uid, i);
70214 return true;
70215 }
70216 }
70217
70218 for(i = 0; i < len; i++) {
70219 var newUid = newData[i].uid;
70220 if(typeof newUid === 'number') newUid = String(newUid);
70221
70222 if(tryUid(newUid, i)) continue;
70223 if(i < oldLen && tryUid(oldFullInput[i].uid, i)) continue;
70224 setUid(Lib.randstr(seenUids), i);
70225 }
70226
70227 return out;
70228}
70229
70230/**
70231 * Make a container for collecting subplots we need to display.
70232 *
70233 * Finds all subplot types we need to enumerate once and caches it,
70234 * but makes a new output object each time.
70235 * Single-trace subplots (which have no `id`) such as pie, table, etc
70236 * do not need to be collected because we just draw all visible traces.
70237 */
70238function emptySubplotLists() {
70239 var collectableSubplotTypes = Registry.collectableSubplotTypes;
70240 var out = {};
70241 var i, j;
70242
70243 if(!collectableSubplotTypes) {
70244 collectableSubplotTypes = [];
70245
70246 var subplotsRegistry = Registry.subplotsRegistry;
70247
70248 for(var subplotType in subplotsRegistry) {
70249 var subplotModule = subplotsRegistry[subplotType];
70250 var subplotAttr = subplotModule.attr;
70251
70252 if(subplotAttr) {
70253 collectableSubplotTypes.push(subplotType);
70254
70255 // special case, currently just for cartesian:
70256 // we need to enumerate axes, not just subplots
70257 if(Array.isArray(subplotAttr)) {
70258 for(j = 0; j < subplotAttr.length; j++) {
70259 Lib.pushUnique(collectableSubplotTypes, subplotAttr[j]);
70260 }
70261 }
70262 }
70263 }
70264 }
70265
70266 for(i = 0; i < collectableSubplotTypes.length; i++) {
70267 out[collectableSubplotTypes[i]] = [];
70268 }
70269 return out;
70270}
70271
70272/**
70273 * getFormatObj: use _context to get the format object from locale.
70274 * Used to get d3.locale argument object and extraFormat argument object
70275 *
70276 * Regarding d3.locale argument :
70277 * decimal and thousands can be overridden later by layout.separators
70278 * grouping and currency are not presently used by our automatic number
70279 * formatting system but can be used by custom formats.
70280 *
70281 * @returns {object} d3.locale format object
70282 */
70283function getFormatObj(gd, formatKeys) {
70284 var locale = gd._context.locale;
70285 if(!locale) locale === 'en-US';
70286
70287 var formatDone = false;
70288 var formatObj = {};
70289
70290 function includeFormat(newFormat) {
70291 var formatFinished = true;
70292 for(var i = 0; i < formatKeys.length; i++) {
70293 var formatKey = formatKeys[i];
70294 if(!formatObj[formatKey]) {
70295 if(newFormat[formatKey]) {
70296 formatObj[formatKey] = newFormat[formatKey];
70297 } else formatFinished = false;
70298 }
70299 }
70300 if(formatFinished) formatDone = true;
70301 }
70302
70303 // same as localize, look for format parts in each format spec in the chain
70304 for(var i = 0; i < 2; i++) {
70305 var locales = gd._context.locales;
70306 for(var j = 0; j < 2; j++) {
70307 var formatj = (locales[locale] || {}).format;
70308 if(formatj) {
70309 includeFormat(formatj);
70310 if(formatDone) break;
70311 }
70312 locales = Registry.localeRegistry;
70313 }
70314
70315 var baseLocale = locale.split('-')[0];
70316 if(formatDone || baseLocale === locale) break;
70317 locale = baseLocale;
70318 }
70319
70320 // lastly pick out defaults from english (non-US, as DMY is so much more common)
70321 if(!formatDone) includeFormat(Registry.localeRegistry.en.format);
70322
70323 return formatObj;
70324}
70325
70326/**
70327 * getFormatter: combine the final separators with the locale formatting object
70328 * we pulled earlier to generate number and time formatters
70329 * TODO: remove separators in v2, only use locale, so we don't need this step?
70330 *
70331 * @param {object} formatObj: d3.locale format object
70332 * @param {string} separators: length-2 string to override decimal and thousands
70333 * separators in number formatting
70334 *
70335 * @returns {object} {numberFormat, timeFormat} d3 formatter factory functions
70336 * for numbers and time
70337 */
70338function getFormatter(formatObj, separators) {
70339 formatObj.decimal = separators.charAt(0);
70340 formatObj.thousands = separators.charAt(1);
70341
70342 return d3.locale(formatObj);
70343}
70344
70345function fillMetaTextHelpers(newFullData, newFullLayout) {
70346 var _meta;
70347 var meta4data = [];
70348
70349 if(newFullLayout.meta) {
70350 _meta = newFullLayout._meta = {
70351 meta: newFullLayout.meta,
70352 layout: {meta: newFullLayout.meta}
70353 };
70354 }
70355
70356 for(var i = 0; i < newFullData.length; i++) {
70357 var trace = newFullData[i];
70358
70359 if(trace.meta) {
70360 meta4data[trace.index] = trace._meta = {meta: trace.meta};
70361 } else if(newFullLayout.meta) {
70362 trace._meta = {meta: newFullLayout.meta};
70363 }
70364 if(newFullLayout.meta) {
70365 trace._meta.layout = {meta: newFullLayout.meta};
70366 }
70367 }
70368
70369 if(meta4data.length) {
70370 if(!_meta) {
70371 _meta = newFullLayout._meta = {};
70372 }
70373 _meta.data = meta4data;
70374 }
70375}
70376
70377// Create storage for all of the data related to frames and transitions:
70378plots.createTransitionData = function(gd) {
70379 // Set up the default keyframe if it doesn't exist:
70380 if(!gd._transitionData) {
70381 gd._transitionData = {};
70382 }
70383
70384 if(!gd._transitionData._frames) {
70385 gd._transitionData._frames = [];
70386 }
70387
70388 if(!gd._transitionData._frameHash) {
70389 gd._transitionData._frameHash = {};
70390 }
70391
70392 if(!gd._transitionData._counter) {
70393 gd._transitionData._counter = 0;
70394 }
70395
70396 if(!gd._transitionData._interruptCallbacks) {
70397 gd._transitionData._interruptCallbacks = [];
70398 }
70399};
70400
70401// helper function to be bound to fullLayout to check
70402// whether a certain plot type is present on plot
70403// or trace has a category
70404plots._hasPlotType = function(category) {
70405 var i;
70406
70407 // check base plot modules
70408 var basePlotModules = this._basePlotModules || [];
70409 for(i = 0; i < basePlotModules.length; i++) {
70410 if(basePlotModules[i].name === category) return true;
70411 }
70412
70413 // check trace modules (including non-visible:true)
70414 var modules = this._modules || [];
70415 for(i = 0; i < modules.length; i++) {
70416 var name = modules[i].name;
70417 if(name === category) return true;
70418 // N.B. this is modules[i] along with 'categories' as a hash object
70419 var _module = Registry.modules[name];
70420 if(_module && _module.categories[category]) return true;
70421 }
70422
70423 return false;
70424};
70425
70426plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
70427 var i, j;
70428
70429 var basePlotModules = oldFullLayout._basePlotModules || [];
70430 for(i = 0; i < basePlotModules.length; i++) {
70431 var _module = basePlotModules[i];
70432
70433 if(_module.clean) {
70434 _module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
70435 }
70436 }
70437
70438 var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
70439 var hasGl = newFullLayout._has && newFullLayout._has('gl');
70440
70441 if(hadGl && !hasGl) {
70442 if(oldFullLayout._glcontainer !== undefined) {
70443 oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
70444 oldFullLayout._glcontainer.selectAll('.no-webgl').remove();
70445 oldFullLayout._glcanvas = null;
70446 }
70447 }
70448
70449 var hasInfoLayer = !!oldFullLayout._infolayer;
70450
70451 oldLoop:
70452 for(i = 0; i < oldFullData.length; i++) {
70453 var oldTrace = oldFullData[i];
70454 var oldUid = oldTrace.uid;
70455
70456 for(j = 0; j < newFullData.length; j++) {
70457 var newTrace = newFullData[j];
70458
70459 if(oldUid === newTrace.uid) continue oldLoop;
70460 }
70461
70462 // clean old colorbars
70463 if(hasInfoLayer) {
70464 oldFullLayout._infolayer.select('.cb' + oldUid).remove();
70465 }
70466 }
70467};
70468
70469plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
70470 var i, j;
70471
70472 var oldSubplots = oldFullLayout._plots || {};
70473 var newSubplots = newFullLayout._plots = {};
70474 var newSubplotList = newFullLayout._subplots;
70475
70476 var mockGd = {
70477 _fullData: newFullData,
70478 _fullLayout: newFullLayout
70479 };
70480
70481 var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []);
70482
70483 for(i = 0; i < ids.length; i++) {
70484 var id = ids[i];
70485 var oldSubplot = oldSubplots[id];
70486 var xaxis = axisIDs.getFromId(mockGd, id, 'x');
70487 var yaxis = axisIDs.getFromId(mockGd, id, 'y');
70488 var plotinfo;
70489
70490 // link or create subplot object
70491 if(oldSubplot) {
70492 plotinfo = newSubplots[id] = oldSubplot;
70493 } else {
70494 plotinfo = newSubplots[id] = {};
70495 plotinfo.id = id;
70496 }
70497
70498 // add these axis ids to each others' subplot lists
70499 xaxis._counterAxes.push(yaxis._id);
70500 yaxis._counterAxes.push(xaxis._id);
70501 xaxis._subplotsWith.push(id);
70502 yaxis._subplotsWith.push(id);
70503
70504 // update x and y axis layout object refs
70505 plotinfo.xaxis = xaxis;
70506 plotinfo.yaxis = yaxis;
70507
70508 // By default, we clip at the subplot level,
70509 // but if one trace on a given subplot has *cliponaxis* set to false,
70510 // we need to clip at the trace module layer level;
70511 // find this out here, once of for all.
70512 plotinfo._hasClipOnAxisFalse = false;
70513
70514 for(j = 0; j < newFullData.length; j++) {
70515 var trace = newFullData[j];
70516
70517 if(
70518 trace.xaxis === plotinfo.xaxis._id &&
70519 trace.yaxis === plotinfo.yaxis._id &&
70520 trace.cliponaxis === false
70521 ) {
70522 plotinfo._hasClipOnAxisFalse = true;
70523 break;
70524 }
70525 }
70526 }
70527
70528 // while we're at it, link overlaying axes to their main axes and
70529 // anchored axes to the axes they're anchored to
70530 var axList = axisIDs.list(mockGd, null, true);
70531 var ax;
70532 for(i = 0; i < axList.length; i++) {
70533 ax = axList[i];
70534 var mainAx = null;
70535
70536 if(ax.overlaying) {
70537 mainAx = axisIDs.getFromId(mockGd, ax.overlaying);
70538
70539 // you cannot overlay an axis that's already overlaying another
70540 if(mainAx && mainAx.overlaying) {
70541 ax.overlaying = false;
70542 mainAx = null;
70543 }
70544 }
70545 ax._mainAxis = mainAx || ax;
70546
70547 /*
70548 * For now force overlays to overlay completely... so they
70549 * can drag together correctly and share backgrounds.
70550 * Later perhaps we make separate axis domain and
70551 * tick/line domain or something, so they can still share
70552 * the (possibly larger) dragger and background but don't
70553 * have to both be drawn over that whole domain
70554 */
70555 if(mainAx) ax.domain = mainAx.domain.slice();
70556
70557 ax._anchorAxis = ax.anchor === 'free' ?
70558 null :
70559 axisIDs.getFromId(mockGd, ax.anchor);
70560 }
70561
70562 // finally, we can find the main subplot for each axis
70563 // (on which the ticks & labels are drawn)
70564 for(i = 0; i < axList.length; i++) {
70565 ax = axList[i];
70566 ax._counterAxes.sort(axisIDs.idSort);
70567 ax._subplotsWith.sort(Lib.subplotSort);
70568 ax._mainSubplot = findMainSubplot(ax, newFullLayout);
70569
70570 // find "full" domain span of counter axes,
70571 // this loop can be costly, so only compute it when required
70572 if(ax._counterAxes.length && (
70573 (ax.spikemode && ax.spikemode.indexOf('across') !== -1) ||
70574 (ax.automargin && ax.mirror && ax.anchor !== 'free') ||
70575 Registry.getComponentMethod('rangeslider', 'isVisible')(ax)
70576 )) {
70577 var min = 1;
70578 var max = 0;
70579 for(j = 0; j < ax._counterAxes.length; j++) {
70580 var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]);
70581 min = Math.min(min, ax2.domain[0]);
70582 max = Math.max(max, ax2.domain[1]);
70583 }
70584 if(min < max) {
70585 ax._counterDomainMin = min;
70586 ax._counterDomainMax = max;
70587 }
70588 }
70589 }
70590};
70591
70592function findMainSubplot(ax, fullLayout) {
70593 var mockGd = {_fullLayout: fullLayout};
70594
70595 var isX = ax._id.charAt(0) === 'x';
70596 var anchorAx = ax._mainAxis._anchorAxis;
70597 var mainSubplotID = '';
70598 var nextBestMainSubplotID = '';
70599 var anchorID = '';
70600
70601 // First try the main ID with the anchor
70602 if(anchorAx) {
70603 anchorID = anchorAx._mainAxis._id;
70604 mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id);
70605 }
70606
70607 // Then look for a subplot with the counteraxis overlaying the anchor
70608 // If that fails just use the first subplot including this axis
70609 if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) {
70610 mainSubplotID = '';
70611
70612 var counterIDs = ax._counterAxes;
70613 for(var j = 0; j < counterIDs.length; j++) {
70614 var counterPart = counterIDs[j];
70615 var id = isX ? (ax._id + counterPart) : (counterPart + ax._id);
70616 if(!nextBestMainSubplotID) nextBestMainSubplotID = id;
70617 var counterAx = axisIDs.getFromId(mockGd, counterPart);
70618 if(anchorID && counterAx.overlaying === anchorID) {
70619 mainSubplotID = id;
70620 break;
70621 }
70622 }
70623 }
70624
70625 return mainSubplotID || nextBestMainSubplotID;
70626}
70627
70628// This function clears any trace attributes with valType: color and
70629// no set dflt filed in the plot schema. This is needed because groupby (which
70630// is the only transform for which this currently applies) supplies parent
70631// trace defaults, then expanded trace defaults. The result is that `null`
70632// colors are default-supplied and inherited as a color instead of a null.
70633// The result is that expanded trace default colors have no effect, with
70634// the final result that groups are indistinguishable. This function clears
70635// those colors so that individual groupby groups get unique colors.
70636plots.clearExpandedTraceDefaultColors = function(trace) {
70637 var colorAttrs, path, i;
70638
70639 // This uses weird closure state in order to satisfy the linter rule
70640 // that we can't create functions in a loop.
70641 function locateColorAttrs(attr, attrName, attrs, level) {
70642 path[level] = attrName;
70643 path.length = level + 1;
70644 if(attr.valType === 'color' && attr.dflt === undefined) {
70645 colorAttrs.push(path.join('.'));
70646 }
70647 }
70648
70649 path = [];
70650
70651 // Get the cached colorAttrs:
70652 colorAttrs = trace._module._colorAttrs;
70653
70654 // Or else compute and cache the colorAttrs on the module:
70655 if(!colorAttrs) {
70656 trace._module._colorAttrs = colorAttrs = [];
70657 PlotSchema.crawl(
70658 trace._module.attributes,
70659 locateColorAttrs
70660 );
70661 }
70662
70663 for(i = 0; i < colorAttrs.length; i++) {
70664 var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]);
70665
70666 if(!origprop.get()) {
70667 Lib.nestedProperty(trace, colorAttrs[i]).set(null);
70668 }
70669 }
70670};
70671
70672
70673plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
70674 var modules = fullLayout._modules;
70675 var visibleModules = fullLayout._visibleModules;
70676 var basePlotModules = fullLayout._basePlotModules;
70677 var cnt = 0;
70678 var colorCnt = 0;
70679
70680 var i, fullTrace, trace;
70681
70682 fullLayout._transformModules = [];
70683
70684 function pushModule(fullTrace) {
70685 dataOut.push(fullTrace);
70686
70687 var _module = fullTrace._module;
70688 if(!_module) return;
70689
70690 Lib.pushUnique(modules, _module);
70691 if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module);
70692 Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
70693 cnt++;
70694
70695 // TODO: do we really want color not to increment for explicitly invisible traces?
70696 // This logic is weird, but matches previous behavior: traces that you explicitly
70697 // set to visible:false do not increment the color, but traces WE determine to be
70698 // empty or invalid (and thus set to visible:false) DO increment color.
70699 // I kind of think we should just let all traces increment color, visible or not.
70700 // see mock: axes-autotype-empty vs. a test of restyling visible: false that
70701 // I can't find right now...
70702 if(fullTrace._input.visible !== false) colorCnt++;
70703 }
70704
70705 var carpetIndex = {};
70706 var carpetDependents = [];
70707 var dataTemplate = (layout.template || {}).data || {};
70708 var templater = Template.traceTemplater(dataTemplate);
70709
70710 for(i = 0; i < dataIn.length; i++) {
70711 trace = dataIn[i];
70712
70713 // reuse uid we may have pulled out of oldFullData
70714 // Note: templater supplies trace type
70715 fullTrace = templater.newTrace(trace);
70716 fullTrace.uid = fullLayout._traceUids[i];
70717 plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);
70718
70719 fullTrace.index = i;
70720 fullTrace._input = trace;
70721 fullTrace._expandedIndex = cnt;
70722
70723 if(fullTrace.transforms && fullTrace.transforms.length) {
70724 var sdInvisible = trace.visible !== false && fullTrace.visible === false;
70725
70726 var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
70727
70728 for(var j = 0; j < expandedTraces.length; j++) {
70729 var expandedTrace = expandedTraces[j];
70730
70731 // No further templating during transforms.
70732 var fullExpandedTrace = {
70733 _template: fullTrace._template,
70734 type: fullTrace.type,
70735 // set uid using parent uid and expanded index
70736 // to promote consistency between update calls
70737 uid: fullTrace.uid + j
70738 };
70739
70740 // If the first supplyDefaults created `visible: false`,
70741 // clear it before running supplyDefaults a second time,
70742 // because sometimes there are items we still want to coerce
70743 // inside trace modules before determining that the trace is
70744 // again `visible: false`, for example partial visibilities
70745 // in `splom` traces.
70746 if(sdInvisible && expandedTrace.visible === false) {
70747 delete expandedTrace.visible;
70748 }
70749
70750 plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i);
70751
70752 // relink private (i.e. underscore) keys expanded trace to full expanded trace so
70753 // that transform supply-default methods can set _ keys for future use.
70754 relinkPrivateKeys(fullExpandedTrace, expandedTrace);
70755
70756 // add info about parent data trace
70757 fullExpandedTrace.index = i;
70758 fullExpandedTrace._input = trace;
70759 fullExpandedTrace._fullInput = fullTrace;
70760
70761 // add info about the expanded data
70762 fullExpandedTrace._expandedIndex = cnt;
70763 fullExpandedTrace._expandedInput = expandedTrace;
70764
70765 pushModule(fullExpandedTrace);
70766 }
70767 } else {
70768 // add identify refs for consistency with transformed traces
70769 fullTrace._fullInput = fullTrace;
70770 fullTrace._expandedInput = fullTrace;
70771
70772 pushModule(fullTrace);
70773 }
70774
70775 if(Registry.traceIs(fullTrace, 'carpetAxis')) {
70776 carpetIndex[fullTrace.carpet] = fullTrace;
70777 }
70778
70779 if(Registry.traceIs(fullTrace, 'carpetDependent')) {
70780 carpetDependents.push(i);
70781 }
70782 }
70783
70784 for(i = 0; i < carpetDependents.length; i++) {
70785 fullTrace = dataOut[carpetDependents[i]];
70786
70787 if(!fullTrace.visible) continue;
70788
70789 var carpetAxis = carpetIndex[fullTrace.carpet];
70790 fullTrace._carpet = carpetAxis;
70791
70792 if(!carpetAxis || !carpetAxis.visible) {
70793 fullTrace.visible = false;
70794 continue;
70795 }
70796
70797 fullTrace.xaxis = carpetAxis.xaxis;
70798 fullTrace.yaxis = carpetAxis.yaxis;
70799 }
70800};
70801
70802plots.supplyAnimationDefaults = function(opts) {
70803 opts = opts || {};
70804 var i;
70805 var optsOut = {};
70806
70807 function coerce(attr, dflt) {
70808 return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt);
70809 }
70810
70811 coerce('mode');
70812 coerce('direction');
70813 coerce('fromcurrent');
70814
70815 if(Array.isArray(opts.frame)) {
70816 optsOut.frame = [];
70817 for(i = 0; i < opts.frame.length; i++) {
70818 optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
70819 }
70820 } else {
70821 optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
70822 }
70823
70824 if(Array.isArray(opts.transition)) {
70825 optsOut.transition = [];
70826 for(i = 0; i < opts.transition.length; i++) {
70827 optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {});
70828 }
70829 } else {
70830 optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
70831 }
70832
70833 return optsOut;
70834};
70835
70836plots.supplyAnimationFrameDefaults = function(opts) {
70837 var optsOut = {};
70838
70839 function coerce(attr, dflt) {
70840 return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt);
70841 }
70842
70843 coerce('duration');
70844 coerce('redraw');
70845
70846 return optsOut;
70847};
70848
70849plots.supplyAnimationTransitionDefaults = function(opts) {
70850 var optsOut = {};
70851
70852 function coerce(attr, dflt) {
70853 return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt);
70854 }
70855
70856 coerce('duration');
70857 coerce('easing');
70858
70859 return optsOut;
70860};
70861
70862plots.supplyFrameDefaults = function(frameIn) {
70863 var frameOut = {};
70864
70865 function coerce(attr, dflt) {
70866 return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt);
70867 }
70868
70869 coerce('group');
70870 coerce('name');
70871 coerce('traces');
70872 coerce('baseframe');
70873 coerce('data');
70874 coerce('layout');
70875
70876 return frameOut;
70877};
70878
70879plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) {
70880 var colorway = layout.colorway || Color.defaults;
70881 var defaultColor = colorway[colorIndex % colorway.length];
70882
70883 var i;
70884
70885 function coerce(attr, dflt) {
70886 return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt);
70887 }
70888
70889 var visible = coerce('visible');
70890
70891 coerce('type');
70892 coerce('name', layout._traceWord + ' ' + traceInIndex);
70893
70894 coerce('uirevision', layout.uirevision);
70895
70896 // we want even invisible traces to make their would-be subplots visible
70897 // so coerce the subplot id(s) now no matter what
70898 var _module = plots.getModule(traceOut);
70899
70900 traceOut._module = _module;
70901 if(_module) {
70902 var basePlotModule = _module.basePlotModule;
70903 var subplotAttr = basePlotModule.attr;
70904 var subplotAttrs = basePlotModule.attributes;
70905 if(subplotAttr && subplotAttrs) {
70906 var subplots = layout._subplots;
70907 var subplotId = '';
70908
70909 if(
70910 visible ||
70911 basePlotModule.name !== 'gl2d' // for now just drop empty gl2d subplots
70912 // TODO - currently if we draw an empty gl2d subplot, it draws
70913 // nothing then gets stuck and you can't get it back without newPlot
70914 // sort this out in the regl refactor?
70915 ) {
70916 if(Array.isArray(subplotAttr)) {
70917 for(i = 0; i < subplotAttr.length; i++) {
70918 var attri = subplotAttr[i];
70919 var vali = Lib.coerce(traceIn, traceOut, subplotAttrs, attri);
70920
70921 if(subplots[attri]) Lib.pushUnique(subplots[attri], vali);
70922 subplotId += vali;
70923 }
70924 } else {
70925 subplotId = Lib.coerce(traceIn, traceOut, subplotAttrs, subplotAttr);
70926 }
70927
70928 if(subplots[basePlotModule.name]) {
70929 Lib.pushUnique(subplots[basePlotModule.name], subplotId);
70930 }
70931 }
70932 }
70933 }
70934
70935 if(visible) {
70936 coerce('customdata');
70937 coerce('ids');
70938 coerce('meta');
70939
70940 if(Registry.traceIs(traceOut, 'showLegend')) {
70941 Lib.coerce(traceIn, traceOut,
70942 _module.attributes.showlegend ? _module.attributes : plots.attributes,
70943 'showlegend'
70944 );
70945
70946 coerce('legendgroup');
70947
70948 traceOut._dfltShowLegend = true;
70949 } else {
70950 traceOut._dfltShowLegend = false;
70951 }
70952
70953 if(_module) {
70954 _module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
70955 }
70956
70957 if(!Registry.traceIs(traceOut, 'noOpacity')) {
70958 coerce('opacity');
70959 }
70960
70961 if(Registry.traceIs(traceOut, 'notLegendIsolatable')) {
70962 // This clears out the legendonly state for traces like carpet that
70963 // cannot be isolated in the legend
70964 traceOut.visible = !!traceOut.visible;
70965 }
70966
70967 if(!Registry.traceIs(traceOut, 'noHover')) {
70968 if(!traceOut.hovertemplate) Lib.coerceHoverinfo(traceIn, traceOut, layout);
70969
70970 // parcats support hover, but not hoverlabel stylings (yet)
70971 if(traceOut.type !== 'parcats') {
70972 Registry.getComponentMethod('fx', 'supplyDefaults')(traceIn, traceOut, defaultColor, layout);
70973 }
70974 }
70975
70976 if(_module && _module.selectPoints) {
70977 coerce('selectedpoints');
70978 }
70979
70980 plots.supplyTransformDefaults(traceIn, traceOut, layout);
70981 }
70982
70983 return traceOut;
70984};
70985
70986/**
70987 * hasMakesDataTransform: does this trace have a transform that makes its own
70988 * data, either by grabbing it from somewhere else or by creating it from input
70989 * parameters? If so, we should still keep going with supplyDefaults
70990 * even if the trace is invisible, which may just be because it has no data yet.
70991 */
70992function hasMakesDataTransform(trace) {
70993 var transforms = trace.transforms;
70994 if(Array.isArray(transforms) && transforms.length) {
70995 for(var i = 0; i < transforms.length; i++) {
70996 var ti = transforms[i];
70997 var _module = ti._module || transformsRegistry[ti.type];
70998 if(_module && _module.makesData) return true;
70999 }
71000 }
71001 return false;
71002}
71003
71004plots.hasMakesDataTransform = hasMakesDataTransform;
71005
71006plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
71007 // For now we only allow transforms on 1D traces, ie those that specify a _length.
71008 // If we were to implement 2D transforms, we'd need to have each transform
71009 // describe its own applicability and disable itself when it doesn't apply.
71010 // Also allow transforms that make their own data, but not in globalTransforms
71011 if(!(traceOut._length || hasMakesDataTransform(traceIn))) return;
71012
71013 var globalTransforms = layout._globalTransforms || [];
71014 var transformModules = layout._transformModules || [];
71015
71016 if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
71017
71018 var containerIn = traceIn.transforms || [];
71019 var transformList = globalTransforms.concat(containerIn);
71020 var containerOut = traceOut.transforms = [];
71021
71022 for(var i = 0; i < transformList.length; i++) {
71023 var transformIn = transformList[i];
71024 var type = transformIn.type;
71025 var _module = transformsRegistry[type];
71026 var transformOut;
71027
71028 /*
71029 * Supply defaults may run twice. First pass runs all supply defaults steps
71030 * and adds the _module to any output transforms.
71031 * If transforms exist another pass is run so that any generated traces also
71032 * go through supply defaults. This has the effect of rerunning
71033 * supplyTransformDefaults. If the transform does not have a `transform`
71034 * function it could not have generated any new traces and the second stage
71035 * is unnecessary. We detect this case with the following variables.
71036 */
71037 var isFirstStage = !(transformIn._module && transformIn._module === _module);
71038 var doLaterStages = _module && typeof _module.transform === 'function';
71039
71040 if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
71041
71042 if(_module && _module.supplyDefaults && (isFirstStage || doLaterStages)) {
71043 transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
71044 transformOut.type = type;
71045 transformOut._module = _module;
71046
71047 Lib.pushUnique(transformModules, _module);
71048 } else {
71049 transformOut = Lib.extendFlat({}, transformIn);
71050 }
71051
71052 containerOut.push(transformOut);
71053 }
71054};
71055
71056function applyTransforms(fullTrace, fullData, layout, fullLayout) {
71057 var container = fullTrace.transforms;
71058 var dataOut = [fullTrace];
71059
71060 for(var i = 0; i < container.length; i++) {
71061 var transform = container[i];
71062 var _module = transformsRegistry[transform.type];
71063
71064 if(_module && _module.transform) {
71065 dataOut = _module.transform(dataOut, {
71066 transform: transform,
71067 fullTrace: fullTrace,
71068 fullData: fullData,
71069 layout: layout,
71070 fullLayout: fullLayout,
71071 transformIndex: i
71072 });
71073 }
71074 }
71075
71076 return dataOut;
71077}
71078
71079plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
71080 function coerce(attr, dflt) {
71081 return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
71082 }
71083
71084 var template = layoutIn.template;
71085 if(Lib.isPlainObject(template)) {
71086 layoutOut.template = template;
71087 layoutOut._template = template.layout;
71088 layoutOut._dataTemplate = template.data;
71089 }
71090
71091 var globalFont = Lib.coerceFont(coerce, 'font');
71092
71093 coerce('title.text', layoutOut._dfltTitle.plot);
71094
71095 Lib.coerceFont(coerce, 'title.font', {
71096 family: globalFont.family,
71097 size: Math.round(globalFont.size * 1.4),
71098 color: globalFont.color
71099 });
71100
71101 coerce('title.xref');
71102 coerce('title.yref');
71103 coerce('title.x');
71104 coerce('title.y');
71105 coerce('title.xanchor');
71106 coerce('title.yanchor');
71107 coerce('title.pad.t');
71108 coerce('title.pad.r');
71109 coerce('title.pad.b');
71110 coerce('title.pad.l');
71111
71112 var uniformtextMode = coerce('uniformtext.mode');
71113 if(uniformtextMode) {
71114 coerce('uniformtext.minsize');
71115 }
71116
71117 // Make sure that autosize is defaulted to *true*
71118 // on layouts with no set width and height for backward compatibly,
71119 // in particular https://plotly.com/javascript/responsive-fluid-layout/
71120 //
71121 // Before https://github.com/plotly/plotly.js/pull/635 ,
71122 // layouts with no set width and height were set temporary set to 'initial'
71123 // to pass through the autosize routine
71124 //
71125 // This behavior is subject to change in v2.
71126 coerce('autosize', !(layoutIn.width && layoutIn.height));
71127
71128 coerce('width');
71129 coerce('height');
71130 coerce('margin.l');
71131 coerce('margin.r');
71132 coerce('margin.t');
71133 coerce('margin.b');
71134 coerce('margin.pad');
71135 coerce('margin.autoexpand');
71136
71137 if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut);
71138
71139 Registry.getComponentMethod('grid', 'sizeDefaults')(layoutIn, layoutOut);
71140
71141 coerce('paper_bgcolor');
71142
71143 coerce('separators', formatObj.decimal + formatObj.thousands);
71144 coerce('hidesources');
71145
71146 coerce('colorway');
71147
71148 coerce('datarevision');
71149 var uirevision = coerce('uirevision');
71150 coerce('editrevision', uirevision);
71151 coerce('selectionrevision', uirevision);
71152
71153 coerce('modebar.orientation');
71154 coerce('modebar.bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5));
71155 var modebarDefaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor));
71156 coerce('modebar.color', Color.addOpacity(modebarDefaultColor, 0.3));
71157 coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7));
71158 coerce('modebar.uirevision', uirevision);
71159
71160 Registry.getComponentMethod(
71161 'shapes',
71162 'supplyDrawNewShapeDefaults'
71163 )(layoutIn, layoutOut, coerce);
71164
71165 coerce('meta');
71166
71167 // do not include defaults in fullLayout when users do not set transition
71168 if(Lib.isPlainObject(layoutIn.transition)) {
71169 coerce('transition.duration');
71170 coerce('transition.easing');
71171 coerce('transition.ordering');
71172 }
71173
71174 Registry.getComponentMethod(
71175 'calendars',
71176 'handleDefaults'
71177 )(layoutIn, layoutOut, 'calendar');
71178
71179 Registry.getComponentMethod(
71180 'fx',
71181 'supplyLayoutGlobalDefaults'
71182 )(layoutIn, layoutOut, coerce);
71183};
71184
71185plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) {
71186 var context = gd._context || {};
71187 var frameMargins = context.frameMargins;
71188 var newWidth;
71189 var newHeight;
71190
71191 var isPlotDiv = Lib.isPlotDiv(gd);
71192
71193 if(isPlotDiv) gd.emit('plotly_autosize');
71194
71195 // embedded in an iframe - just take the full iframe size
71196 // if we get to this point, with no aspect ratio restrictions
71197 if(context.fillFrame) {
71198 newWidth = window.innerWidth;
71199 newHeight = window.innerHeight;
71200
71201 // somehow we get a few extra px height sometimes...
71202 // just hide it
71203 document.body.style.overflow = 'hidden';
71204 } else {
71205 // plotly.js - let the developers do what they want, either
71206 // provide height and width for the container div,
71207 // specify size in layout, or take the defaults,
71208 // but don't enforce any ratio restrictions
71209 var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {};
71210
71211 newWidth = parseFloat(computedStyle.width) || parseFloat(computedStyle.maxWidth) || fullLayout.width;
71212 newHeight = parseFloat(computedStyle.height) || parseFloat(computedStyle.maxHeight) || fullLayout.height;
71213
71214 if(isNumeric(frameMargins) && frameMargins > 0) {
71215 var factor = 1 - 2 * frameMargins;
71216 newWidth = Math.round(factor * newWidth);
71217 newHeight = Math.round(factor * newHeight);
71218 }
71219 }
71220
71221 var minWidth = plots.layoutAttributes.width.min;
71222 var minHeight = plots.layoutAttributes.height.min;
71223 if(newWidth < minWidth) newWidth = minWidth;
71224 if(newHeight < minHeight) newHeight = minHeight;
71225
71226 var widthHasChanged = !layout.width &&
71227 (Math.abs(fullLayout.width - newWidth) > 1);
71228 var heightHasChanged = !layout.height &&
71229 (Math.abs(fullLayout.height - newHeight) > 1);
71230
71231 if(heightHasChanged || widthHasChanged) {
71232 if(widthHasChanged) fullLayout.width = newWidth;
71233 if(heightHasChanged) fullLayout.height = newHeight;
71234 }
71235
71236 // cache initial autosize value, used in relayout when
71237 // width or height values are set to null
71238 if(!gd._initialAutoSize) {
71239 gd._initialAutoSize = { width: newWidth, height: newHeight };
71240 }
71241
71242 plots.sanitizeMargins(fullLayout);
71243};
71244
71245plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
71246 var componentsRegistry = Registry.componentsRegistry;
71247 var basePlotModules = layoutOut._basePlotModules;
71248 var component, i, _module;
71249
71250 var Cartesian = Registry.subplotsRegistry.cartesian;
71251
71252 // check if any components need to add more base plot modules
71253 // that weren't captured by traces
71254 for(component in componentsRegistry) {
71255 _module = componentsRegistry[component];
71256
71257 if(_module.includeBasePlot) {
71258 _module.includeBasePlot(layoutIn, layoutOut);
71259 }
71260 }
71261
71262 // make sure we *at least* have some cartesian axes
71263 if(!basePlotModules.length) {
71264 basePlotModules.push(Cartesian);
71265 }
71266
71267 // ensure all cartesian axes have at least one subplot
71268 if(layoutOut._has('cartesian')) {
71269 Registry.getComponentMethod('grid', 'contentDefaults')(layoutIn, layoutOut);
71270 Cartesian.finalizeSubplots(layoutIn, layoutOut);
71271 }
71272
71273 // sort subplot lists
71274 for(var subplotType in layoutOut._subplots) {
71275 layoutOut._subplots[subplotType].sort(Lib.subplotSort);
71276 }
71277
71278 // base plot module layout defaults
71279 for(i = 0; i < basePlotModules.length; i++) {
71280 _module = basePlotModules[i];
71281
71282 // e.g. pie does not have a layout-defaults step
71283 if(_module.supplyLayoutDefaults) {
71284 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
71285 }
71286 }
71287
71288 // trace module layout defaults
71289 // use _modules rather than _visibleModules so that even
71290 // legendonly traces can include settings - eg barmode, which affects
71291 // legend.traceorder default value.
71292 var modules = layoutOut._modules;
71293 for(i = 0; i < modules.length; i++) {
71294 _module = modules[i];
71295
71296 if(_module.supplyLayoutDefaults) {
71297 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
71298 }
71299 }
71300
71301 // transform module layout defaults
71302 var transformModules = layoutOut._transformModules;
71303 for(i = 0; i < transformModules.length; i++) {
71304 _module = transformModules[i];
71305
71306 if(_module.supplyLayoutDefaults) {
71307 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData);
71308 }
71309 }
71310
71311 for(component in componentsRegistry) {
71312 _module = componentsRegistry[component];
71313
71314 if(_module.supplyLayoutDefaults) {
71315 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
71316 }
71317 }
71318};
71319
71320// Remove all plotly attributes from a div so it can be replotted fresh
71321// TODO: these really need to be encapsulated into a much smaller set...
71322plots.purge = function(gd) {
71323 // note: we DO NOT remove _context because it doesn't change when we insert
71324 // a new plot, and may have been set outside of our scope.
71325
71326 var fullLayout = gd._fullLayout || {};
71327 if(fullLayout._glcontainer !== undefined) {
71328 fullLayout._glcontainer.selectAll('.gl-canvas').remove();
71329 fullLayout._glcontainer.remove();
71330 fullLayout._glcanvas = null;
71331 }
71332
71333 // remove modebar
71334 if(fullLayout._modeBar) fullLayout._modeBar.destroy();
71335
71336 if(gd._transitionData) {
71337 // Ensure any dangling callbacks are simply dropped if the plot is purged.
71338 // This is more or less only actually important for testing.
71339 if(gd._transitionData._interruptCallbacks) {
71340 gd._transitionData._interruptCallbacks.length = 0;
71341 }
71342
71343 if(gd._transitionData._animationRaf) {
71344 window.cancelAnimationFrame(gd._transitionData._animationRaf);
71345 }
71346 }
71347
71348 // remove any planned throttles
71349 Lib.clearThrottle();
71350
71351 // remove responsive handler
71352 Lib.clearResponsive(gd);
71353
71354 // data and layout
71355 delete gd.data;
71356 delete gd.layout;
71357 delete gd._fullData;
71358 delete gd._fullLayout;
71359 delete gd.calcdata;
71360 delete gd.framework;
71361 delete gd.empty;
71362
71363 delete gd.fid;
71364
71365 delete gd.undoqueue; // action queue
71366 delete gd.undonum;
71367 delete gd.autoplay; // are we doing an action that doesn't go in undo queue?
71368 delete gd.changed;
71369
71370 // these get recreated on Plotly.plot anyway, but just to be safe
71371 // (and to have a record of them...)
71372 delete gd._promises;
71373 delete gd._redrawTimer;
71374 delete gd._hmlumcount;
71375 delete gd._hmpixcount;
71376 delete gd._transitionData;
71377 delete gd._transitioning;
71378 delete gd._initialAutoSize;
71379 delete gd._transitioningWithDuration;
71380
71381 // created during certain events, that *should* clean them up
71382 // themselves, but may not if there was an error
71383 delete gd._dragging;
71384 delete gd._dragged;
71385 delete gd._dragdata;
71386 delete gd._hoverdata;
71387 delete gd._snapshotInProgress;
71388 delete gd._editing;
71389 delete gd._mouseDownTime;
71390 delete gd._legendMouseDownTime;
71391
71392 // remove all event listeners
71393 if(gd.removeAllListeners) gd.removeAllListeners();
71394};
71395
71396plots.style = function(gd) {
71397 var _modules = gd._fullLayout._visibleModules;
71398 var styleModules = [];
71399 var i;
71400
71401 // some trace modules reuse the same style method,
71402 // make sure to not unnecessary call them multiple times.
71403
71404 for(i = 0; i < _modules.length; i++) {
71405 var _module = _modules[i];
71406 if(_module.style) {
71407 Lib.pushUnique(styleModules, _module.style);
71408 }
71409 }
71410
71411 for(i = 0; i < styleModules.length; i++) {
71412 styleModules[i](gd);
71413 }
71414};
71415
71416plots.sanitizeMargins = function(fullLayout) {
71417 // polar doesn't do margins...
71418 if(!fullLayout || !fullLayout.margin) return;
71419
71420 var width = fullLayout.width;
71421 var height = fullLayout.height;
71422 var margin = fullLayout.margin;
71423 var plotWidth = width - (margin.l + margin.r);
71424 var plotHeight = height - (margin.t + margin.b);
71425 var correction;
71426
71427 // if margin.l + margin.r = 0 then plotWidth > 0
71428 // as width >= 10 by supplyDefaults
71429 // similarly for margin.t + margin.b
71430
71431 if(plotWidth < 0) {
71432 correction = (width - 1) / (margin.l + margin.r);
71433 margin.l = Math.floor(correction * margin.l);
71434 margin.r = Math.floor(correction * margin.r);
71435 }
71436
71437 if(plotHeight < 0) {
71438 correction = (height - 1) / (margin.t + margin.b);
71439 margin.t = Math.floor(correction * margin.t);
71440 margin.b = Math.floor(correction * margin.b);
71441 }
71442};
71443
71444plots.clearAutoMarginIds = function(gd) {
71445 gd._fullLayout._pushmarginIds = {};
71446};
71447
71448plots.allowAutoMargin = function(gd, id) {
71449 gd._fullLayout._pushmarginIds[id] = 1;
71450};
71451
71452function initMargins(fullLayout) {
71453 var margin = fullLayout.margin;
71454
71455 if(!fullLayout._size) {
71456 var gs = fullLayout._size = {
71457 l: Math.round(margin.l),
71458 r: Math.round(margin.r),
71459 t: Math.round(margin.t),
71460 b: Math.round(margin.b),
71461 p: Math.round(margin.pad)
71462 };
71463 gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
71464 gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
71465 }
71466 if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
71467 if(!fullLayout._pushmarginIds) fullLayout._pushmarginIds = {};
71468}
71469
71470/**
71471 * autoMargin: called by components that may need to expand the margins to
71472 * be rendered on-plot.
71473 *
71474 * @param {DOM element} gd
71475 * @param {string} id - an identifier unique (within this plot) to this object,
71476 * so we can remove a previous margin expansion from the same object.
71477 * @param {object} o - the margin requirements of this object, or omit to delete
71478 * this entry (like if it's hidden). Keys are:
71479 * x, y: plot fraction of the anchor point.
71480 * xl, xr, yt, yb: if the object has an extent defined in plot fraction,
71481 * you can specify both edges as plot fractions in each dimension
71482 * l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b]
71483 * pad: extra pixels to add in all directions, default 12 (why?)
71484 */
71485plots.autoMargin = function(gd, id, o) {
71486 var fullLayout = gd._fullLayout;
71487
71488 var pushMargin = fullLayout._pushmargin;
71489 var pushMarginIds = fullLayout._pushmarginIds;
71490
71491 if(fullLayout.margin.autoexpand !== false) {
71492 if(!o) {
71493 delete pushMargin[id];
71494 delete pushMarginIds[id];
71495 } else {
71496 var pad = o.pad;
71497 if(pad === undefined) {
71498 var margin = fullLayout.margin;
71499 // if no explicit pad is given, use 12px unless there's a
71500 // specified margin that's smaller than that
71501 pad = Math.min(12, margin.l, margin.r, margin.t, margin.b);
71502 }
71503
71504 // if the item is too big, just give it enough automargin to
71505 // make sure you can still grab it and bring it back
71506 if(o.l + o.r > fullLayout.width * 0.5) {
71507 Lib.log('Margin push', id, 'is too big in x, dropping');
71508 o.l = o.r = 0;
71509 }
71510 if(o.b + o.t > fullLayout.height * 0.5) {
71511 Lib.log('Margin push', id, 'is too big in y, dropping');
71512 o.b = o.t = 0;
71513 }
71514
71515 var xl = o.xl !== undefined ? o.xl : o.x;
71516 var xr = o.xr !== undefined ? o.xr : o.x;
71517 var yt = o.yt !== undefined ? o.yt : o.y;
71518 var yb = o.yb !== undefined ? o.yb : o.y;
71519
71520 pushMargin[id] = {
71521 l: {val: xl, size: o.l + pad},
71522 r: {val: xr, size: o.r + pad},
71523 b: {val: yb, size: o.b + pad},
71524 t: {val: yt, size: o.t + pad}
71525 };
71526 pushMarginIds[id] = 1;
71527 }
71528
71529 if(!fullLayout._replotting) {
71530 return plots.doAutoMargin(gd);
71531 }
71532 }
71533};
71534
71535plots.doAutoMargin = function(gd) {
71536 var fullLayout = gd._fullLayout;
71537 if(!fullLayout._size) fullLayout._size = {};
71538 initMargins(fullLayout);
71539
71540 var gs = fullLayout._size;
71541 var margin = fullLayout.margin;
71542 var oldMargins = Lib.extendFlat({}, gs);
71543
71544 // adjust margins for outside components
71545 // fullLayout.margin is the requested margin,
71546 // fullLayout._size has margins and plotsize after adjustment
71547 var ml = margin.l;
71548 var mr = margin.r;
71549 var mt = margin.t;
71550 var mb = margin.b;
71551 var width = fullLayout.width;
71552 var height = fullLayout.height;
71553 var pushMargin = fullLayout._pushmargin;
71554 var pushMarginIds = fullLayout._pushmarginIds;
71555
71556 if(fullLayout.margin.autoexpand !== false) {
71557 for(var k in pushMargin) {
71558 if(!pushMarginIds[k]) delete pushMargin[k];
71559 }
71560
71561 // fill in the requested margins
71562 pushMargin.base = {
71563 l: {val: 0, size: ml},
71564 r: {val: 1, size: mr},
71565 t: {val: 1, size: mt},
71566 b: {val: 0, size: mb}
71567 };
71568
71569 // now cycle through all the combinations of l and r
71570 // (and t and b) to find the required margins
71571
71572 for(var k1 in pushMargin) {
71573 var pushleft = pushMargin[k1].l || {};
71574 var pushbottom = pushMargin[k1].b || {};
71575 var fl = pushleft.val;
71576 var pl = pushleft.size;
71577 var fb = pushbottom.val;
71578 var pb = pushbottom.size;
71579
71580 for(var k2 in pushMargin) {
71581 if(isNumeric(pl) && pushMargin[k2].r) {
71582 var fr = pushMargin[k2].r.val;
71583 var pr = pushMargin[k2].r.size;
71584
71585 if(fr > fl) {
71586 var newL = (pl * fr + (pr - width) * fl) / (fr - fl);
71587 var newR = (pr * (1 - fl) + (pl - width) * (1 - fr)) / (fr - fl);
71588 if(newL >= 0 && newR >= 0 && width - (newL + newR) > 0 && newL + newR > ml + mr) {
71589 ml = newL;
71590 mr = newR;
71591 }
71592 }
71593 }
71594
71595 if(isNumeric(pb) && pushMargin[k2].t) {
71596 var ft = pushMargin[k2].t.val;
71597 var pt = pushMargin[k2].t.size;
71598
71599 if(ft > fb) {
71600 var newB = (pb * ft + (pt - height) * fb) / (ft - fb);
71601 var newT = (pt * (1 - fb) + (pb - height) * (1 - ft)) / (ft - fb);
71602 if(newB >= 0 && newT >= 0 && height - (newT + newB) > 0 && newB + newT > mb + mt) {
71603 mb = newB;
71604 mt = newT;
71605 }
71606 }
71607 }
71608 }
71609 }
71610 }
71611
71612 gs.l = Math.round(ml);
71613 gs.r = Math.round(mr);
71614 gs.t = Math.round(mt);
71615 gs.b = Math.round(mb);
71616 gs.p = Math.round(margin.pad);
71617 gs.w = Math.round(width) - gs.l - gs.r;
71618 gs.h = Math.round(height) - gs.t - gs.b;
71619
71620 // if things changed and we're not already redrawing, trigger a redraw
71621 if(!fullLayout._replotting && plots.didMarginChange(oldMargins, gs)) {
71622 if('_redrawFromAutoMarginCount' in fullLayout) {
71623 fullLayout._redrawFromAutoMarginCount++;
71624 } else {
71625 fullLayout._redrawFromAutoMarginCount = 1;
71626 }
71627
71628 // Always allow at least one redraw and give each margin-push
71629 // call 3 loops to converge. Of course, for most cases this way too many,
71630 // but let's keep things on the safe side until we fix our
71631 // auto-margin pipeline problems:
71632 // https://github.com/plotly/plotly.js/issues/2704
71633 var maxNumberOfRedraws = 3 * (1 + Object.keys(pushMarginIds).length);
71634
71635 if(fullLayout._redrawFromAutoMarginCount < maxNumberOfRedraws) {
71636 return Registry.call('plot', gd);
71637 } else {
71638 Lib.warn('Too many auto-margin redraws.');
71639 }
71640 }
71641};
71642
71643var marginKeys = ['l', 'r', 't', 'b', 'p', 'w', 'h'];
71644
71645plots.didMarginChange = function(margin0, margin1) {
71646 for(var i = 0; i < marginKeys.length; i++) {
71647 var k = marginKeys[i];
71648 var m0 = margin0[k];
71649 var m1 = margin1[k];
71650 // use 1px tolerance in case we old/new differ only
71651 // by rounding errors, which can lead to infinite loops
71652 if(!isNumeric(m0) || Math.abs(m1 - m0) > 1) {
71653 return true;
71654 }
71655 }
71656 return false;
71657};
71658
71659/**
71660 * JSONify the graph data and layout
71661 *
71662 * This function needs to recurse because some src can be inside
71663 * sub-objects.
71664 *
71665 * It also strips out functions and private (starts with _) elements.
71666 * Therefore, we can add temporary things to data and layout that don't
71667 * get saved.
71668 *
71669 * @param gd The graphDiv
71670 * @param {Boolean} dataonly If true, don't return layout.
71671 * @param {'keepref'|'keepdata'|'keepall'} [mode='keepref'] Filter what's kept
71672 * keepref: remove data for which there's a src present
71673 * eg if there's xsrc present (and xsrc is well-formed,
71674 * ie has : and some chars before it), strip out x
71675 * keepdata: remove all src tags, don't remove the data itself
71676 * keepall: keep data and src
71677 * @param {String} output If you specify 'object', the result will not be stringified
71678 * @param {Boolean} useDefaults If truthy, use _fullLayout and _fullData
71679 * @param {Boolean} includeConfig If truthy, include _context
71680 * @returns {Object|String}
71681 */
71682plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfig) {
71683 // if the defaults aren't supplied yet, we need to do that...
71684 if((useDefaults && dataonly && !gd._fullData) ||
71685 (useDefaults && !dataonly && !gd._fullLayout)) {
71686 plots.supplyDefaults(gd);
71687 }
71688
71689 var data = (useDefaults) ? gd._fullData : gd.data;
71690 var layout = (useDefaults) ? gd._fullLayout : gd.layout;
71691 var frames = (gd._transitionData || {})._frames;
71692
71693 function stripObj(d, keepFunction) {
71694 if(typeof d === 'function') {
71695 return keepFunction ? '_function_' : null;
71696 }
71697 if(Lib.isPlainObject(d)) {
71698 var o = {};
71699 var src;
71700 Object.keys(d).sort().forEach(function(v) {
71701 // remove private elements and functions
71702 // _ is for private, [ is a mistake ie [object Object]
71703 if(['_', '['].indexOf(v.charAt(0)) !== -1) return;
71704
71705 // if a function, add if necessary then move on
71706 if(typeof d[v] === 'function') {
71707 if(keepFunction) o[v] = '_function';
71708 return;
71709 }
71710
71711 // look for src/data matches and remove the appropriate one
71712 if(mode === 'keepdata') {
71713 // keepdata: remove all ...src tags
71714 if(v.substr(v.length - 3) === 'src') {
71715 return;
71716 }
71717 } else if(mode === 'keepstream') {
71718 // keep sourced data if it's being streamed.
71719 // similar to keepref, but if the 'stream' object exists
71720 // in a trace, we will keep the data array.
71721 src = d[v + 'src'];
71722 if(typeof src === 'string' && src.indexOf(':') > 0) {
71723 if(!Lib.isPlainObject(d.stream)) {
71724 return;
71725 }
71726 }
71727 } else if(mode !== 'keepall') {
71728 // keepref: remove sourced data but only
71729 // if the source tag is well-formed
71730 src = d[v + 'src'];
71731 if(typeof src === 'string' && src.indexOf(':') > 0) {
71732 return;
71733 }
71734 }
71735
71736 // OK, we're including this... recurse into it
71737 o[v] = stripObj(d[v], keepFunction);
71738 });
71739 return o;
71740 }
71741
71742 if(Array.isArray(d)) {
71743 return d.map(function(x) {return stripObj(x, keepFunction);});
71744 }
71745
71746 if(Lib.isTypedArray(d)) {
71747 return Lib.simpleMap(d, Lib.identity);
71748 }
71749
71750 // convert native dates to date strings...
71751 // mostly for external users exporting to plotly
71752 if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d);
71753
71754 return d;
71755 }
71756
71757 var obj = {
71758 data: (data || []).map(function(v) {
71759 var d = stripObj(v);
71760 // fit has some little arrays in it that don't contain data,
71761 // just fit params and meta
71762 if(dataonly) { delete d.fit; }
71763 return d;
71764 })
71765 };
71766 if(!dataonly) { obj.layout = stripObj(layout); }
71767
71768 if(gd.framework && gd.framework.isPolar) obj = gd.framework.getConfig();
71769
71770 if(frames) obj.frames = stripObj(frames);
71771
71772 if(includeConfig) obj.config = stripObj(gd._context, true);
71773
71774 return (output === 'object') ? obj : JSON.stringify(obj);
71775};
71776
71777/**
71778 * Modify a keyframe using a list of operations:
71779 *
71780 * @param {array of objects} operations
71781 * Sequence of operations to be performed on the keyframes
71782 */
71783plots.modifyFrames = function(gd, operations) {
71784 var i, op, frame;
71785 var _frames = gd._transitionData._frames;
71786 var _frameHash = gd._transitionData._frameHash;
71787
71788 for(i = 0; i < operations.length; i++) {
71789 op = operations[i];
71790
71791 switch(op.type) {
71792 // No reason this couldn't exist, but is currently unused/untested:
71793 /* case 'rename':
71794 frame = _frames[op.index];
71795 delete _frameHash[frame.name];
71796 _frameHash[op.name] = frame;
71797 frame.name = op.name;
71798 break;*/
71799 case 'replace':
71800 frame = op.value;
71801 var oldName = (_frames[op.index] || {}).name;
71802 var newName = frame.name;
71803 _frames[op.index] = _frameHash[newName] = frame;
71804
71805 if(newName !== oldName) {
71806 // If name has changed in addition to replacement, then update
71807 // the lookup table:
71808 delete _frameHash[oldName];
71809 _frameHash[newName] = frame;
71810 }
71811
71812 break;
71813 case 'insert':
71814 frame = op.value;
71815 _frameHash[frame.name] = frame;
71816 _frames.splice(op.index, 0, frame);
71817 break;
71818 case 'delete':
71819 frame = _frames[op.index];
71820 delete _frameHash[frame.name];
71821 _frames.splice(op.index, 1);
71822 break;
71823 }
71824 }
71825
71826 return Promise.resolve();
71827};
71828
71829/*
71830 * Compute a keyframe. Merge a keyframe into its base frame(s) and
71831 * expand properties.
71832 *
71833 * @param {object} frameLookup
71834 * An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
71835 * @param {string} frame
71836 * The name of the keyframe to be computed
71837 *
71838 * Returns: a new object with the merged content
71839 */
71840plots.computeFrame = function(gd, frameName) {
71841 var frameLookup = gd._transitionData._frameHash;
71842 var i, traceIndices, traceIndex, destIndex;
71843
71844 // Null or undefined will fail on .toString(). We'll allow numbers since we
71845 // make it clear frames must be given string names, but we'll allow numbers
71846 // here since they're otherwise fine for looking up frames as long as they're
71847 // properly cast to strings. We really just want to ensure here that this
71848 // 1) doesn't fail, and
71849 // 2) doens't give an incorrect answer (which String(frameName) would)
71850 if(!frameName) {
71851 throw new Error('computeFrame must be given a string frame name');
71852 }
71853
71854 var framePtr = frameLookup[frameName.toString()];
71855
71856 // Return false if the name is invalid:
71857 if(!framePtr) {
71858 return false;
71859 }
71860
71861 var frameStack = [framePtr];
71862 var frameNameStack = [framePtr.name];
71863
71864 // Follow frame pointers:
71865 while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) {
71866 // Avoid infinite loops:
71867 if(frameNameStack.indexOf(framePtr.name) !== -1) break;
71868
71869 frameStack.push(framePtr);
71870 frameNameStack.push(framePtr.name);
71871 }
71872
71873 // A new object for the merged result:
71874 var result = {};
71875
71876 // Merge, starting with the last and ending with the desired frame:
71877 while((framePtr = frameStack.pop())) {
71878 if(framePtr.layout) {
71879 result.layout = plots.extendLayout(result.layout, framePtr.layout);
71880 }
71881
71882 if(framePtr.data) {
71883 if(!result.data) {
71884 result.data = [];
71885 }
71886 traceIndices = framePtr.traces;
71887
71888 if(!traceIndices) {
71889 // If not defined, assume serial order starting at zero
71890 traceIndices = [];
71891 for(i = 0; i < framePtr.data.length; i++) {
71892 traceIndices[i] = i;
71893 }
71894 }
71895
71896 if(!result.traces) {
71897 result.traces = [];
71898 }
71899
71900 for(i = 0; i < framePtr.data.length; i++) {
71901 // Loop through this frames data, find out where it should go,
71902 // and merge it!
71903 traceIndex = traceIndices[i];
71904 if(traceIndex === undefined || traceIndex === null) {
71905 continue;
71906 }
71907
71908 destIndex = result.traces.indexOf(traceIndex);
71909 if(destIndex === -1) {
71910 destIndex = result.data.length;
71911 result.traces[destIndex] = traceIndex;
71912 }
71913
71914 result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]);
71915 }
71916 }
71917 }
71918
71919 return result;
71920};
71921
71922/*
71923 * Recompute the lookup table that maps frame name -> frame object. addFrames/
71924 * deleteFrames already manages this data one at a time, so the only time this
71925 * is necessary is if you poke around manually in `gd._transitionData._frames`
71926 * and create and haven't updated the lookup table.
71927 */
71928plots.recomputeFrameHash = function(gd) {
71929 var hash = gd._transitionData._frameHash = {};
71930 var frames = gd._transitionData._frames;
71931 for(var i = 0; i < frames.length; i++) {
71932 var frame = frames[i];
71933 if(frame && frame.name) {
71934 hash[frame.name] = frame;
71935 }
71936 }
71937};
71938
71939/**
71940 * Extend an object, treating container arrays very differently by extracting
71941 * their contents and merging them separately.
71942 *
71943 * This exists so that we can extendDeepNoArrays and avoid stepping into data
71944 * arrays without knowledge of the plot schema, but so that we may also manually
71945 * recurse into known container arrays, such as transforms.
71946 *
71947 * See extendTrace and extendLayout below for usage.
71948 */
71949plots.extendObjectWithContainers = function(dest, src, containerPaths) {
71950 var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer;
71951 var copy = Lib.extendDeepNoArrays({}, src || {});
71952 var expandedObj = Lib.expandObjectPaths(copy);
71953 var containerObj = {};
71954
71955 // Step through and extract any container properties. Otherwise extendDeepNoArrays
71956 // will clobber any existing properties with an empty array and then supplyDefaults
71957 // will reset everything to defaults.
71958 if(containerPaths && containerPaths.length) {
71959 for(i = 0; i < containerPaths.length; i++) {
71960 containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]);
71961 containerVal = containerProp.get();
71962
71963 if(containerVal === undefined) {
71964 Lib.nestedProperty(containerObj, containerPaths[i]).set(null);
71965 } else {
71966 containerProp.set(null);
71967 Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal);
71968 }
71969 }
71970 }
71971
71972 dest = Lib.extendDeepNoArrays(dest || {}, expandedObj);
71973
71974 if(containerPaths && containerPaths.length) {
71975 for(i = 0; i < containerPaths.length; i++) {
71976 srcProp = Lib.nestedProperty(containerObj, containerPaths[i]);
71977 srcContainer = srcProp.get();
71978
71979 if(!srcContainer) continue;
71980
71981 destProp = Lib.nestedProperty(dest, containerPaths[i]);
71982 destContainer = destProp.get();
71983
71984 if(!Array.isArray(destContainer)) {
71985 destContainer = [];
71986 destProp.set(destContainer);
71987 }
71988
71989 for(j = 0; j < srcContainer.length; j++) {
71990 var srcObj = srcContainer[j];
71991
71992 if(srcObj === null) destContainer[j] = null;
71993 else {
71994 destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj);
71995 }
71996 }
71997
71998 destProp.set(destContainer);
71999 }
72000 }
72001
72002 return dest;
72003};
72004
72005plots.dataArrayContainers = ['transforms', 'dimensions'];
72006plots.layoutArrayContainers = Registry.layoutArrayContainers;
72007
72008/*
72009 * Extend a trace definition. This method:
72010 *
72011 * 1. directly transfers any array references
72012 * 2. manually recurses into container arrays like transforms
72013 *
72014 * The result is the original object reference with the new contents merged in.
72015 */
72016plots.extendTrace = function(destTrace, srcTrace) {
72017 return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers);
72018};
72019
72020/*
72021 * Extend a layout definition. This method:
72022 *
72023 * 1. directly transfers any array references (not critically important for
72024 * layout since there aren't really data arrays)
72025 * 2. manually recurses into container arrays like annotations
72026 *
72027 * The result is the original object reference with the new contents merged in.
72028 */
72029plots.extendLayout = function(destLayout, srcLayout) {
72030 return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers);
72031};
72032
72033/**
72034 * Transition to a set of new data and layout properties from Plotly.animate
72035 *
72036 * @param {DOM element} gd
72037 * @param {Object[]} data
72038 * an array of data objects following the normal Plotly data definition format
72039 * @param {Object} layout
72040 * a layout object, following normal Plotly layout format
72041 * @param {Number[]} traces
72042 * indices of the corresponding traces specified in `data`
72043 * @param {Object} frameOpts
72044 * options for the frame (i.e. whether to redraw post-transition)
72045 * @param {Object} transitionOpts
72046 * options for the transition
72047 */
72048plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) {
72049 var opts = {redraw: frameOpts.redraw};
72050 var transitionedTraces = {};
72051 var axEdits = [];
72052
72053 opts.prepareFn = function() {
72054 var dataLength = Array.isArray(data) ? data.length : 0;
72055 var traceIndices = traces.slice(0, dataLength);
72056
72057 for(var i = 0; i < traceIndices.length; i++) {
72058 var traceIdx = traceIndices[i];
72059 var trace = gd._fullData[traceIdx];
72060 var _module = trace._module;
72061
72062 // There's nothing to do if this module is not defined:
72063 if(!_module) continue;
72064
72065 // Don't register the trace as transitioned if it doesn't know what to do.
72066 // If it *is* registered, it will receive a callback that it's responsible
72067 // for calling in order to register the transition as having completed.
72068 if(_module.animatable) {
72069 var n = _module.basePlotModule.name;
72070 if(!transitionedTraces[n]) transitionedTraces[n] = [];
72071 transitionedTraces[n].push(traceIdx);
72072 }
72073
72074 gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
72075 }
72076
72077 // Follow the same procedure. Clone it so we don't mangle the input, then
72078 // expand any object paths so we can merge deep into gd.layout:
72079 var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout));
72080
72081 // Before merging though, we need to modify the incoming layout. We only
72082 // know how to *transition* layout ranges, so it's imperative that a new
72083 // range not be sent to the layout before the transition has started. So
72084 // we must remove the things we can transition:
72085 var axisAttrRe = /^[xy]axis[0-9]*$/;
72086 for(var attr in layoutUpdate) {
72087 if(!axisAttrRe.test(attr)) continue;
72088 delete layoutUpdate[attr].range;
72089 }
72090
72091 plots.extendLayout(gd.layout, layoutUpdate);
72092
72093 // Supply defaults after applying the incoming properties. Note that any attempt
72094 // to simplify this step and reduce the amount of work resulted in the reconstruction
72095 // of essentially the whole supplyDefaults step, so that it seems sensible to just use
72096 // supplyDefaults even though it's heavier than would otherwise be desired for
72097 // transitions:
72098
72099 // first delete calcdata so supplyDefaults knows a calc step is coming
72100 delete gd.calcdata;
72101
72102 plots.supplyDefaults(gd);
72103 plots.doCalcdata(gd);
72104
72105 var newLayout = Lib.expandObjectPaths(layout);
72106
72107 if(newLayout) {
72108 var subplots = gd._fullLayout._plots;
72109
72110 for(var k in subplots) {
72111 var plotinfo = subplots[k];
72112 var xa = plotinfo.xaxis;
72113 var ya = plotinfo.yaxis;
72114 var xr0 = xa.range.slice();
72115 var yr0 = ya.range.slice();
72116
72117 var xr1 = null;
72118 var yr1 = null;
72119 var editX = null;
72120 var editY = null;
72121
72122 if(Array.isArray(newLayout[xa._name + '.range'])) {
72123 xr1 = newLayout[xa._name + '.range'].slice();
72124 } else if(Array.isArray((newLayout[xa._name] || {}).range)) {
72125 xr1 = newLayout[xa._name].range.slice();
72126 }
72127 if(Array.isArray(newLayout[ya._name + '.range'])) {
72128 yr1 = newLayout[ya._name + '.range'].slice();
72129 } else if(Array.isArray((newLayout[ya._name] || {}).range)) {
72130 yr1 = newLayout[ya._name].range.slice();
72131 }
72132
72133 if(xr0 && xr1 &&
72134 (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1]))
72135 ) {
72136 editX = {xr0: xr0, xr1: xr1};
72137 }
72138 if(yr0 && yr1 &&
72139 (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1]))
72140 ) {
72141 editY = {yr0: yr0, yr1: yr1};
72142 }
72143
72144 if(editX || editY) {
72145 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
72146 }
72147 }
72148 }
72149
72150 return Promise.resolve();
72151 };
72152
72153 opts.runFn = function(makeCallback) {
72154 var traceTransitionOpts;
72155 var basePlotModules = gd._fullLayout._basePlotModules;
72156 var hasAxisTransition = axEdits.length;
72157 var i;
72158
72159 if(layout) {
72160 for(i = 0; i < basePlotModules.length; i++) {
72161 if(basePlotModules[i].transitionAxes) {
72162 basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback);
72163 }
72164 }
72165 }
72166
72167 // Here handle the exception that we refuse to animate scales and axes at the same
72168 // time. In other words, if there's an axis transition, then set the data transition
72169 // to instantaneous.
72170 if(hasAxisTransition) {
72171 traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
72172 traceTransitionOpts.duration = 0;
72173 // This means do not transition cartesian traces,
72174 // this happens on layout-only (e.g. axis range) animations
72175 delete transitionedTraces.cartesian;
72176 } else {
72177 traceTransitionOpts = transitionOpts;
72178 }
72179
72180 // Note that we pass a callback to *create* the callback that must be invoked on completion.
72181 // This is since not all traces know about transitions, so it greatly simplifies matters if
72182 // the trace is responsible for creating a callback, if needed, and then executing it when
72183 // the time is right.
72184 for(var n in transitionedTraces) {
72185 var traceIndices = transitionedTraces[n];
72186 var _module = gd._fullData[traceIndices[0]]._module;
72187 _module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback);
72188 }
72189 };
72190
72191 return _transition(gd, transitionOpts, opts);
72192};
72193
72194/**
72195 * Transition to a set of new data and layout properties from Plotly.react
72196 *
72197 * @param {DOM element} gd
72198 * @param {object} restyleFlags
72199 * - anim {'all'|'some'}
72200 * @param {object} relayoutFlags
72201 * - anim {'all'|'some'}
72202 * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout
72203 */
72204plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) {
72205 var fullLayout = gd._fullLayout;
72206 var transitionOpts = fullLayout.transition;
72207 var opts = {};
72208 var axEdits = [];
72209
72210 opts.prepareFn = function() {
72211 var subplots = fullLayout._plots;
72212
72213 // no need to redraw at end of transition,
72214 // if all changes are animatable
72215 opts.redraw = false;
72216 if(restyleFlags.anim === 'some') opts.redraw = true;
72217 if(relayoutFlags.anim === 'some') opts.redraw = true;
72218
72219 for(var k in subplots) {
72220 var plotinfo = subplots[k];
72221 var xa = plotinfo.xaxis;
72222 var ya = plotinfo.yaxis;
72223 var xr0 = oldFullLayout[xa._name].range.slice();
72224 var yr0 = oldFullLayout[ya._name].range.slice();
72225 var xr1 = xa.range.slice();
72226 var yr1 = ya.range.slice();
72227
72228 xa.setScale();
72229 ya.setScale();
72230
72231 var editX = null;
72232 var editY = null;
72233
72234 if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) {
72235 editX = {xr0: xr0, xr1: xr1};
72236 }
72237 if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) {
72238 editY = {yr0: yr0, yr1: yr1};
72239 }
72240
72241 if(editX || editY) {
72242 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
72243 }
72244 }
72245
72246 return Promise.resolve();
72247 };
72248
72249 opts.runFn = function(makeCallback) {
72250 var fullData = gd._fullData;
72251 var fullLayout = gd._fullLayout;
72252 var basePlotModules = fullLayout._basePlotModules;
72253
72254 var axisTransitionOpts;
72255 var traceTransitionOpts;
72256 var transitionedTraces;
72257
72258 var allTraceIndices = [];
72259 for(var i = 0; i < fullData.length; i++) {
72260 allTraceIndices.push(i);
72261 }
72262
72263 function transitionAxes() {
72264 for(var j = 0; j < basePlotModules.length; j++) {
72265 if(basePlotModules[j].transitionAxes) {
72266 basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback);
72267 }
72268 }
72269 }
72270
72271 function transitionTraces() {
72272 for(var j = 0; j < basePlotModules.length; j++) {
72273 basePlotModules[j].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
72274 }
72275 }
72276
72277 if(axEdits.length && restyleFlags.anim) {
72278 if(transitionOpts.ordering === 'traces first') {
72279 axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
72280 transitionedTraces = allTraceIndices;
72281 traceTransitionOpts = transitionOpts;
72282 setTimeout(transitionAxes, transitionOpts.duration);
72283 transitionTraces();
72284 } else {
72285 axisTransitionOpts = transitionOpts;
72286 transitionedTraces = null;
72287 traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
72288 setTimeout(transitionTraces, axisTransitionOpts.duration);
72289 transitionAxes();
72290 }
72291 } else if(axEdits.length) {
72292 axisTransitionOpts = transitionOpts;
72293 transitionAxes();
72294 } else if(restyleFlags.anim) {
72295 transitionedTraces = allTraceIndices;
72296 traceTransitionOpts = transitionOpts;
72297 transitionTraces();
72298 }
72299 };
72300
72301 return _transition(gd, transitionOpts, opts);
72302};
72303
72304/**
72305 * trace/layout transition wrapper that works
72306 * for transitions initiated by Plotly.animate and Plotly.react.
72307 *
72308 * @param {DOM element} gd
72309 * @param {object} transitionOpts
72310 * @param {object} opts
72311 * - redraw {boolean}
72312 * - prepareFn {function} *should return a Promise*
72313 * - runFn {function} ran inside executeTransitions
72314 */
72315function _transition(gd, transitionOpts, opts) {
72316 var aborted = false;
72317
72318 function executeCallbacks(list) {
72319 var p = Promise.resolve();
72320 if(!list) return p;
72321 while(list.length) {
72322 p = p.then((list.shift()));
72323 }
72324 return p;
72325 }
72326
72327 function flushCallbacks(list) {
72328 if(!list) return;
72329 while(list.length) {
72330 list.shift();
72331 }
72332 }
72333
72334 function executeTransitions() {
72335 gd.emit('plotly_transitioning', []);
72336
72337 return new Promise(function(resolve) {
72338 // This flag is used to disabled things like autorange:
72339 gd._transitioning = true;
72340
72341 // When instantaneous updates are coming through quickly, it's too much to simply disable
72342 // all interaction, so store this flag so we can disambiguate whether mouse interactions
72343 // should be fully disabled or not:
72344 if(transitionOpts.duration > 0) {
72345 gd._transitioningWithDuration = true;
72346 }
72347
72348 // If another transition is triggered, this callback will be executed simply because it's
72349 // in the interruptCallbacks queue. If this transition completes, it will instead flush
72350 // that queue and forget about this callback.
72351 gd._transitionData._interruptCallbacks.push(function() {
72352 aborted = true;
72353 });
72354
72355 if(opts.redraw) {
72356 gd._transitionData._interruptCallbacks.push(function() {
72357 return Registry.call('redraw', gd);
72358 });
72359 }
72360
72361 // Emit this and make sure it happens last:
72362 gd._transitionData._interruptCallbacks.push(function() {
72363 gd.emit('plotly_transitioninterrupted', []);
72364 });
72365
72366 // Construct callbacks that are executed on transition end. This ensures the d3 transitions
72367 // are *complete* before anything else is done.
72368 var numCallbacks = 0;
72369 var numCompleted = 0;
72370 function makeCallback() {
72371 numCallbacks++;
72372 return function() {
72373 numCompleted++;
72374 // When all are complete, perform a redraw:
72375 if(!aborted && numCompleted === numCallbacks) {
72376 completeTransition(resolve);
72377 }
72378 };
72379 }
72380
72381 opts.runFn(makeCallback);
72382
72383 // If nothing else creates a callback, then this will trigger the completion in the next tick:
72384 setTimeout(makeCallback());
72385 });
72386 }
72387
72388 function completeTransition(callback) {
72389 // This a simple workaround for tests which purge the graph before animations
72390 // have completed. That's not a very common case, so this is the simplest
72391 // fix.
72392 if(!gd._transitionData) return;
72393
72394 flushCallbacks(gd._transitionData._interruptCallbacks);
72395
72396 return Promise.resolve().then(function() {
72397 if(opts.redraw) {
72398 return Registry.call('redraw', gd);
72399 }
72400 }).then(function() {
72401 // Set transitioning false again once the redraw has occurred. This is used, for example,
72402 // to prevent the trailing redraw from autoranging:
72403 gd._transitioning = false;
72404 gd._transitioningWithDuration = false;
72405
72406 gd.emit('plotly_transitioned', []);
72407 }).then(callback);
72408 }
72409
72410 function interruptPreviousTransitions() {
72411 // Fail-safe against purged plot:
72412 if(!gd._transitionData) return;
72413
72414 // If a transition is interrupted, set this to false. At the moment, the only thing that would
72415 // interrupt a transition is another transition, so that it will momentarily be set to true
72416 // again, but this determines whether autorange or dragbox work, so it's for the sake of
72417 // cleanliness:
72418 gd._transitioning = false;
72419
72420 return executeCallbacks(gd._transitionData._interruptCallbacks);
72421 }
72422
72423 var seq = [
72424 plots.previousPromises,
72425 interruptPreviousTransitions,
72426 opts.prepareFn,
72427 plots.rehover,
72428 executeTransitions
72429 ];
72430
72431 var transitionStarting = Lib.syncOrAsync(seq, gd);
72432
72433 if(!transitionStarting || !transitionStarting.then) {
72434 transitionStarting = Promise.resolve();
72435 }
72436
72437 return transitionStarting.then(function() { return gd; });
72438}
72439
72440plots.doCalcdata = function(gd, traces) {
72441 var axList = axisIDs.list(gd);
72442 var fullData = gd._fullData;
72443 var fullLayout = gd._fullLayout;
72444
72445 var trace, _module, i, j;
72446
72447 // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
72448 // *all* needing doCalcdata:
72449 var calcdata = new Array(fullData.length);
72450 var oldCalcdata = (gd.calcdata || []).slice();
72451 gd.calcdata = calcdata;
72452
72453 // extra helper variables
72454
72455 // how many box/violins plots do we have (in case they're grouped)
72456 fullLayout._numBoxes = 0;
72457 fullLayout._numViolins = 0;
72458
72459 // initialize violin per-scale-group stats container
72460 fullLayout._violinScaleGroupStats = {};
72461
72462 // for calculating avg luminosity of heatmaps
72463 gd._hmpixcount = 0;
72464 gd._hmlumcount = 0;
72465
72466 // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend)
72467 fullLayout._piecolormap = {};
72468 fullLayout._sunburstcolormap = {};
72469 fullLayout._treemapcolormap = {};
72470 fullLayout._funnelareacolormap = {};
72471
72472 // If traces were specified and this trace was not included,
72473 // then transfer it over from the old calcdata:
72474 for(i = 0; i < fullData.length; i++) {
72475 if(Array.isArray(traces) && traces.indexOf(i) === -1) {
72476 calcdata[i] = oldCalcdata[i];
72477 continue;
72478 }
72479 }
72480
72481 for(i = 0; i < fullData.length; i++) {
72482 trace = fullData[i];
72483
72484 trace._arrayAttrs = PlotSchema.findArrayAttributes(trace);
72485
72486 // keep track of trace extremes (for autorange) in here
72487 trace._extremes = {};
72488 }
72489
72490 // add polar axes to axis list
72491 var polarIds = fullLayout._subplots.polar || [];
72492 for(i = 0; i < polarIds.length; i++) {
72493 axList.push(
72494 fullLayout[polarIds[i]].radialaxis,
72495 fullLayout[polarIds[i]].angularaxis
72496 );
72497 }
72498
72499 // clear relinked cmin/cmax values in shared axes to start aggregation from scratch
72500 for(var k in fullLayout._colorAxes) {
72501 var cOpts = fullLayout[k];
72502 if(cOpts.cauto !== false) {
72503 delete cOpts.cmin;
72504 delete cOpts.cmax;
72505 }
72506 }
72507
72508 var hasCalcTransform = false;
72509
72510 function transformCalci(i) {
72511 trace = fullData[i];
72512 _module = trace._module;
72513
72514 if(trace.visible === true && trace.transforms) {
72515 // we need one round of trace module calc before
72516 // the calc transform to 'fill in' the categories list
72517 // used for example in the data-to-coordinate method
72518 if(_module && _module.calc) {
72519 var cdi = _module.calc(gd, trace);
72520
72521 // must clear scene 'batches', so that 2nd
72522 // _module.calc call starts from scratch
72523 if(cdi[0] && cdi[0].t && cdi[0].t._scene) {
72524 delete cdi[0].t._scene.dirty;
72525 }
72526 }
72527
72528 for(j = 0; j < trace.transforms.length; j++) {
72529 var transform = trace.transforms[j];
72530
72531 _module = transformsRegistry[transform.type];
72532 if(_module && _module.calcTransform) {
72533 trace._hasCalcTransform = true;
72534 hasCalcTransform = true;
72535 _module.calcTransform(gd, trace, transform);
72536 }
72537 }
72538 }
72539 }
72540
72541 function calci(i, isContainer) {
72542 trace = fullData[i];
72543 _module = trace._module;
72544
72545 if(!!_module.isContainer !== isContainer) return;
72546
72547 var cd = [];
72548
72549 if(trace.visible === true && trace._length !== 0) {
72550 // clear existing ref in case it got relinked
72551 delete trace._indexToPoints;
72552 // keep ref of index-to-points map object of the *last* enabled transform,
72553 // this index-to-points map object is required to determine the calcdata indices
72554 // that correspond to input indices (e.g. from 'selectedpoints')
72555 var transforms = trace.transforms || [];
72556 for(j = transforms.length - 1; j >= 0; j--) {
72557 if(transforms[j].enabled) {
72558 trace._indexToPoints = transforms[j]._indexToPoints;
72559 break;
72560 }
72561 }
72562
72563 if(_module && _module.calc) {
72564 cd = _module.calc(gd, trace);
72565 }
72566 }
72567
72568 // Make sure there is a first point.
72569 //
72570 // This ensures there is a calcdata item for every trace,
72571 // even if cartesian logic doesn't handle it (for things like legends).
72572 if(!Array.isArray(cd) || !cd[0]) {
72573 cd = [{x: BADNUM, y: BADNUM}];
72574 }
72575
72576 // add the trace-wide properties to the first point,
72577 // per point properties to every point
72578 // t is the holder for trace-wide properties
72579 if(!cd[0].t) cd[0].t = {};
72580 cd[0].trace = trace;
72581
72582 calcdata[i] = cd;
72583 }
72584
72585 setupAxisCategories(axList, fullData, fullLayout);
72586
72587 // 'transform' loop - must calc container traces first
72588 // so that if their dependent traces can get transform properly
72589 for(i = 0; i < fullData.length; i++) calci(i, true);
72590 for(i = 0; i < fullData.length; i++) transformCalci(i);
72591
72592 // clear stuff that should recomputed in 'regular' loop
72593 if(hasCalcTransform) setupAxisCategories(axList, fullData, fullLayout);
72594
72595 // 'regular' loop - make sure container traces (eg carpet) calc before
72596 // contained traces (eg contourcarpet)
72597 for(i = 0; i < fullData.length; i++) calci(i, true);
72598 for(i = 0; i < fullData.length; i++) calci(i, false);
72599
72600 doCrossTraceCalc(gd);
72601
72602 // Sort axis categories per value if specified
72603 var sorted = sortAxisCategoriesByValue(axList, gd);
72604 if(sorted.length) {
72605 // how many box/violins plots do we have (in case they're grouped)
72606 fullLayout._numBoxes = 0;
72607 fullLayout._numViolins = 0;
72608 // If a sort operation was performed, run calc() again
72609 for(i = 0; i < sorted.length; i++) calci(sorted[i], true);
72610 for(i = 0; i < sorted.length; i++) calci(sorted[i], false);
72611 doCrossTraceCalc(gd);
72612 }
72613
72614 Registry.getComponentMethod('fx', 'calc')(gd);
72615 Registry.getComponentMethod('errorbars', 'calc')(gd);
72616};
72617
72618var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|median) (ascending|descending)/;
72619
72620function sortAxisCategoriesByValue(axList, gd) {
72621 var affectedTraces = [];
72622 var i, j, k, l, o;
72623
72624 function zMapCategory(type, ax, value) {
72625 var axLetter = ax._id.charAt(0);
72626 if(type === 'histogram2dcontour') {
72627 var counterAxLetter = ax._counterAxes[0];
72628 var counterAx = axisIDs.getFromId(gd, counterAxLetter);
72629
72630 var xCategorical = axLetter === 'x' || (counterAxLetter === 'x' && counterAx.type === 'category');
72631 var yCategorical = axLetter === 'y' || (counterAxLetter === 'y' && counterAx.type === 'category');
72632
72633 return function(o, l) {
72634 if(o === 0 || l === 0) return -1; // Skip first row and column
72635 if(xCategorical && o === value[l].length - 1) return -1;
72636 if(yCategorical && l === value.length - 1) return -1;
72637
72638 return (axLetter === 'y' ? l : o) - 1;
72639 };
72640 } else {
72641 return function(o, l) {
72642 return axLetter === 'y' ? l : o;
72643 };
72644 }
72645 }
72646
72647 var aggFn = {
72648 'min': function(values) {return Lib.aggNums(Math.min, null, values);},
72649 'max': function(values) {return Lib.aggNums(Math.max, null, values);},
72650 'sum': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
72651 'total': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
72652 'mean': function(values) {return Lib.mean(values);},
72653 'median': function(values) {return Lib.median(values);}
72654 };
72655
72656 for(i = 0; i < axList.length; i++) {
72657 var ax = axList[i];
72658 if(ax.type !== 'category') continue;
72659
72660 // Order by value
72661 var match = ax.categoryorder.match(sortAxisCategoriesByValueRegex);
72662 if(match) {
72663 var aggregator = match[1];
72664 var order = match[2];
72665
72666 // Store values associated with each category
72667 var categoriesValue = [];
72668 for(j = 0; j < ax._categories.length; j++) {
72669 categoriesValue.push([ax._categories[j], []]);
72670 }
72671
72672 // Collect values across traces
72673 for(j = 0; j < ax._traceIndices.length; j++) {
72674 var traceIndex = ax._traceIndices[j];
72675 var fullTrace = gd._fullData[traceIndex];
72676 var axLetter = ax._id.charAt(0);
72677
72678 // Skip over invisible traces
72679 if(fullTrace.visible !== true) continue;
72680
72681 var type = fullTrace.type;
72682 if(Registry.traceIs(fullTrace, 'histogram')) {
72683 delete fullTrace._xautoBinFinished;
72684 delete fullTrace._yautoBinFinished;
72685 }
72686
72687 var cd = gd.calcdata[traceIndex];
72688 for(k = 0; k < cd.length; k++) {
72689 var cdi = cd[k];
72690 var cat, catIndex, value;
72691
72692 if(type === 'splom') {
72693 // If `splom`, collect values across dimensions
72694 // Find which dimension the current axis is representing
72695 var currentDimensionIndex = fullTrace._axesDim[ax._id];
72696
72697 // Apply logic to associated x axis if it's defined
72698 if(axLetter === 'y') {
72699 var associatedXAxisID = fullTrace._diag[currentDimensionIndex][0];
72700 if(associatedXAxisID) ax = gd._fullLayout[axisIDs.id2name(associatedXAxisID)];
72701 }
72702
72703 var categories = cdi.trace.dimensions[currentDimensionIndex].values;
72704 for(l = 0; l < categories.length; l++) {
72705 cat = categories[l];
72706 catIndex = ax._categoriesMap[cat];
72707
72708 // Collect associated values at index `l` over all other dimensions
72709 for(o = 0; o < cdi.trace.dimensions.length; o++) {
72710 if(o === currentDimensionIndex) continue;
72711 var dimension = cdi.trace.dimensions[o];
72712 categoriesValue[catIndex][1].push(dimension.values[l]);
72713 }
72714 }
72715 } else if(type === 'scattergl') {
72716 // If `scattergl`, collect all values stashed under cdi.t
72717 for(l = 0; l < cdi.t.x.length; l++) {
72718 if(axLetter === 'x') {
72719 cat = cdi.t.x[l];
72720 catIndex = cat;
72721 value = cdi.t.y[l];
72722 }
72723
72724 if(axLetter === 'y') {
72725 cat = cdi.t.y[l];
72726 catIndex = cat;
72727 value = cdi.t.x[l];
72728 }
72729 categoriesValue[catIndex][1].push(value);
72730 }
72731 // must clear scene 'batches', so that 2nd
72732 // _module.calc call starts from scratch
72733 if(cdi.t && cdi.t._scene) {
72734 delete cdi.t._scene.dirty;
72735 }
72736 } else if(cdi.hasOwnProperty('z')) {
72737 // If 2dMap, collect values in `z`
72738 value = cdi.z;
72739 var mapping = zMapCategory(fullTrace.type, ax, value);
72740
72741 for(l = 0; l < value.length; l++) {
72742 for(o = 0; o < value[l].length; o++) {
72743 catIndex = mapping(o, l);
72744 if(catIndex + 1) categoriesValue[catIndex][1].push(value[l][o]);
72745 }
72746 }
72747 } else {
72748 // For all other 2d cartesian traces
72749 if(axLetter === 'x') {
72750 cat = cdi.p + 1 ? cdi.p : cdi.x;
72751 value = cdi.s || cdi.v || cdi.y;
72752 } else if(axLetter === 'y') {
72753 cat = cdi.p + 1 ? cdi.p : cdi.y;
72754 value = cdi.s || cdi.v || cdi.x;
72755 }
72756 if(!Array.isArray(value)) value = [value];
72757 for(l = 0; l < value.length; l++) {
72758 categoriesValue[cat][1].push(value[l]);
72759 }
72760 }
72761 }
72762 }
72763
72764 ax._categoriesValue = categoriesValue;
72765
72766 var categoriesAggregatedValue = [];
72767 for(j = 0; j < categoriesValue.length; j++) {
72768 categoriesAggregatedValue.push([
72769 categoriesValue[j][0],
72770 aggFn[aggregator](categoriesValue[j][1])
72771 ]);
72772 }
72773
72774 // Sort by aggregated value
72775 categoriesAggregatedValue.sort(function(a, b) {
72776 return a[1] - b[1];
72777 });
72778
72779 ax._categoriesAggregatedValue = categoriesAggregatedValue;
72780
72781 // Set new category order
72782 ax._initialCategories = categoriesAggregatedValue.map(function(c) {
72783 return c[0];
72784 });
72785
72786 // Reverse if descending
72787 if(order === 'descending') {
72788 ax._initialCategories.reverse();
72789 }
72790
72791 // Sort all matching axes
72792 affectedTraces = affectedTraces.concat(ax.sortByInitialCategories());
72793 }
72794 }
72795 return affectedTraces;
72796}
72797
72798function setupAxisCategories(axList, fullData, fullLayout) {
72799 var axLookup = {};
72800 var i, ax, axId;
72801
72802 for(i = 0; i < axList.length; i++) {
72803 ax = axList[i];
72804 axId = ax._id;
72805
72806 ax.clearCalc();
72807 if(ax.type === 'multicategory') {
72808 ax.setupMultiCategory(fullData);
72809 }
72810
72811 axLookup[ax._id] = 1;
72812 }
72813
72814 // look into match groups for 'missing' axes
72815 var matchGroups = fullLayout._axisMatchGroups || [];
72816 for(i = 0; i < matchGroups.length; i++) {
72817 for(axId in matchGroups[i]) {
72818 if(!axLookup[axId]) {
72819 ax = fullLayout[axisIDs.id2name(axId)];
72820 ax.clearCalc();
72821 }
72822 }
72823 }
72824}
72825
72826function doCrossTraceCalc(gd) {
72827 var fullLayout = gd._fullLayout;
72828 var modules = fullLayout._visibleModules;
72829 var hash = {};
72830 var i, j, k;
72831
72832 // position and range calculations for traces that
72833 // depend on each other ie bars (stacked or grouped)
72834 // and boxes (grouped) push each other out of the way
72835
72836 for(j = 0; j < modules.length; j++) {
72837 var _module = modules[j];
72838 var fn = _module.crossTraceCalc;
72839 if(fn) {
72840 var spType = _module.basePlotModule.name;
72841 if(hash[spType]) {
72842 Lib.pushUnique(hash[spType], fn);
72843 } else {
72844 hash[spType] = [fn];
72845 }
72846 }
72847 }
72848
72849 for(k in hash) {
72850 var methods = hash[k];
72851 var subplots = fullLayout._subplots[k];
72852
72853 if(Array.isArray(subplots)) {
72854 for(i = 0; i < subplots.length; i++) {
72855 var sp = subplots[i];
72856 var spInfo = k === 'cartesian' ?
72857 fullLayout._plots[sp] :
72858 fullLayout[sp];
72859
72860 for(j = 0; j < methods.length; j++) {
72861 methods[j](gd, spInfo, sp);
72862 }
72863 }
72864 } else {
72865 for(j = 0; j < methods.length; j++) {
72866 methods[j](gd);
72867 }
72868 }
72869 }
72870}
72871
72872plots.rehover = function(gd) {
72873 if(gd._fullLayout._rehover) {
72874 gd._fullLayout._rehover();
72875 }
72876};
72877
72878plots.redrag = function(gd) {
72879 if(gd._fullLayout._redrag) {
72880 gd._fullLayout._redrag();
72881 }
72882};
72883
72884plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) {
72885 var traceHashOld = subplot.traceHash;
72886 var traceHash = {};
72887 var i;
72888
72889 // build up moduleName -> calcData hash
72890 for(i = 0; i < subplotCalcData.length; i++) {
72891 var calcTraces = subplotCalcData[i];
72892 var trace = calcTraces[0].trace;
72893
72894 // skip over visible === false traces
72895 // as they don't have `_module` ref
72896 if(trace.visible) {
72897 traceHash[trace.type] = traceHash[trace.type] || [];
72898 traceHash[trace.type].push(calcTraces);
72899 }
72900 }
72901
72902 // when a trace gets deleted, make sure that its module's
72903 // plot method is called so that it is properly
72904 // removed from the DOM.
72905 for(var moduleNameOld in traceHashOld) {
72906 if(!traceHash[moduleNameOld]) {
72907 var fakeCalcTrace = traceHashOld[moduleNameOld][0];
72908 var fakeTrace = fakeCalcTrace[0].trace;
72909
72910 fakeTrace.visible = false;
72911 traceHash[moduleNameOld] = [fakeCalcTrace];
72912 }
72913 }
72914
72915 // call module plot method
72916 for(var moduleName in traceHash) {
72917 var moduleCalcData = traceHash[moduleName];
72918 var _module = moduleCalcData[0][0].trace._module;
72919
72920 _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
72921 }
72922
72923 // update moduleName -> calcData hash
72924 subplot.traceHash = traceHash;
72925};
72926
72927plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) {
72928 var _module = Registry.getModule(desiredType);
72929 var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
72930 _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
72931};
72932
72933plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) {
72934 var had = (oldFullLayout._has && oldFullLayout._has(desiredType));
72935 var has = (newFullLayout._has && newFullLayout._has(desiredType));
72936
72937 if(had && !has) {
72938 oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove();
72939 }
72940};
72941
72942},{"../components/color":50,"../constants/numerical":155,"../lib":177,"../plot_api/plot_schema":211,"../plot_api/plot_template":212,"../plots/get_data":259,"../registry":272,"./animation_attributes":217,"./attributes":219,"./cartesian/axis_ids":225,"./cartesian/handle_outline":232,"./command":248,"./font_attributes":250,"./frame_attributes":251,"./layout_attributes":261,"d3":13,"fast-isnumeric":15}],264:[function(_dereq_,module,exports){
72943/**
72944* Copyright 2012-2020, Plotly, Inc.
72945* All rights reserved.
72946*
72947* This source code is licensed under the MIT license found in the
72948* LICENSE file in the root directory of this source tree.
72949*/
72950
72951'use strict';
72952
72953var scatterAttrs = _dereq_('../../../traces/scatter/attributes');
72954var scatterMarkerAttrs = scatterAttrs.marker;
72955var extendFlat = _dereq_('../../../lib/extend').extendFlat;
72956
72957var deprecationWarning = [
72958 'Area traces are deprecated!',
72959 'Please switch to the *barpolar* trace type.'
72960].join(' ');
72961
72962module.exports = {
72963 r: extendFlat({}, scatterAttrs.r, {
72964
72965 }),
72966 t: extendFlat({}, scatterAttrs.t, {
72967
72968 }),
72969 marker: {
72970 color: extendFlat({}, scatterMarkerAttrs.color, {
72971
72972 }),
72973 size: extendFlat({}, scatterMarkerAttrs.size, {
72974
72975 }),
72976 symbol: extendFlat({}, scatterMarkerAttrs.symbol, {
72977
72978 }),
72979 opacity: extendFlat({}, scatterMarkerAttrs.opacity, {
72980
72981 }),
72982 editType: 'calc'
72983 }
72984};
72985
72986},{"../../../lib/extend":170,"../../../traces/scatter/attributes":294}],265:[function(_dereq_,module,exports){
72987/**
72988* Copyright 2012-2020, Plotly, Inc.
72989* All rights reserved.
72990*
72991* This source code is licensed under the MIT license found in the
72992* LICENSE file in the root directory of this source tree.
72993*/
72994
72995
72996'use strict';
72997
72998var axesAttrs = _dereq_('../../cartesian/layout_attributes');
72999var extendFlat = _dereq_('../../../lib/extend').extendFlat;
73000var overrideAll = _dereq_('../../../plot_api/edit_types').overrideAll;
73001
73002var deprecationWarning = [
73003 'Legacy polar charts are deprecated!',
73004 'Please switch to *polar* subplots.'
73005].join(' ');
73006
73007var domainAttr = extendFlat({}, axesAttrs.domain, {
73008
73009});
73010
73011function mergeAttrs(axisName, nonCommonAttrs) {
73012 var commonAttrs = {
73013 showline: {
73014 valType: 'boolean',
73015
73016
73017 },
73018 showticklabels: {
73019 valType: 'boolean',
73020
73021
73022 },
73023 tickorientation: {
73024 valType: 'enumerated',
73025 values: ['horizontal', 'vertical'],
73026
73027
73028 },
73029 ticklen: {
73030 valType: 'number',
73031 min: 0,
73032
73033
73034 },
73035 tickcolor: {
73036 valType: 'color',
73037
73038
73039 },
73040 ticksuffix: {
73041 valType: 'string',
73042
73043
73044 },
73045 endpadding: {
73046 valType: 'number',
73047
73048 description: deprecationWarning,
73049 },
73050 visible: {
73051 valType: 'boolean',
73052
73053
73054 }
73055 };
73056
73057 return extendFlat({}, nonCommonAttrs, commonAttrs);
73058}
73059
73060module.exports = overrideAll({
73061 radialaxis: mergeAttrs('radial', {
73062 range: {
73063 valType: 'info_array',
73064
73065 items: [
73066 { valType: 'number' },
73067 { valType: 'number' }
73068 ],
73069
73070 },
73071 domain: domainAttr,
73072 orientation: {
73073 valType: 'number',
73074
73075
73076 }
73077 }),
73078
73079 angularaxis: mergeAttrs('angular', {
73080 range: {
73081 valType: 'info_array',
73082
73083 items: [
73084 { valType: 'number', dflt: 0 },
73085 { valType: 'number', dflt: 360 }
73086 ],
73087
73088 },
73089 domain: domainAttr
73090 }),
73091
73092 // attributes that appear at layout root
73093 layout: {
73094 direction: {
73095 valType: 'enumerated',
73096 values: ['clockwise', 'counterclockwise'],
73097
73098
73099 },
73100 orientation: {
73101 valType: 'angle',
73102
73103
73104 }
73105 }
73106}, 'plot', 'nested');
73107
73108},{"../../../lib/extend":170,"../../../plot_api/edit_types":205,"../../cartesian/layout_attributes":236}],266:[function(_dereq_,module,exports){
73109/**
73110* Copyright 2012-2020, Plotly, Inc.
73111* All rights reserved.
73112*
73113* This source code is licensed under the MIT license found in the
73114* LICENSE file in the root directory of this source tree.
73115*/
73116
73117'use strict';
73118
73119var Polar = module.exports = _dereq_('./micropolar');
73120
73121Polar.manager = _dereq_('./micropolar_manager');
73122
73123},{"./micropolar":267,"./micropolar_manager":268}],267:[function(_dereq_,module,exports){
73124/**
73125* Copyright 2012-2020, Plotly, Inc.
73126* All rights reserved.
73127*
73128* This source code is licensed under the MIT license found in the
73129* LICENSE file in the root directory of this source tree.
73130*/
73131
73132var d3 = _dereq_('d3');
73133var Lib = _dereq_('../../../lib');
73134var extendDeepAll = Lib.extendDeepAll;
73135var MID_SHIFT = _dereq_('../../../constants/alignment').MID_SHIFT;
73136
73137var µ = module.exports = { version: '0.2.2' };
73138
73139µ.Axis = function module() {
73140 var config = {
73141 data: [],
73142 layout: {}
73143 }, inputConfig = {}, liveConfig = {};
73144 var svg, container, dispatch = d3.dispatch('hover'), radialScale, angularScale;
73145 var exports = {};
73146 function render(_container) {
73147 container = _container || container;
73148 var data = config.data;
73149 var axisConfig = config.layout;
73150 if (typeof container == 'string' || container.nodeName) container = d3.select(container);
73151 container.datum(data).each(function(_data, _index) {
73152 var dataOriginal = _data.slice();
73153 liveConfig = {
73154 data: µ.util.cloneJson(dataOriginal),
73155 layout: µ.util.cloneJson(axisConfig)
73156 };
73157 var colorIndex = 0;
73158 dataOriginal.forEach(function(d, i) {
73159 if (!d.color) {
73160 d.color = axisConfig.defaultColorRange[colorIndex];
73161 colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length;
73162 }
73163 if (!d.strokeColor) {
73164 d.strokeColor = d.geometry === 'LinePlot' ? d.color : d3.rgb(d.color).darker().toString();
73165 }
73166 liveConfig.data[i].color = d.color;
73167 liveConfig.data[i].strokeColor = d.strokeColor;
73168 liveConfig.data[i].strokeDash = d.strokeDash;
73169 liveConfig.data[i].strokeSize = d.strokeSize;
73170 });
73171 var data = dataOriginal.filter(function(d, i) {
73172 var visible = d.visible;
73173 return typeof visible === 'undefined' || visible === true;
73174 });
73175 var isStacked = false;
73176 var dataWithGroupId = data.map(function(d, i) {
73177 isStacked = isStacked || typeof d.groupId !== 'undefined';
73178 return d;
73179 });
73180 if (isStacked) {
73181 var grouped = d3.nest().key(function(d, i) {
73182 return typeof d.groupId != 'undefined' ? d.groupId : 'unstacked';
73183 }).entries(dataWithGroupId);
73184 var dataYStack = [];
73185 var stacked = grouped.map(function(d, i) {
73186 if (d.key === 'unstacked') return d.values; else {
73187 var prevArray = d.values[0].r.map(function(d, i) {
73188 return 0;
73189 });
73190 d.values.forEach(function(d, i, a) {
73191 d.yStack = [ prevArray ];
73192 dataYStack.push(prevArray);
73193 prevArray = µ.util.sumArrays(d.r, prevArray);
73194 });
73195 return d.values;
73196 }
73197 });
73198 data = d3.merge(stacked);
73199 }
73200 data.forEach(function(d, i) {
73201 d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ];
73202 d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ];
73203 });
73204 var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
73205 radius = Math.max(10, radius);
73206 var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
73207 var extent;
73208 if (isStacked) {
73209 var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack)));
73210 extent = [ 0, highestStackedValue ];
73211 } else extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) {
73212 return d.r;
73213 })));
73214 if (axisConfig.radialAxis.domain != µ.DATAEXTENT) extent[0] = 0;
73215 radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain != µ.DATAEXTENT && axisConfig.radialAxis.domain ? axisConfig.radialAxis.domain : extent).range([ 0, radius ]);
73216 liveConfig.layout.radialAxis.domain = radialScale.domain();
73217 var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) {
73218 return d.t;
73219 }));
73220 var isOrdinal = typeof angularDataMerged[0] === 'string';
73221 var ticks;
73222 if (isOrdinal) {
73223 angularDataMerged = µ.util.deduplicate(angularDataMerged);
73224 ticks = angularDataMerged.slice();
73225 angularDataMerged = d3.range(angularDataMerged.length);
73226 data = data.map(function(d, i) {
73227 var result = d;
73228 d.t = [ angularDataMerged ];
73229 if (isStacked) result.yStack = d.yStack;
73230 return result;
73231 });
73232 }
73233 var hasOnlyLineOrDotPlot = data.filter(function(d, i) {
73234 return d.geometry === 'LinePlot' || d.geometry === 'DotPlot';
73235 }).length === data.length;
73236 var needsEndSpacing = axisConfig.needsEndSpacing === null ? isOrdinal || !hasOnlyLineOrDotPlot : axisConfig.needsEndSpacing;
73237 var useProvidedDomain = axisConfig.angularAxis.domain && axisConfig.angularAxis.domain != µ.DATAEXTENT && !isOrdinal && axisConfig.angularAxis.domain[0] >= 0;
73238 var angularDomain = useProvidedDomain ? axisConfig.angularAxis.domain : d3.extent(angularDataMerged);
73239 var angularDomainStep = Math.abs(angularDataMerged[1] - angularDataMerged[0]);
73240 if (hasOnlyLineOrDotPlot && !isOrdinal) angularDomainStep = 0;
73241 var angularDomainWithPadding = angularDomain.slice();
73242 if (needsEndSpacing && isOrdinal) angularDomainWithPadding[1] += angularDomainStep;
73243 var tickCount = axisConfig.angularAxis.ticksCount || 4;
73244 if (tickCount > 8) tickCount = tickCount / (tickCount / 8) + tickCount % 8;
73245 if (axisConfig.angularAxis.ticksStep) {
73246 tickCount = (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / tickCount;
73247 }
73248 var angularTicksStep = axisConfig.angularAxis.ticksStep || (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / (tickCount * (axisConfig.minorTicks + 1));
73249 if (ticks) angularTicksStep = Math.max(Math.round(angularTicksStep), 1);
73250 if (!angularDomainWithPadding[2]) angularDomainWithPadding[2] = angularTicksStep;
73251 var angularAxisRange = d3.range.apply(this, angularDomainWithPadding);
73252 angularAxisRange = angularAxisRange.map(function(d, i) {
73253 return parseFloat(d.toPrecision(12));
73254 });
73255 angularScale = d3.scale.linear().domain(angularDomainWithPadding.slice(0, 2)).range(axisConfig.direction === 'clockwise' ? [ 0, 360 ] : [ 360, 0 ]);
73256 liveConfig.layout.angularAxis.domain = angularScale.domain();
73257 liveConfig.layout.angularAxis.endPadding = needsEndSpacing ? angularDomainStep : 0;
73258 svg = d3.select(this).select('svg.chart-root');
73259 if (typeof svg === 'undefined' || svg.empty()) {
73260 var skeleton = "<svg xmlns='http://www.w3.org/2000/svg' class='chart-root'>' + '<g class='outer-group'>' + '<g class='chart-group'>' + '<circle class='background-circle'></circle>' + '<g class='geometry-group'></g>' + '<g class='radial axis-group'>' + '<circle class='outside-circle'></circle>' + '</g>' + '<g class='angular axis-group'></g>' + '<g class='guides-group'><line></line><circle r='0'></circle></g>' + '</g>' + '<g class='legend-group'></g>' + '<g class='tooltips-group'></g>' + '<g class='title-group'><text></text></g>' + '</g>' + '</svg>";
73261 var doc = new DOMParser().parseFromString(skeleton, 'application/xml');
73262 var newSvg = this.appendChild(this.ownerDocument.importNode(doc.documentElement, true));
73263 svg = d3.select(newSvg);
73264 }
73265 svg.select('.guides-group').style({
73266 'pointer-events': 'none'
73267 });
73268 svg.select('.angular.axis-group').style({
73269 'pointer-events': 'none'
73270 });
73271 svg.select('.radial.axis-group').style({
73272 'pointer-events': 'none'
73273 });
73274 var chartGroup = svg.select('.chart-group');
73275 var lineStyle = {
73276 fill: 'none',
73277 stroke: axisConfig.tickColor
73278 };
73279 var fontStyle = {
73280 'font-size': axisConfig.font.size,
73281 'font-family': axisConfig.font.family,
73282 fill: axisConfig.font.color,
73283 'text-shadow': [ '-1px 0px', '1px -1px', '-1px 1px', '1px 1px' ].map(function(d, i) {
73284 return ' ' + d + ' 0 ' + axisConfig.font.outlineColor;
73285 }).join(',')
73286 };
73287 var legendContainer;
73288 if (axisConfig.showLegend) {
73289 legendContainer = svg.select('.legend-group').attr({
73290 transform: 'translate(' + [ radius, axisConfig.margin.top ] + ')'
73291 }).style({
73292 display: 'block'
73293 });
73294 var elements = data.map(function(d, i) {
73295 var datumClone = µ.util.cloneJson(d);
73296 datumClone.symbol = d.geometry === 'DotPlot' ? d.dotType || 'circle' : d.geometry != 'LinePlot' ? 'square' : 'line';
73297 datumClone.visibleInLegend = typeof d.visibleInLegend === 'undefined' || d.visibleInLegend;
73298 datumClone.color = d.geometry === 'LinePlot' ? d.strokeColor : d.color;
73299 return datumClone;
73300 });
73301
73302 µ.Legend().config({
73303 data: data.map(function(d, i) {
73304 return d.name || 'Element' + i;
73305 }),
73306 legendConfig: extendDeepAll({},
73307 µ.Legend.defaultConfig().legendConfig,
73308 {
73309 container: legendContainer,
73310 elements: elements,
73311 reverseOrder: axisConfig.legend.reverseOrder
73312 }
73313 )
73314 })();
73315
73316 var legendBBox = legendContainer.node().getBBox();
73317 radius = Math.min(axisConfig.width - legendBBox.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
73318 radius = Math.max(10, radius);
73319 chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
73320 radialScale.range([ 0, radius ]);
73321 liveConfig.layout.radialAxis.domain = radialScale.domain();
73322 legendContainer.attr('transform', 'translate(' + [ chartCenter[0] + radius, chartCenter[1] - radius ] + ')');
73323 } else {
73324 legendContainer = svg.select('.legend-group').style({
73325 display: 'none'
73326 });
73327 }
73328 svg.attr({
73329 width: axisConfig.width,
73330 height: axisConfig.height
73331 }).style({
73332 opacity: axisConfig.opacity
73333 });
73334 chartGroup.attr('transform', 'translate(' + chartCenter + ')').style({
73335 cursor: 'crosshair'
73336 });
73337 var centeringOffset = [ (axisConfig.width - (axisConfig.margin.left + axisConfig.margin.right + radius * 2 + (legendBBox ? legendBBox.width : 0))) / 2, (axisConfig.height - (axisConfig.margin.top + axisConfig.margin.bottom + radius * 2)) / 2 ];
73338 centeringOffset[0] = Math.max(0, centeringOffset[0]);
73339 centeringOffset[1] = Math.max(0, centeringOffset[1]);
73340 svg.select('.outer-group').attr('transform', 'translate(' + centeringOffset + ')');
73341 if (axisConfig.title && axisConfig.title.text) {
73342 var title = svg.select('g.title-group text').style(fontStyle).text(axisConfig.title.text);
73343 var titleBBox = title.node().getBBox();
73344 title.attr({
73345 x: chartCenter[0] - titleBBox.width / 2,
73346 y: chartCenter[1] - radius - 20
73347 });
73348 }
73349 var radialAxis = svg.select('.radial.axis-group');
73350 if (axisConfig.radialAxis.gridLinesVisible) {
73351 var gridCircles = radialAxis.selectAll('circle.grid-circle').data(radialScale.ticks(5));
73352 gridCircles.enter().append('circle').attr({
73353 'class': 'grid-circle'
73354 }).style(lineStyle);
73355 gridCircles.attr('r', radialScale);
73356 gridCircles.exit().remove();
73357 }
73358 radialAxis.select('circle.outside-circle').attr({
73359 r: radius
73360 }).style(lineStyle);
73361 var backgroundCircle = svg.select('circle.background-circle').attr({
73362 r: radius
73363 }).style({
73364 fill: axisConfig.backgroundColor,
73365 stroke: axisConfig.stroke
73366 });
73367 function currentAngle(d, i) {
73368 return angularScale(d) % 360 + axisConfig.orientation;
73369 }
73370 if (axisConfig.radialAxis.visible) {
73371 var axis = d3.svg.axis().scale(radialScale).ticks(5).tickSize(5);
73372 radialAxis.call(axis).attr({
73373 transform: 'rotate(' + axisConfig.radialAxis.orientation + ')'
73374 });
73375 radialAxis.selectAll('.domain').style(lineStyle);
73376 radialAxis.selectAll('g>text').text(function(d, i) {
73377 return this.textContent + axisConfig.radialAxis.ticksSuffix;
73378 }).style(fontStyle).style({
73379 'text-anchor': 'start'
73380 }).attr({
73381 x: 0,
73382 y: 0,
73383 dx: 0,
73384 dy: 0,
73385 transform: function(d, i) {
73386 if (axisConfig.radialAxis.tickOrientation === 'horizontal') {
73387 return 'rotate(' + -axisConfig.radialAxis.orientation + ') translate(' + [ 0, fontStyle['font-size'] ] + ')';
73388 } else return 'translate(' + [ 0, fontStyle['font-size'] ] + ')';
73389 }
73390 });
73391 radialAxis.selectAll('g>line').style({
73392 stroke: 'black'
73393 });
73394 }
73395 var angularAxis = svg.select('.angular.axis-group').selectAll('g.angular-tick').data(angularAxisRange);
73396 var angularAxisEnter = angularAxis.enter().append('g').classed('angular-tick', true);
73397 angularAxis.attr({
73398 transform: function(d, i) {
73399 return 'rotate(' + currentAngle(d, i) + ')';
73400 }
73401 }).style({
73402 display: axisConfig.angularAxis.visible ? 'block' : 'none'
73403 });
73404 angularAxis.exit().remove();
73405 angularAxisEnter.append('line').classed('grid-line', true).classed('major', function(d, i) {
73406 return i % (axisConfig.minorTicks + 1) == 0;
73407 }).classed('minor', function(d, i) {
73408 return !(i % (axisConfig.minorTicks + 1) == 0);
73409 }).style(lineStyle);
73410 angularAxisEnter.selectAll('.minor').style({
73411 stroke: axisConfig.minorTickColor
73412 });
73413 angularAxis.select('line.grid-line').attr({
73414 x1: axisConfig.tickLength ? radius - axisConfig.tickLength : 0,
73415 x2: radius
73416 }).style({
73417 display: axisConfig.angularAxis.gridLinesVisible ? 'block' : 'none'
73418 });
73419 angularAxisEnter.append('text').classed('axis-text', true).style(fontStyle);
73420 var ticksText = angularAxis.select('text.axis-text').attr({
73421 x: radius + axisConfig.labelOffset,
73422 dy: MID_SHIFT + 'em',
73423 transform: function(d, i) {
73424 var angle = currentAngle(d, i);
73425 var rad = radius + axisConfig.labelOffset;
73426 var orient = axisConfig.angularAxis.tickOrientation;
73427 if (orient == 'horizontal') return 'rotate(' + -angle + ' ' + rad + ' 0)'; else if (orient == 'radial') return angle < 270 && angle > 90 ? 'rotate(180 ' + rad + ' 0)' : null; else return 'rotate(' + (angle <= 180 && angle > 0 ? -90 : 90) + ' ' + rad + ' 0)';
73428 }
73429 }).style({
73430 'text-anchor': 'middle',
73431 display: axisConfig.angularAxis.labelsVisible ? 'block' : 'none'
73432 }).text(function(d, i) {
73433 if (i % (axisConfig.minorTicks + 1) != 0) return '';
73434 if (ticks) {
73435 return ticks[d] + axisConfig.angularAxis.ticksSuffix;
73436 } else return d + axisConfig.angularAxis.ticksSuffix;
73437 }).style(fontStyle);
73438 if (axisConfig.angularAxis.rewriteTicks) ticksText.text(function(d, i) {
73439 if (i % (axisConfig.minorTicks + 1) != 0) return '';
73440 return axisConfig.angularAxis.rewriteTicks(this.textContent, i);
73441 });
73442 var rightmostTickEndX = d3.max(chartGroup.selectAll('.angular-tick text')[0].map(function(d, i) {
73443 return d.getCTM().e + d.getBBox().width;
73444 }));
73445 legendContainer.attr({
73446 transform: 'translate(' + [ radius + rightmostTickEndX, axisConfig.margin.top ] + ')'
73447 });
73448 var hasGeometry = svg.select('g.geometry-group').selectAll('g').size() > 0;
73449 var geometryContainer = svg.select('g.geometry-group').selectAll('g.geometry').data(data);
73450 geometryContainer.enter().append('g').attr({
73451 'class': function(d, i) {
73452 return 'geometry geometry' + i;
73453 }
73454 });
73455 geometryContainer.exit().remove();
73456 if (data[0] || hasGeometry) {
73457 var geometryConfigs = [];
73458 data.forEach(function(d, i) {
73459 var geometryConfig = {};
73460 geometryConfig.radialScale = radialScale;
73461 geometryConfig.angularScale = angularScale;
73462 geometryConfig.container = geometryContainer.filter(function(dB, iB) {
73463 return iB == i;
73464 });
73465 geometryConfig.geometry = d.geometry;
73466 geometryConfig.orientation = axisConfig.orientation;
73467 geometryConfig.direction = axisConfig.direction;
73468 geometryConfig.index = i;
73469 geometryConfigs.push({
73470 data: d,
73471 geometryConfig: geometryConfig
73472 });
73473 });
73474 var geometryConfigsGrouped = d3.nest().key(function(d, i) {
73475 return typeof d.data.groupId != 'undefined' || 'unstacked';
73476 }).entries(geometryConfigs);
73477 var geometryConfigsGrouped2 = [];
73478 geometryConfigsGrouped.forEach(function(d, i) {
73479 if (d.key === 'unstacked') geometryConfigsGrouped2 = geometryConfigsGrouped2.concat(d.values.map(function(d, i) {
73480 return [ d ];
73481 })); else geometryConfigsGrouped2.push(d.values);
73482 });
73483 geometryConfigsGrouped2.forEach(function(d, i) {
73484 var geometry;
73485 if (Array.isArray(d)) geometry = d[0].geometryConfig.geometry; else geometry = d.geometryConfig.geometry;
73486 var finalGeometryConfig = d.map(function(dB, iB) {
73487 return extendDeepAll(µ[geometry].defaultConfig(), dB);
73488 });
73489 µ[geometry]().config(finalGeometryConfig)();
73490 });
73491 }
73492 var guides = svg.select('.guides-group');
73493 var tooltipContainer = svg.select('.tooltips-group');
73494 var angularTooltip = µ.tooltipPanel().config({
73495 container: tooltipContainer,
73496 fontSize: 8
73497 })();
73498 var radialTooltip = µ.tooltipPanel().config({
73499 container: tooltipContainer,
73500 fontSize: 8
73501 })();
73502 var geometryTooltip = µ.tooltipPanel().config({
73503 container: tooltipContainer,
73504 hasTick: true
73505 })();
73506 var angularValue, radialValue;
73507 if (!isOrdinal) {
73508 var angularGuideLine = guides.select('line').attr({
73509 x1: 0,
73510 y1: 0,
73511 y2: 0
73512 }).style({
73513 stroke: 'grey',
73514 'pointer-events': 'none'
73515 });
73516 chartGroup.on('mousemove.angular-guide', function(d, i) {
73517 var mouseAngle = µ.util.getMousePos(backgroundCircle).angle;
73518 angularGuideLine.attr({
73519 x2: -radius,
73520 transform: 'rotate(' + mouseAngle + ')'
73521 }).style({
73522 opacity: .5
73523 });
73524 var angleWithOriginOffset = (mouseAngle + 180 + 360 - axisConfig.orientation) % 360;
73525 angularValue = angularScale.invert(angleWithOriginOffset);
73526 var pos = µ.util.convertToCartesian(radius + 12, mouseAngle + 180);
73527 angularTooltip.text(µ.util.round(angularValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
73528 }).on('mouseout.angular-guide', function(d, i) {
73529 guides.select('line').style({
73530 opacity: 0
73531 });
73532 });
73533 }
73534 var angularGuideCircle = guides.select('circle').style({
73535 stroke: 'grey',
73536 fill: 'none'
73537 });
73538 chartGroup.on('mousemove.radial-guide', function(d, i) {
73539 var r = µ.util.getMousePos(backgroundCircle).radius;
73540 angularGuideCircle.attr({
73541 r: r
73542 }).style({
73543 opacity: .5
73544 });
73545 radialValue = radialScale.invert(µ.util.getMousePos(backgroundCircle).radius);
73546 var pos = µ.util.convertToCartesian(r, axisConfig.radialAxis.orientation);
73547 radialTooltip.text(µ.util.round(radialValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
73548 }).on('mouseout.radial-guide', function(d, i) {
73549 angularGuideCircle.style({
73550 opacity: 0
73551 });
73552 geometryTooltip.hide();
73553 angularTooltip.hide();
73554 radialTooltip.hide();
73555 });
73556 svg.selectAll('.geometry-group .mark').on('mouseover.tooltip', function(d, i) {
73557 var el = d3.select(this);
73558 var color = this.style.fill;
73559 var newColor = 'black';
73560 var opacity = this.style.opacity || 1;
73561 el.attr({
73562 'data-opacity': opacity
73563 });
73564 if (color && color !== 'none') {
73565 el.attr({
73566 'data-fill': color
73567 });
73568 newColor = d3.hsl(color).darker().toString();
73569 el.style({
73570 fill: newColor,
73571 opacity: 1
73572 });
73573 var textData = {
73574 t: µ.util.round(d[0]),
73575 r: µ.util.round(d[1])
73576 };
73577 if (isOrdinal) textData.t = ticks[d[0]];
73578 var text = 't: ' + textData.t + ', r: ' + textData.r;
73579 var bbox = this.getBoundingClientRect();
73580 var svgBBox = svg.node().getBoundingClientRect();
73581 var pos = [ bbox.left + bbox.width / 2 - centeringOffset[0] - svgBBox.left, bbox.top + bbox.height / 2 - centeringOffset[1] - svgBBox.top ];
73582 geometryTooltip.config({
73583 color: newColor
73584 }).text(text);
73585 geometryTooltip.move(pos);
73586 } else {
73587 color = this.style.stroke || 'black';
73588 el.attr({
73589 'data-stroke': color
73590 });
73591 newColor = d3.hsl(color).darker().toString();
73592 el.style({
73593 stroke: newColor,
73594 opacity: 1
73595 });
73596 }
73597 }).on('mousemove.tooltip', function(d, i) {
73598 if (d3.event.which != 0) return false;
73599 if (d3.select(this).attr('data-fill')) geometryTooltip.show();
73600 }).on('mouseout.tooltip', function(d, i) {
73601 geometryTooltip.hide();
73602 var el = d3.select(this);
73603 var fillColor = el.attr('data-fill');
73604 if (fillColor) el.style({
73605 fill: fillColor,
73606 opacity: el.attr('data-opacity')
73607 }); else el.style({
73608 stroke: el.attr('data-stroke'),
73609 opacity: el.attr('data-opacity')
73610 });
73611 });
73612 });
73613 return exports;
73614 }
73615 exports.render = function(_container) {
73616 render(_container);
73617 return this;
73618 };
73619 exports.config = function(_x) {
73620 if (!arguments.length) return config;
73621 var xClone = µ.util.cloneJson(_x);
73622 xClone.data.forEach(function(d, i) {
73623 if (!config.data[i]) config.data[i] = {};
73624 extendDeepAll(config.data[i], µ.Axis.defaultConfig().data[0]);
73625 extendDeepAll(config.data[i], d);
73626 });
73627 extendDeepAll(config.layout, µ.Axis.defaultConfig().layout);
73628 extendDeepAll(config.layout, xClone.layout);
73629 return this;
73630 };
73631 exports.getLiveConfig = function() {
73632 return liveConfig;
73633 };
73634 exports.getinputConfig = function() {
73635 return inputConfig;
73636 };
73637 exports.radialScale = function(_x) {
73638 return radialScale;
73639 };
73640 exports.angularScale = function(_x) {
73641 return angularScale;
73642 };
73643 exports.svg = function() {
73644 return svg;
73645 };
73646 d3.rebind(exports, dispatch, 'on');
73647 return exports;
73648};
73649
73650µ.Axis.defaultConfig = function(d, i) {
73651 var config = {
73652 data: [ {
73653 t: [ 1, 2, 3, 4 ],
73654 r: [ 10, 11, 12, 13 ],
73655 name: 'Line1',
73656 geometry: 'LinePlot',
73657 color: null,
73658 strokeDash: 'solid',
73659 strokeColor: null,
73660 strokeSize: '1',
73661 visibleInLegend: true,
73662 opacity: 1
73663 } ],
73664 layout: {
73665 defaultColorRange: d3.scale.category10().range(),
73666 title: null,
73667 height: 450,
73668 width: 500,
73669 margin: {
73670 top: 40,
73671 right: 40,
73672 bottom: 40,
73673 left: 40
73674 },
73675 font: {
73676 size: 12,
73677 color: 'gray',
73678 outlineColor: 'white',
73679 family: 'Tahoma, sans-serif'
73680 },
73681 direction: 'clockwise',
73682 orientation: 0,
73683 labelOffset: 10,
73684 radialAxis: {
73685 domain: null,
73686 orientation: -45,
73687 ticksSuffix: '',
73688 visible: true,
73689 gridLinesVisible: true,
73690 tickOrientation: 'horizontal',
73691 rewriteTicks: null
73692 },
73693 angularAxis: {
73694 domain: [ 0, 360 ],
73695 ticksSuffix: '',
73696 visible: true,
73697 gridLinesVisible: true,
73698 labelsVisible: true,
73699 tickOrientation: 'horizontal',
73700 rewriteTicks: null,
73701 ticksCount: null,
73702 ticksStep: null
73703 },
73704 minorTicks: 0,
73705 tickLength: null,
73706 tickColor: 'silver',
73707 minorTickColor: '#eee',
73708 backgroundColor: 'none',
73709 needsEndSpacing: null,
73710 showLegend: true,
73711 legend: {
73712 reverseOrder: false
73713 },
73714 opacity: 1
73715 }
73716 };
73717 return config;
73718};
73719
73720µ.util = {};
73721
73722µ.DATAEXTENT = 'dataExtent';
73723
73724µ.AREA = 'AreaChart';
73725
73726µ.LINE = 'LinePlot';
73727
73728µ.DOT = 'DotPlot';
73729
73730µ.BAR = 'BarChart';
73731
73732µ.util._override = function(_objA, _objB) {
73733 for (var x in _objA) if (x in _objB) _objB[x] = _objA[x];
73734};
73735
73736µ.util._extend = function(_objA, _objB) {
73737 for (var x in _objA) _objB[x] = _objA[x];
73738};
73739
73740µ.util._rndSnd = function() {
73741 return Math.random() * 2 - 1 + (Math.random() * 2 - 1) + (Math.random() * 2 - 1);
73742};
73743
73744µ.util.dataFromEquation2 = function(_equation, _step) {
73745 var step = _step || 6;
73746 var data = d3.range(0, 360 + step, step).map(function(deg, index) {
73747 var theta = deg * Math.PI / 180;
73748 var radius = _equation(theta);
73749 return [ deg, radius ];
73750 });
73751 return data;
73752};
73753
73754µ.util.dataFromEquation = function(_equation, _step, _name) {
73755 var step = _step || 6;
73756 var t = [], r = [];
73757 d3.range(0, 360 + step, step).forEach(function(deg, index) {
73758 var theta = deg * Math.PI / 180;
73759 var radius = _equation(theta);
73760 t.push(deg);
73761 r.push(radius);
73762 });
73763 var result = {
73764 t: t,
73765 r: r
73766 };
73767 if (_name) result.name = _name;
73768 return result;
73769};
73770
73771µ.util.ensureArray = function(_val, _count) {
73772 if (typeof _val === 'undefined') return null;
73773 var arr = [].concat(_val);
73774 return d3.range(_count).map(function(d, i) {
73775 return arr[i] || arr[0];
73776 });
73777};
73778
73779µ.util.fillArrays = function(_obj, _valueNames, _count) {
73780 _valueNames.forEach(function(d, i) {
73781 _obj[d] = µ.util.ensureArray(_obj[d], _count);
73782 });
73783 return _obj;
73784};
73785
73786µ.util.cloneJson = function(json) {
73787 return JSON.parse(JSON.stringify(json));
73788};
73789
73790µ.util.validateKeys = function(obj, keys) {
73791 if (typeof keys === 'string') keys = keys.split('.');
73792 var next = keys.shift();
73793 return obj[next] && (!keys.length || objHasKeys(obj[next], keys));
73794};
73795
73796µ.util.sumArrays = function(a, b) {
73797 return d3.zip(a, b).map(function(d, i) {
73798 return d3.sum(d);
73799 });
73800};
73801
73802µ.util.arrayLast = function(a) {
73803 return a[a.length - 1];
73804};
73805
73806µ.util.arrayEqual = function(a, b) {
73807 var i = Math.max(a.length, b.length, 1);
73808 while (i-- >= 0 && a[i] === b[i]) ;
73809 return i === -2;
73810};
73811
73812µ.util.flattenArray = function(arr) {
73813 var r = [];
73814 while (!µ.util.arrayEqual(r, arr)) {
73815 r = arr;
73816 arr = [].concat.apply([], arr);
73817 }
73818 return arr;
73819};
73820
73821µ.util.deduplicate = function(arr) {
73822 return arr.filter(function(v, i, a) {
73823 return a.indexOf(v) == i;
73824 });
73825};
73826
73827µ.util.convertToCartesian = function(radius, theta) {
73828 var thetaRadians = theta * Math.PI / 180;
73829 var x = radius * Math.cos(thetaRadians);
73830 var y = radius * Math.sin(thetaRadians);
73831 return [ x, y ];
73832};
73833
73834µ.util.round = function(_value, _digits) {
73835 var digits = _digits || 2;
73836 var mult = Math.pow(10, digits);
73837 return Math.round(_value * mult) / mult;
73838};
73839
73840µ.util.getMousePos = function(_referenceElement) {
73841 var mousePos = d3.mouse(_referenceElement.node());
73842 var mouseX = mousePos[0];
73843 var mouseY = mousePos[1];
73844 var mouse = {};
73845 mouse.x = mouseX;
73846 mouse.y = mouseY;
73847 mouse.pos = mousePos;
73848 mouse.angle = (Math.atan2(mouseY, mouseX) + Math.PI) * 180 / Math.PI;
73849 mouse.radius = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
73850 return mouse;
73851};
73852
73853µ.util.duplicatesCount = function(arr) {
73854 var uniques = {}, val;
73855 var dups = {};
73856 for (var i = 0, len = arr.length; i < len; i++) {
73857 val = arr[i];
73858 if (val in uniques) {
73859 uniques[val]++;
73860 dups[val] = uniques[val];
73861 } else {
73862 uniques[val] = 1;
73863 }
73864 }
73865 return dups;
73866};
73867
73868µ.util.duplicates = function(arr) {
73869 return Object.keys(µ.util.duplicatesCount(arr));
73870};
73871
73872µ.util.translator = function(obj, sourceBranch, targetBranch, reverse) {
73873 if (reverse) {
73874 var targetBranchCopy = targetBranch.slice();
73875 targetBranch = sourceBranch;
73876 sourceBranch = targetBranchCopy;
73877 }
73878 var value = sourceBranch.reduce(function(previousValue, currentValue) {
73879 if (typeof previousValue != 'undefined') return previousValue[currentValue];
73880 }, obj);
73881 if (typeof value === 'undefined') return;
73882 sourceBranch.reduce(function(previousValue, currentValue, index) {
73883 if (typeof previousValue == 'undefined') return;
73884 if (index === sourceBranch.length - 1) delete previousValue[currentValue];
73885 return previousValue[currentValue];
73886 }, obj);
73887 targetBranch.reduce(function(previousValue, currentValue, index) {
73888 if (typeof previousValue[currentValue] === 'undefined') previousValue[currentValue] = {};
73889 if (index === targetBranch.length - 1) previousValue[currentValue] = value;
73890 return previousValue[currentValue];
73891 }, obj);
73892};
73893
73894µ.PolyChart = function module() {
73895 var config = [ µ.PolyChart.defaultConfig() ];
73896 var dispatch = d3.dispatch('hover');
73897 var dashArray = {
73898 solid: 'none',
73899 dash: [ 5, 2 ],
73900 dot: [ 2, 5 ]
73901 };
73902 var colorScale;
73903 function exports() {
73904 var geometryConfig = config[0].geometryConfig;
73905 var container = geometryConfig.container;
73906 if (typeof container == 'string') container = d3.select(container);
73907 container.datum(config).each(function(_config, _index) {
73908 var isStack = !!_config[0].data.yStack;
73909 var data = _config.map(function(d, i) {
73910 if (isStack) return d3.zip(d.data.t[0], d.data.r[0], d.data.yStack[0]); else return d3.zip(d.data.t[0], d.data.r[0]);
73911 });
73912 var angularScale = geometryConfig.angularScale;
73913 var domainMin = geometryConfig.radialScale.domain()[0];
73914 var generator = {};
73915 generator.bar = function(d, i, pI) {
73916 var dataConfig = _config[pI].data;
73917 var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0);
73918 var stackTop = geometryConfig.radialScale(d[2] || 0);
73919 var w = dataConfig.barWidth;
73920 d3.select(this).attr({
73921 'class': 'mark bar',
73922 d: 'M' + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join('L') + 'Z',
73923 transform: function(d, i) {
73924 return 'rotate(' + (geometryConfig.orientation + angularScale(d[0])) + ')';
73925 }
73926 });
73927 };
73928 generator.dot = function(d, i, pI) {
73929 var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d;
73930 var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i);
73931 d3.select(this).attr({
73932 'class': 'mark dot',
73933 d: symbol,
73934 transform: function(d, i) {
73935 var coord = convertToCartesian(getPolarCoordinates(stackedData));
73936 return 'translate(' + [ coord.x, coord.y ] + ')';
73937 }
73938 });
73939 };
73940 var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) {
73941 return geometryConfig.radialScale(d[1]);
73942 }).angle(function(d) {
73943 return geometryConfig.angularScale(d[0]) * Math.PI / 180;
73944 });
73945 generator.line = function(d, i, pI) {
73946 var lineData = d[2] ? data[pI].map(function(d, i) {
73947 return [ d[0], d[1] + d[2] ];
73948 }) : data[pI];
73949 d3.select(this).each(generator['dot']).style({
73950 opacity: function(dB, iB) {
73951 return +_config[pI].data.dotVisible;
73952 },
73953 fill: markStyle.stroke(d, i, pI)
73954 }).attr({
73955 'class': 'mark dot'
73956 });
73957 if (i > 0) return;
73958 var lineSelection = d3.select(this.parentNode).selectAll('path.line').data([ 0 ]);
73959 lineSelection.enter().insert('path');
73960 lineSelection.attr({
73961 'class': 'line',
73962 d: line(lineData),
73963 transform: function(dB, iB) {
73964 return 'rotate(' + (geometryConfig.orientation + 90) + ')';
73965 },
73966 'pointer-events': 'none'
73967 }).style({
73968 fill: function(dB, iB) {
73969 return markStyle.fill(d, i, pI);
73970 },
73971 'fill-opacity': 0,
73972 stroke: function(dB, iB) {
73973 return markStyle.stroke(d, i, pI);
73974 },
73975 'stroke-width': function(dB, iB) {
73976 return markStyle['stroke-width'](d, i, pI);
73977 },
73978 'stroke-dasharray': function(dB, iB) {
73979 return markStyle['stroke-dasharray'](d, i, pI);
73980 },
73981 opacity: function(dB, iB) {
73982 return markStyle.opacity(d, i, pI);
73983 },
73984 display: function(dB, iB) {
73985 return markStyle.display(d, i, pI);
73986 }
73987 });
73988 };
73989 var angularRange = geometryConfig.angularScale.range();
73990 var triangleAngle = Math.abs(angularRange[1] - angularRange[0]) / data[0].length * Math.PI / 180;
73991 var arc = d3.svg.arc().startAngle(function(d) {
73992 return -triangleAngle / 2;
73993 }).endAngle(function(d) {
73994 return triangleAngle / 2;
73995 }).innerRadius(function(d) {
73996 return geometryConfig.radialScale(domainMin + (d[2] || 0));
73997 }).outerRadius(function(d) {
73998 return geometryConfig.radialScale(domainMin + (d[2] || 0)) + geometryConfig.radialScale(d[1]);
73999 });
74000 generator.arc = function(d, i, pI) {
74001 d3.select(this).attr({
74002 'class': 'mark arc',
74003 d: arc,
74004 transform: function(d, i) {
74005 return 'rotate(' + (geometryConfig.orientation + angularScale(d[0]) + 90) + ')';
74006 }
74007 });
74008 };
74009 var markStyle = {
74010 fill: function(d, i, pI) {
74011 return _config[pI].data.color;
74012 },
74013 stroke: function(d, i, pI) {
74014 return _config[pI].data.strokeColor;
74015 },
74016 'stroke-width': function(d, i, pI) {
74017 return _config[pI].data.strokeSize + 'px';
74018 },
74019 'stroke-dasharray': function(d, i, pI) {
74020 return dashArray[_config[pI].data.strokeDash];
74021 },
74022 opacity: function(d, i, pI) {
74023 return _config[pI].data.opacity;
74024 },
74025 display: function(d, i, pI) {
74026 return typeof _config[pI].data.visible === 'undefined' || _config[pI].data.visible ? 'block' : 'none';
74027 }
74028 };
74029 var geometryLayer = d3.select(this).selectAll('g.layer').data(data);
74030 geometryLayer.enter().append('g').attr({
74031 'class': 'layer'
74032 });
74033 var geometry = geometryLayer.selectAll('path.mark').data(function(d, i) {
74034 return d;
74035 });
74036 geometry.enter().append('path').attr({
74037 'class': 'mark'
74038 });
74039 geometry.style(markStyle).each(generator[geometryConfig.geometryType]);
74040 geometry.exit().remove();
74041 geometryLayer.exit().remove();
74042 function getPolarCoordinates(d, i) {
74043 var r = geometryConfig.radialScale(d[1]);
74044 var t = (geometryConfig.angularScale(d[0]) + geometryConfig.orientation) * Math.PI / 180;
74045 return {
74046 r: r,
74047 t: t
74048 };
74049 }
74050 function convertToCartesian(polarCoordinates) {
74051 var x = polarCoordinates.r * Math.cos(polarCoordinates.t);
74052 var y = polarCoordinates.r * Math.sin(polarCoordinates.t);
74053 return {
74054 x: x,
74055 y: y
74056 };
74057 }
74058 });
74059 }
74060 exports.config = function(_x) {
74061 if (!arguments.length) return config;
74062 _x.forEach(function(d, i) {
74063 if (!config[i]) config[i] = {};
74064 extendDeepAll(config[i], µ.PolyChart.defaultConfig());
74065 extendDeepAll(config[i], d);
74066 });
74067 return this;
74068 };
74069 exports.getColorScale = function() {
74070 return colorScale;
74071 };
74072 d3.rebind(exports, dispatch, 'on');
74073 return exports;
74074};
74075
74076µ.PolyChart.defaultConfig = function() {
74077 var config = {
74078 data: {
74079 name: 'geom1',
74080 t: [ [ 1, 2, 3, 4 ] ],
74081 r: [ [ 1, 2, 3, 4 ] ],
74082 dotType: 'circle',
74083 dotSize: 64,
74084 dotVisible: false,
74085 barWidth: 20,
74086 color: '#ffa500',
74087 strokeSize: 1,
74088 strokeColor: 'silver',
74089 strokeDash: 'solid',
74090 opacity: 1,
74091 index: 0,
74092 visible: true,
74093 visibleInLegend: true
74094 },
74095 geometryConfig: {
74096 geometry: 'LinePlot',
74097 geometryType: 'arc',
74098 direction: 'clockwise',
74099 orientation: 0,
74100 container: 'body',
74101 radialScale: null,
74102 angularScale: null,
74103 colorScale: d3.scale.category20()
74104 }
74105 };
74106 return config;
74107};
74108
74109µ.BarChart = function module() {
74110 return µ.PolyChart();
74111};
74112
74113µ.BarChart.defaultConfig = function() {
74114 var config = {
74115 geometryConfig: {
74116 geometryType: 'bar'
74117 }
74118 };
74119 return config;
74120};
74121
74122µ.AreaChart = function module() {
74123 return µ.PolyChart();
74124};
74125
74126µ.AreaChart.defaultConfig = function() {
74127 var config = {
74128 geometryConfig: {
74129 geometryType: 'arc'
74130 }
74131 };
74132 return config;
74133};
74134
74135µ.DotPlot = function module() {
74136 return µ.PolyChart();
74137};
74138
74139µ.DotPlot.defaultConfig = function() {
74140 var config = {
74141 geometryConfig: {
74142 geometryType: 'dot',
74143 dotType: 'circle'
74144 }
74145 };
74146 return config;
74147};
74148
74149µ.LinePlot = function module() {
74150 return µ.PolyChart();
74151};
74152
74153µ.LinePlot.defaultConfig = function() {
74154 var config = {
74155 geometryConfig: {
74156 geometryType: 'line'
74157 }
74158 };
74159 return config;
74160};
74161
74162µ.Legend = function module() {
74163 var config = µ.Legend.defaultConfig();
74164 var dispatch = d3.dispatch('hover');
74165 function exports() {
74166 var legendConfig = config.legendConfig;
74167 var flattenData = config.data.map(function(d, i) {
74168 return [].concat(d).map(function(dB, iB) {
74169 var element = extendDeepAll({}, legendConfig.elements[i]);
74170 element.name = dB;
74171 element.color = [].concat(legendConfig.elements[i].color)[iB];
74172 return element;
74173 });
74174 });
74175 var data = d3.merge(flattenData);
74176 data = data.filter(function(d, i) {
74177 return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend === 'undefined');
74178 });
74179 if (legendConfig.reverseOrder) data = data.reverse();
74180 var container = legendConfig.container;
74181 if (typeof container == 'string' || container.nodeName) container = d3.select(container);
74182 var colors = data.map(function(d, i) {
74183 return d.color;
74184 });
74185 var lineHeight = legendConfig.fontSize;
74186 var isContinuous = legendConfig.isContinuous == null ? typeof data[0] === 'number' : legendConfig.isContinuous;
74187 var height = isContinuous ? legendConfig.height : lineHeight * data.length;
74188 var legendContainerGroup = container.classed('legend-group', true);
74189 var svg = legendContainerGroup.selectAll('svg').data([ 0 ]);
74190 var svgEnter = svg.enter().append('svg').attr({
74191 width: 300,
74192 height: height + lineHeight,
74193 xmlns: 'http://www.w3.org/2000/svg',
74194 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
74195 version: '1.1'
74196 });
74197 svgEnter.append('g').classed('legend-axis', true);
74198 svgEnter.append('g').classed('legend-marks', true);
74199 var dataNumbered = d3.range(data.length);
74200 var colorScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered).range(colors);
74201 var dataScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered)[isContinuous ? 'range' : 'rangePoints']([ 0, height ]);
74202 var shapeGenerator = function(_type, _size) {
74203 var squareSize = _size * 3;
74204 if (_type === 'line') {
74205 return 'M' + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2, _size / 12 ] ] + 'Z';
74206 } else if (d3.svg.symbolTypes.indexOf(_type) != -1) return d3.svg.symbol().type(_type).size(squareSize)(); else return d3.svg.symbol().type('square').size(squareSize)();
74207 };
74208 if (isContinuous) {
74209 var gradient = svg.select('.legend-marks').append('defs').append('linearGradient').attr({
74210 id: 'grad1',
74211 x1: '0%',
74212 y1: '0%',
74213 x2: '0%',
74214 y2: '100%'
74215 }).selectAll('stop').data(colors);
74216 gradient.enter().append('stop');
74217 gradient.attr({
74218 offset: function(d, i) {
74219 return i / (colors.length - 1) * 100 + '%';
74220 }
74221 }).style({
74222 'stop-color': function(d, i) {
74223 return d;
74224 }
74225 });
74226 svg.append('rect').classed('legend-mark', true).attr({
74227 height: legendConfig.height,
74228 width: legendConfig.colorBandWidth,
74229 fill: 'url(#grad1)'
74230 });
74231 } else {
74232 var legendElement = svg.select('.legend-marks').selectAll('path.legend-mark').data(data);
74233 legendElement.enter().append('path').classed('legend-mark', true);
74234 legendElement.attr({
74235 transform: function(d, i) {
74236 return 'translate(' + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ')';
74237 },
74238 d: function(d, i) {
74239 var symbolType = d.symbol;
74240 return shapeGenerator(symbolType, lineHeight);
74241 },
74242 fill: function(d, i) {
74243 return colorScale(i);
74244 }
74245 });
74246 legendElement.exit().remove();
74247 }
74248 var legendAxis = d3.svg.axis().scale(dataScale).orient('right');
74249 var axis = svg.select('g.legend-axis').attr({
74250 transform: 'translate(' + [ isContinuous ? legendConfig.colorBandWidth : lineHeight, lineHeight / 2 ] + ')'
74251 }).call(legendAxis);
74252 axis.selectAll('.domain').style({
74253 fill: 'none',
74254 stroke: 'none'
74255 });
74256 axis.selectAll('line').style({
74257 fill: 'none',
74258 stroke: isContinuous ? legendConfig.textColor : 'none'
74259 });
74260 axis.selectAll('text').style({
74261 fill: legendConfig.textColor,
74262 'font-size': legendConfig.fontSize
74263 }).text(function(d, i) {
74264 return data[i].name;
74265 });
74266 return exports;
74267 }
74268 exports.config = function(_x) {
74269 if (!arguments.length) return config;
74270 extendDeepAll(config, _x);
74271 return this;
74272 };
74273 d3.rebind(exports, dispatch, 'on');
74274 return exports;
74275};
74276
74277µ.Legend.defaultConfig = function(d, i) {
74278 var config = {
74279 data: [ 'a', 'b', 'c' ],
74280 legendConfig: {
74281 elements: [ {
74282 symbol: 'line',
74283 color: 'red'
74284 }, {
74285 symbol: 'square',
74286 color: 'yellow'
74287 }, {
74288 symbol: 'diamond',
74289 color: 'limegreen'
74290 } ],
74291 height: 150,
74292 colorBandWidth: 30,
74293 fontSize: 12,
74294 container: 'body',
74295 isContinuous: null,
74296 textColor: 'grey',
74297 reverseOrder: false
74298 }
74299 };
74300 return config;
74301};
74302
74303µ.tooltipPanel = function() {
74304 var tooltipEl, tooltipTextEl, backgroundEl;
74305 var config = {
74306 container: null,
74307 hasTick: false,
74308 fontSize: 12,
74309 color: 'white',
74310 padding: 5
74311 };
74312 var id = 'tooltip-' + µ.tooltipPanel.uid++;
74313 var tickSize = 10;
74314 var exports = function() {
74315 tooltipEl = config.container.selectAll('g.' + id).data([ 0 ]);
74316 var tooltipEnter = tooltipEl.enter().append('g').classed(id, true).style({
74317 'pointer-events': 'none',
74318 display: 'none'
74319 });
74320 backgroundEl = tooltipEnter.append('path').style({
74321 fill: 'white',
74322 'fill-opacity': .9
74323 }).attr({
74324 d: 'M0 0'
74325 });
74326 tooltipTextEl = tooltipEnter.append('text').attr({
74327 dx: config.padding + tickSize,
74328 dy: +config.fontSize * .3
74329 });
74330 return exports;
74331 };
74332 exports.text = function(_text) {
74333 var l = d3.hsl(config.color).l;
74334 var strokeColor = l >= .5 ? '#aaa' : 'white';
74335 var fillColor = l >= .5 ? 'black' : 'white';
74336 var text = _text || '';
74337 tooltipTextEl.style({
74338 fill: fillColor,
74339 'font-size': config.fontSize + 'px'
74340 }).text(text);
74341 var padding = config.padding;
74342 var bbox = tooltipTextEl.node().getBBox();
74343 var boxStyle = {
74344 fill: config.color,
74345 stroke: strokeColor,
74346 'stroke-width': '2px'
74347 };
74348 var backGroundW = bbox.width + padding * 2 + tickSize;
74349 var backGroundH = bbox.height + padding * 2;
74350 backgroundEl.attr({
74351 d: 'M' + [ [ tickSize, -backGroundH / 2 ], [ tickSize, -backGroundH / 4 ], [ config.hasTick ? 0 : tickSize, 0 ], [ tickSize, backGroundH / 4 ], [ tickSize, backGroundH / 2 ], [ backGroundW, backGroundH / 2 ], [ backGroundW, -backGroundH / 2 ] ].join('L') + 'Z'
74352 }).style(boxStyle);
74353 tooltipEl.attr({
74354 transform: 'translate(' + [ tickSize, -backGroundH / 2 + padding * 2 ] + ')'
74355 });
74356 tooltipEl.style({
74357 display: 'block'
74358 });
74359 return exports;
74360 };
74361 exports.move = function(_pos) {
74362 if (!tooltipEl) return;
74363 tooltipEl.attr({
74364 transform: 'translate(' + [ _pos[0], _pos[1] ] + ')'
74365 }).style({
74366 display: 'block'
74367 });
74368 return exports;
74369 };
74370 exports.hide = function() {
74371 if (!tooltipEl) return;
74372 tooltipEl.style({
74373 display: 'none'
74374 });
74375 return exports;
74376 };
74377 exports.show = function() {
74378 if (!tooltipEl) return;
74379 tooltipEl.style({
74380 display: 'block'
74381 });
74382 return exports;
74383 };
74384 exports.config = function(_x) {
74385 extendDeepAll(config, _x);
74386 return exports;
74387 };
74388 return exports;
74389};
74390
74391µ.tooltipPanel.uid = 1;
74392
74393µ.adapter = {};
74394
74395µ.adapter.plotly = function module() {
74396 var exports = {};
74397 exports.convert = function(_inputConfig, reverse) {
74398 var outputConfig = {};
74399 if (_inputConfig.data) {
74400 outputConfig.data = _inputConfig.data.map(function(d, i) {
74401 var r = extendDeepAll({}, d);
74402 var toTranslate = [
74403 [ r, [ 'marker', 'color' ], [ 'color' ] ],
74404 [ r, [ 'marker', 'opacity' ], [ 'opacity' ] ],
74405 [ r, [ 'marker', 'line', 'color' ], [ 'strokeColor' ] ],
74406 [ r, [ 'marker', 'line', 'dash' ], [ 'strokeDash' ] ],
74407 [ r, [ 'marker', 'line', 'width' ], [ 'strokeSize' ] ],
74408 [ r, [ 'marker', 'symbol' ], [ 'dotType' ] ],
74409 [ r, [ 'marker', 'size' ], [ 'dotSize' ] ],
74410 [ r, [ 'marker', 'barWidth' ], [ 'barWidth' ] ],
74411 [ r, [ 'line', 'interpolation' ], [ 'lineInterpolation' ] ],
74412 [ r, [ 'showlegend' ], [ 'visibleInLegend' ] ]
74413 ];
74414 toTranslate.forEach(function(d, i) {
74415 µ.util.translator.apply(null, d.concat(reverse));
74416 });
74417
74418 if (!reverse) delete r.marker;
74419 if (reverse) delete r.groupId;
74420 if (!reverse) {
74421 if (r.type === 'scatter') {
74422 if (r.mode === 'lines') r.geometry = 'LinePlot'; else if (r.mode === 'markers') r.geometry = 'DotPlot'; else if (r.mode === 'lines+markers') {
74423 r.geometry = 'LinePlot';
74424 r.dotVisible = true;
74425 }
74426 } else if (r.type === 'area') r.geometry = 'AreaChart'; else if (r.type === 'bar') r.geometry = 'BarChart';
74427 delete r.mode;
74428 delete r.type;
74429 } else {
74430 if (r.geometry === 'LinePlot') {
74431 r.type = 'scatter';
74432 if (r.dotVisible === true) {
74433 delete r.dotVisible;
74434 r.mode = 'lines+markers';
74435 } else r.mode = 'lines';
74436 } else if (r.geometry === 'DotPlot') {
74437 r.type = 'scatter';
74438 r.mode = 'markers';
74439 } else if (r.geometry === 'AreaChart') r.type = 'area'; else if (r.geometry === 'BarChart') r.type = 'bar';
74440 delete r.geometry;
74441 }
74442 return r;
74443 });
74444 if (!reverse && _inputConfig.layout && _inputConfig.layout.barmode === 'stack') {
74445 var duplicates = µ.util.duplicates(outputConfig.data.map(function(d, i) {
74446 return d.geometry;
74447 }));
74448 outputConfig.data.forEach(function(d, i) {
74449 var idx = duplicates.indexOf(d.geometry);
74450 if (idx != -1) outputConfig.data[i].groupId = idx;
74451 });
74452 }
74453 }
74454 if (_inputConfig.layout) {
74455 var r = extendDeepAll({}, _inputConfig.layout);
74456 var toTranslate = [
74457 [ r, [ 'plot_bgcolor' ], [ 'backgroundColor' ] ],
74458 [ r, [ 'showlegend' ], [ 'showLegend' ] ],
74459 [ r, [ 'radialaxis' ], [ 'radialAxis' ] ],
74460 [ r, [ 'angularaxis' ], [ 'angularAxis' ] ],
74461 [ r.angularaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
74462 [ r.angularaxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
74463 [ r.angularaxis, [ 'nticks' ], [ 'ticksCount' ] ],
74464 [ r.angularaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
74465 [ r.angularaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
74466 [ r.angularaxis, [ 'range' ], [ 'domain' ] ],
74467 [ r.angularaxis, [ 'endpadding' ], [ 'endPadding' ] ],
74468 [ r.radialaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
74469 [ r.radialaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
74470 [ r.radialaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
74471 [ r.radialaxis, [ 'range' ], [ 'domain' ] ],
74472 [ r.angularAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
74473 [ r.angularAxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
74474 [ r.angularAxis, [ 'nticks' ], [ 'ticksCount' ] ],
74475 [ r.angularAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
74476 [ r.angularAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
74477 [ r.angularAxis, [ 'range' ], [ 'domain' ] ],
74478 [ r.angularAxis, [ 'endpadding' ], [ 'endPadding' ] ],
74479 [ r.radialAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
74480 [ r.radialAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
74481 [ r.radialAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
74482 [ r.radialAxis, [ 'range' ], [ 'domain' ] ],
74483 [ r.font, [ 'outlinecolor' ], [ 'outlineColor' ] ],
74484 [ r.legend, [ 'traceorder' ], [ 'reverseOrder' ] ],
74485 [ r, [ 'labeloffset' ], [ 'labelOffset' ] ],
74486 [ r, [ 'defaultcolorrange' ], [ 'defaultColorRange' ] ]
74487 ];
74488 toTranslate.forEach(function(d, i) {
74489 µ.util.translator.apply(null, d.concat(reverse));
74490 });
74491
74492 if (!reverse) {
74493 if (r.angularAxis && typeof r.angularAxis.ticklen !== 'undefined') r.tickLength = r.angularAxis.ticklen;
74494 if (r.angularAxis && typeof r.angularAxis.tickcolor !== 'undefined') r.tickColor = r.angularAxis.tickcolor;
74495 } else {
74496 if (typeof r.tickLength !== 'undefined') {
74497 r.angularaxis.ticklen = r.tickLength;
74498 delete r.tickLength;
74499 }
74500 if (r.tickColor) {
74501 r.angularaxis.tickcolor = r.tickColor;
74502 delete r.tickColor;
74503 }
74504 }
74505 if (r.legend && typeof r.legend.reverseOrder != 'boolean') {
74506 r.legend.reverseOrder = r.legend.reverseOrder != 'normal';
74507 }
74508 if (r.legend && typeof r.legend.traceorder == 'boolean') {
74509 r.legend.traceorder = r.legend.traceorder ? 'reversed' : 'normal';
74510 delete r.legend.reverseOrder;
74511 }
74512 if (r.margin && typeof r.margin.t != 'undefined') {
74513 var source = [ 't', 'r', 'b', 'l', 'pad' ];
74514 var target = [ 'top', 'right', 'bottom', 'left', 'pad' ];
74515 var margin = {};
74516 d3.entries(r.margin).forEach(function(dB, iB) {
74517 margin[target[source.indexOf(dB.key)]] = dB.value;
74518 });
74519 r.margin = margin;
74520 }
74521 if (reverse) {
74522 delete r.needsEndSpacing;
74523 delete r.minorTickColor;
74524 delete r.minorTicks;
74525 delete r.angularaxis.ticksCount;
74526 delete r.angularaxis.ticksCount;
74527 delete r.angularaxis.ticksStep;
74528 delete r.angularaxis.rewriteTicks;
74529 delete r.angularaxis.nticks;
74530 delete r.radialaxis.ticksCount;
74531 delete r.radialaxis.ticksCount;
74532 delete r.radialaxis.ticksStep;
74533 delete r.radialaxis.rewriteTicks;
74534 delete r.radialaxis.nticks;
74535 }
74536 outputConfig.layout = r;
74537 }
74538 return outputConfig;
74539 };
74540 return exports;
74541};
74542
74543},{"../../../constants/alignment":152,"../../../lib":177,"d3":13}],268:[function(_dereq_,module,exports){
74544/**
74545* Copyright 2012-2020, Plotly, Inc.
74546* All rights reserved.
74547*
74548* This source code is licensed under the MIT license found in the
74549* LICENSE file in the root directory of this source tree.
74550*/
74551
74552/* eslint-disable new-cap */
74553
74554'use strict';
74555
74556var d3 = _dereq_('d3');
74557var Lib = _dereq_('../../../lib');
74558var Color = _dereq_('../../../components/color');
74559
74560var micropolar = _dereq_('./micropolar');
74561var UndoManager = _dereq_('./undo_manager');
74562var extendDeepAll = Lib.extendDeepAll;
74563
74564var manager = module.exports = {};
74565
74566manager.framework = function(_gd) {
74567 var config, previousConfigClone, plot, convertedInput, container;
74568 var undoManager = new UndoManager();
74569
74570 function exports(_inputConfig, _container) {
74571 if(_container) container = _container;
74572 d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove();
74573
74574 config = (!config) ?
74575 _inputConfig :
74576 extendDeepAll(config, _inputConfig);
74577
74578 if(!plot) plot = micropolar.Axis();
74579 convertedInput = micropolar.adapter.plotly().convert(config);
74580 plot.config(convertedInput).render(container);
74581 _gd.data = config.data;
74582 _gd.layout = config.layout;
74583 manager.fillLayout(_gd);
74584 return config;
74585 }
74586 exports.isPolar = true;
74587 exports.svg = function() { return plot.svg(); };
74588 exports.getConfig = function() { return config; };
74589 exports.getLiveConfig = function() {
74590 return micropolar.adapter.plotly().convert(plot.getLiveConfig(), true);
74591 };
74592 exports.getLiveScales = function() { return {t: plot.angularScale(), r: plot.radialScale()}; };
74593 exports.setUndoPoint = function() {
74594 var that = this;
74595 var configClone = micropolar.util.cloneJson(config);
74596 (function(_configClone, _previousConfigClone) {
74597 undoManager.add({
74598 undo: function() {
74599 if(_previousConfigClone) that(_previousConfigClone);
74600 },
74601 redo: function() {
74602 that(_configClone);
74603 }
74604 });
74605 })(configClone, previousConfigClone);
74606 previousConfigClone = micropolar.util.cloneJson(configClone);
74607 };
74608 exports.undo = function() { undoManager.undo(); };
74609 exports.redo = function() { undoManager.redo(); };
74610 return exports;
74611};
74612
74613manager.fillLayout = function(_gd) {
74614 var container = d3.select(_gd).selectAll('.plot-container');
74615 var paperDiv = container.selectAll('.svg-container');
74616 var paper = _gd.framework && _gd.framework.svg && _gd.framework.svg();
74617 var dflts = {
74618 width: 800,
74619 height: 600,
74620 paper_bgcolor: Color.background,
74621 _container: container,
74622 _paperdiv: paperDiv,
74623 _paper: paper
74624 };
74625
74626 _gd._fullLayout = extendDeepAll(dflts, _gd.layout);
74627};
74628
74629},{"../../../components/color":50,"../../../lib":177,"./micropolar":267,"./undo_manager":269,"d3":13}],269:[function(_dereq_,module,exports){
74630/**
74631* Copyright 2012-2020, Plotly, Inc.
74632* All rights reserved.
74633*
74634* This source code is licensed under the MIT license found in the
74635* LICENSE file in the root directory of this source tree.
74636*/
74637
74638'use strict';
74639
74640// Modified from https://github.com/ArthurClemens/Javascript-Undo-Manager
74641// Copyright (c) 2010-2013 Arthur Clemens, arthur@visiblearea.com
74642module.exports = function UndoManager() {
74643 var undoCommands = [];
74644 var index = -1;
74645 var isExecuting = false;
74646 var callback;
74647
74648 function execute(command, action) {
74649 if(!command) return this;
74650
74651 isExecuting = true;
74652 command[action]();
74653 isExecuting = false;
74654
74655 return this;
74656 }
74657
74658 return {
74659 add: function(command) {
74660 if(isExecuting) return this;
74661 undoCommands.splice(index + 1, undoCommands.length - index);
74662 undoCommands.push(command);
74663 index = undoCommands.length - 1;
74664 return this;
74665 },
74666 setCallback: function(callbackFunc) { callback = callbackFunc; },
74667 undo: function() {
74668 var command = undoCommands[index];
74669 if(!command) return this;
74670 execute(command, 'undo');
74671 index -= 1;
74672 if(callback) callback(command.undo);
74673 return this;
74674 },
74675 redo: function() {
74676 var command = undoCommands[index + 1];
74677 if(!command) return this;
74678 execute(command, 'redo');
74679 index += 1;
74680 if(callback) callback(command.redo);
74681 return this;
74682 },
74683 clear: function() {
74684 undoCommands = [];
74685 index = -1;
74686 },
74687 hasUndo: function() { return index !== -1; },
74688 hasRedo: function() { return index < (undoCommands.length - 1); },
74689 getCommands: function() { return undoCommands; },
74690 getPreviousCommand: function() { return undoCommands[index - 1]; },
74691 getIndex: function() { return index; }
74692 };
74693};
74694
74695},{}],270:[function(_dereq_,module,exports){
74696/**
74697* Copyright 2012-2020, Plotly, Inc.
74698* All rights reserved.
74699*
74700* This source code is licensed under the MIT license found in the
74701* LICENSE file in the root directory of this source tree.
74702*/
74703
74704
74705'use strict';
74706
74707var Lib = _dereq_('../lib');
74708var Template = _dereq_('../plot_api/plot_template');
74709var handleDomainDefaults = _dereq_('./domain').defaults;
74710
74711
74712/**
74713 * Find and supply defaults to all subplots of a given type
74714 * This handles subplots that are contained within one container - so
74715 * gl3d, geo, ternary... but not 2d axes which have separate x and y axes
74716 * finds subplots, coerces their `domain` attributes, then calls the
74717 * given handleDefaults function to fill in everything else.
74718 *
74719 * layoutIn: the complete user-supplied input layout
74720 * layoutOut: the complete finished layout
74721 * fullData: the finished data array, used only to find subplots
74722 * opts: {
74723 * type: subplot type string
74724 * attributes: subplot attributes object
74725 * partition: 'x' or 'y', which direction to divide domain space by default
74726 * (default 'x', ie side-by-side subplots)
74727 * TODO: this option is only here because 3D and geo made opposite
74728 * choices in this regard previously and I didn't want to change it.
74729 * Instead we should do:
74730 * - something consistent
74731 * - something more square (4 cuts 2x2, 5/6 cuts 2x3, etc.)
74732 * - something that includes all subplot types in one arrangement,
74733 * now that we can have them together!
74734 * handleDefaults: function of (subplotLayoutIn, subplotLayoutOut, coerce, opts)
74735 * this opts object is passed through to handleDefaults, so attach any
74736 * additional items needed by this function here as well
74737 * }
74738 */
74739module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) {
74740 var subplotType = opts.type;
74741 var subplotAttributes = opts.attributes;
74742 var handleDefaults = opts.handleDefaults;
74743 var partition = opts.partition || 'x';
74744
74745 var ids = layoutOut._subplots[subplotType];
74746 var idsLength = ids.length;
74747
74748 var baseId = idsLength && ids[0].replace(/\d+$/, '');
74749
74750 var subplotLayoutIn, subplotLayoutOut;
74751
74752 function coerce(attr, dflt) {
74753 return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt);
74754 }
74755
74756 for(var i = 0; i < idsLength; i++) {
74757 var id = ids[i];
74758
74759 // ternary traces get a layout ternary for free!
74760 if(layoutIn[id]) subplotLayoutIn = layoutIn[id];
74761 else subplotLayoutIn = layoutIn[id] = {};
74762
74763 subplotLayoutOut = Template.newContainer(layoutOut, id, baseId);
74764
74765 // All subplot containers get a `uirevision` inheriting from the base.
74766 // Currently all subplots containers have some user interaction
74767 // attributes, but if we ever add one that doesn't, we would need an
74768 // option to skip this step.
74769 coerce('uirevision', layoutOut.uirevision);
74770
74771 var dfltDomains = {};
74772 dfltDomains[partition] = [i / idsLength, (i + 1) / idsLength];
74773 handleDomainDefaults(subplotLayoutOut, layoutOut, coerce, dfltDomains);
74774
74775 opts.id = id;
74776 handleDefaults(subplotLayoutIn, subplotLayoutOut, coerce, opts);
74777 }
74778};
74779
74780},{"../lib":177,"../plot_api/plot_template":212,"./domain":249}],271:[function(_dereq_,module,exports){
74781/**
74782* Copyright 2012-2020, Plotly, Inc.
74783* All rights reserved.
74784*
74785* This source code is licensed under the MIT license found in the
74786* LICENSE file in the root directory of this source tree.
74787*/
74788
74789'use strict';
74790
74791var FORMAT_LINK = _dereq_('../constants/docs').FORMAT_LINK;
74792var DATE_FORMAT_LINK = _dereq_('../constants/docs').DATE_FORMAT_LINK;
74793
74794var templateFormatStringDescription = [
74795 'Variables are inserted using %{variable}, for example "y: %{y}".',
74796 'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".',
74797 FORMAT_LINK,
74798 'for details on the formatting syntax.',
74799 'Dates are formatted using d3-time-format\'s syntax %{variable|d3-time-format}, for example "Day: %{2019-01-01|%A}".',
74800 DATE_FORMAT_LINK,
74801 'for details on the date formatting syntax.'
74802].join(' ');
74803
74804function describeVariables(extra) {
74805 var descPart = extra.description ? ' ' + extra.description : '';
74806 var keys = extra.keys || [];
74807 if(keys.length > 0) {
74808 var quotedKeys = [];
74809 for(var i = 0; i < keys.length; i++) {
74810 quotedKeys[i] = '`' + keys[i] + '`';
74811 }
74812 descPart = descPart + 'Finally, the template string has access to ';
74813 if(keys.length === 1) {
74814 descPart = 'variable ' + quotedKeys[0];
74815 } else {
74816 descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.';
74817 }
74818 }
74819 return descPart;
74820}
74821
74822exports.hovertemplateAttrs = function(opts, extra) {
74823 opts = opts || {};
74824 extra = extra || {};
74825
74826 var descPart = describeVariables(extra);
74827
74828 var hovertemplate = {
74829 valType: 'string',
74830
74831 dflt: '',
74832 editType: opts.editType || 'none',
74833
74834 };
74835
74836 if(opts.arrayOk !== false) {
74837 hovertemplate.arrayOk = true;
74838 }
74839
74840 return hovertemplate;
74841};
74842
74843exports.texttemplateAttrs = function(opts, extra) {
74844 opts = opts || {};
74845 extra = extra || {};
74846
74847 var descPart = describeVariables(extra);
74848
74849 var texttemplate = {
74850 valType: 'string',
74851
74852 dflt: '',
74853 editType: opts.editType || 'calc',
74854
74855 };
74856
74857 if(opts.arrayOk !== false) {
74858 texttemplate.arrayOk = true;
74859 }
74860 return texttemplate;
74861};
74862
74863},{"../constants/docs":153}],272:[function(_dereq_,module,exports){
74864/**
74865* Copyright 2012-2020, Plotly, Inc.
74866* All rights reserved.
74867*
74868* This source code is licensed under the MIT license found in the
74869* LICENSE file in the root directory of this source tree.
74870*/
74871
74872'use strict';
74873
74874var Loggers = _dereq_('./lib/loggers');
74875var noop = _dereq_('./lib/noop');
74876var pushUnique = _dereq_('./lib/push_unique');
74877var isPlainObject = _dereq_('./lib/is_plain_object');
74878var addStyleRule = _dereq_('./lib/dom').addStyleRule;
74879var ExtendModule = _dereq_('./lib/extend');
74880
74881var basePlotAttributes = _dereq_('./plots/attributes');
74882var baseLayoutAttributes = _dereq_('./plots/layout_attributes');
74883
74884var extendFlat = ExtendModule.extendFlat;
74885var extendDeepAll = ExtendModule.extendDeepAll;
74886
74887exports.modules = {};
74888exports.allCategories = {};
74889exports.allTypes = [];
74890exports.subplotsRegistry = {};
74891exports.transformsRegistry = {};
74892exports.componentsRegistry = {};
74893exports.layoutArrayContainers = [];
74894exports.layoutArrayRegexes = [];
74895exports.traceLayoutAttributes = {};
74896exports.localeRegistry = {};
74897exports.apiMethodRegistry = {};
74898exports.collectableSubplotTypes = null;
74899
74900/**
74901 * Top-level register routine, exported as Plotly.register
74902 *
74903 * @param {object array or array of objects} _modules :
74904 * module object or list of module object to register.
74905 *
74906 * A valid `moduleType: 'trace'` module has fields:
74907 * - name {string} : the trace type
74908 * - categories {array} : categories associated with this trace type,
74909 * tested with Register.traceIs()
74910 * - meta {object} : meta info (mostly for plot-schema)
74911 *
74912 * A valid `moduleType: 'locale'` module has fields:
74913 * - name {string} : the locale name. Should be a 2-digit language string ('en', 'de')
74914 * optionally with a country/region code ('en-GB', 'de-CH'). If a country
74915 * code is used but the base language locale has not yet been supplied,
74916 * we will use this locale for the base as well.
74917 * - dictionary {object} : the dictionary mapping input strings to localized strings
74918 * generally the keys should be the literal input strings, but
74919 * if default translations are provided you can use any string as a key.
74920 * - format {object} : a `d3.locale` format specifier for this locale
74921 * any omitted keys we'll fall back on en-US.
74922 *
74923 * A valid `moduleType: 'transform'` module has fields:
74924 * - name {string} : transform name
74925 * - transform {function} : default-level transform function
74926 * - calcTransform {function} : calc-level transform function
74927 * - attributes {object} : transform attributes declarations
74928 * - supplyDefaults {function} : attributes default-supply function
74929 *
74930 * A valid `moduleType: 'component'` module has fields:
74931 * - name {string} : the component name, used it with Register.getComponentMethod()
74932 * to employ component method.
74933 *
74934 * A valid `moduleType: 'apiMethod'` module has fields:
74935 * - name {string} : the api method name.
74936 * - fn {function} : the api method called with Register.call();
74937 *
74938 */
74939exports.register = function register(_modules) {
74940 exports.collectableSubplotTypes = null;
74941
74942 if(!_modules) {
74943 throw new Error('No argument passed to Plotly.register.');
74944 } else if(_modules && !Array.isArray(_modules)) {
74945 _modules = [_modules];
74946 }
74947
74948 for(var i = 0; i < _modules.length; i++) {
74949 var newModule = _modules[i];
74950
74951 if(!newModule) {
74952 throw new Error('Invalid module was attempted to be registered!');
74953 }
74954
74955 switch(newModule.moduleType) {
74956 case 'trace':
74957 registerTraceModule(newModule);
74958 break;
74959 case 'transform':
74960 registerTransformModule(newModule);
74961 break;
74962 case 'component':
74963 registerComponentModule(newModule);
74964 break;
74965 case 'locale':
74966 registerLocale(newModule);
74967 break;
74968 case 'apiMethod':
74969 var name = newModule.name;
74970 exports.apiMethodRegistry[name] = newModule.fn;
74971 break;
74972 default:
74973 throw new Error('Invalid module was attempted to be registered!');
74974 }
74975 }
74976};
74977
74978/**
74979 * Get registered module using trace object or trace type
74980 *
74981 * @param {object||string} trace
74982 * trace object with prop 'type' or trace type as a string
74983 * @return {object}
74984 * module object corresponding to trace type
74985 */
74986exports.getModule = function(trace) {
74987 var _module = exports.modules[getTraceType(trace)];
74988 if(!_module) return false;
74989 return _module._module;
74990};
74991
74992/**
74993 * Determine if this trace type is in a given category
74994 *
74995 * @param {object||string} traceType
74996 * a trace (object) or trace type (string)
74997 * @param {string} category
74998 * category in question
74999 * @return {boolean}
75000 */
75001exports.traceIs = function(traceType, category) {
75002 traceType = getTraceType(traceType);
75003
75004 // old Chart Studio Cloud workspace hack, nothing to see here
75005 if(traceType === 'various') return false;
75006
75007 var _module = exports.modules[traceType];
75008
75009 if(!_module) {
75010 if(traceType && traceType !== 'area') {
75011 Loggers.log('Unrecognized trace type ' + traceType + '.');
75012 }
75013
75014 _module = exports.modules[basePlotAttributes.type.dflt];
75015 }
75016
75017 return !!_module.categories[category];
75018};
75019
75020/**
75021 * Determine if this trace has a transform of the given type and return
75022 * array of matching indices.
75023 *
75024 * @param {object} data
75025 * a trace object (member of data or fullData)
75026 * @param {string} type
75027 * type of trace to test
75028 * @return {array}
75029 * array of matching indices. If none found, returns []
75030 */
75031exports.getTransformIndices = function(data, type) {
75032 var indices = [];
75033 var transforms = data.transforms || [];
75034 for(var i = 0; i < transforms.length; i++) {
75035 if(transforms[i].type === type) {
75036 indices.push(i);
75037 }
75038 }
75039 return indices;
75040};
75041
75042/**
75043 * Determine if this trace has a transform of the given type
75044 *
75045 * @param {object} data
75046 * a trace object (member of data or fullData)
75047 * @param {string} type
75048 * type of trace to test
75049 * @return {boolean}
75050 */
75051exports.hasTransform = function(data, type) {
75052 var transforms = data.transforms || [];
75053 for(var i = 0; i < transforms.length; i++) {
75054 if(transforms[i].type === type) {
75055 return true;
75056 }
75057 }
75058 return false;
75059};
75060
75061/**
75062 * Retrieve component module method. Falls back on noop if either the
75063 * module or the method is missing, so the result can always be safely called
75064 *
75065 * @param {string} name
75066 * name of component (as declared in component module)
75067 * @param {string} method
75068 * name of component module method
75069 * @return {function}
75070 */
75071exports.getComponentMethod = function(name, method) {
75072 var _module = exports.componentsRegistry[name];
75073
75074 if(!_module) return noop;
75075 return _module[method] || noop;
75076};
75077
75078/**
75079 * Call registered api method.
75080 *
75081 * @param {string} name : api method name
75082 * @param {...array} args : arguments passed to api method
75083 * @return {any} : returns api method output
75084 */
75085exports.call = function() {
75086 var name = arguments[0];
75087 var args = [].slice.call(arguments, 1);
75088 return exports.apiMethodRegistry[name].apply(null, args);
75089};
75090
75091function registerTraceModule(_module) {
75092 var thisType = _module.name;
75093 var categoriesIn = _module.categories;
75094 var meta = _module.meta;
75095
75096 if(exports.modules[thisType]) {
75097 Loggers.log('Type ' + thisType + ' already registered');
75098 return;
75099 }
75100
75101 if(!exports.subplotsRegistry[_module.basePlotModule.name]) {
75102 registerSubplot(_module.basePlotModule);
75103 }
75104
75105 var categoryObj = {};
75106 for(var i = 0; i < categoriesIn.length; i++) {
75107 categoryObj[categoriesIn[i]] = true;
75108 exports.allCategories[categoriesIn[i]] = true;
75109 }
75110
75111 exports.modules[thisType] = {
75112 _module: _module,
75113 categories: categoryObj
75114 };
75115
75116 if(meta && Object.keys(meta).length) {
75117 exports.modules[thisType].meta = meta;
75118 }
75119
75120 exports.allTypes.push(thisType);
75121
75122 for(var componentName in exports.componentsRegistry) {
75123 mergeComponentAttrsToTrace(componentName, thisType);
75124 }
75125
75126 /*
75127 * Collect all trace layout attributes in one place for easier lookup later
75128 * but don't merge them into the base schema as it would confuse the docs
75129 * (at least after https://github.com/plotly/documentation/issues/202 gets done!)
75130 */
75131 if(_module.layoutAttributes) {
75132 extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes);
75133 }
75134
75135 var basePlotModule = _module.basePlotModule;
75136 var bpmName = basePlotModule.name;
75137
75138 // add mapbox-gl CSS here to avoid console warning on instantiation
75139 if(bpmName === 'mapbox') {
75140 var styleRules = basePlotModule.constants.styleRules;
75141 for(var k in styleRules) {
75142 addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]);
75143 }
75144 }
75145
75146 // if `plotly-geo-assets.js` is not included,
75147 // add `PlotlyGeoAssets` global to stash references to all fetched
75148 // topojson / geojson data
75149 if((bpmName === 'geo' || bpmName === 'mapbox') &&
75150 (typeof window !== undefined && window.PlotlyGeoAssets === undefined)
75151 ) {
75152 window.PlotlyGeoAssets = {topojson: {}};
75153 }
75154}
75155
75156function registerSubplot(_module) {
75157 var plotType = _module.name;
75158
75159 if(exports.subplotsRegistry[plotType]) {
75160 Loggers.log('Plot type ' + plotType + ' already registered.');
75161 return;
75162 }
75163
75164 // relayout array handling will look for component module methods with this
75165 // name and won't find them because this is a subplot module... but that
75166 // should be fine, it will just fall back on redrawing the plot.
75167 findArrayRegexps(_module);
75168
75169 // not sure what's best for the 'cartesian' type at this point
75170 exports.subplotsRegistry[plotType] = _module;
75171
75172 for(var componentName in exports.componentsRegistry) {
75173 mergeComponentAttrsToSubplot(componentName, _module.name);
75174 }
75175}
75176
75177function registerComponentModule(_module) {
75178 if(typeof _module.name !== 'string') {
75179 throw new Error('Component module *name* must be a string.');
75180 }
75181
75182 var name = _module.name;
75183 exports.componentsRegistry[name] = _module;
75184
75185 if(_module.layoutAttributes) {
75186 if(_module.layoutAttributes._isLinkedToArray) {
75187 pushUnique(exports.layoutArrayContainers, name);
75188 }
75189 findArrayRegexps(_module);
75190 }
75191
75192 for(var traceType in exports.modules) {
75193 mergeComponentAttrsToTrace(name, traceType);
75194 }
75195
75196 for(var subplotName in exports.subplotsRegistry) {
75197 mergeComponentAttrsToSubplot(name, subplotName);
75198 }
75199
75200 for(var transformType in exports.transformsRegistry) {
75201 mergeComponentAttrsToTransform(name, transformType);
75202 }
75203
75204 if(_module.schema && _module.schema.layout) {
75205 extendDeepAll(baseLayoutAttributes, _module.schema.layout);
75206 }
75207}
75208
75209function registerTransformModule(_module) {
75210 if(typeof _module.name !== 'string') {
75211 throw new Error('Transform module *name* must be a string.');
75212 }
75213
75214 var prefix = 'Transform module ' + _module.name;
75215 var hasTransform = typeof _module.transform === 'function';
75216 var hasCalcTransform = typeof _module.calcTransform === 'function';
75217
75218 if(!hasTransform && !hasCalcTransform) {
75219 throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
75220 }
75221 if(hasTransform && hasCalcTransform) {
75222 Loggers.log([
75223 prefix + ' has both a *transform* and *calcTransform* methods.',
75224 'Please note that all *transform* methods are executed',
75225 'before all *calcTransform* methods.'
75226 ].join(' '));
75227 }
75228 if(!isPlainObject(_module.attributes)) {
75229 Loggers.log(prefix + ' registered without an *attributes* object.');
75230 }
75231 if(typeof _module.supplyDefaults !== 'function') {
75232 Loggers.log(prefix + ' registered without a *supplyDefaults* method.');
75233 }
75234
75235 exports.transformsRegistry[_module.name] = _module;
75236
75237 for(var componentName in exports.componentsRegistry) {
75238 mergeComponentAttrsToTransform(componentName, _module.name);
75239 }
75240}
75241
75242function registerLocale(_module) {
75243 var locale = _module.name;
75244 var baseLocale = locale.split('-')[0];
75245
75246 var newDict = _module.dictionary;
75247 var newFormat = _module.format;
75248 var hasDict = newDict && Object.keys(newDict).length;
75249 var hasFormat = newFormat && Object.keys(newFormat).length;
75250
75251 var locales = exports.localeRegistry;
75252
75253 var localeObj = locales[locale];
75254 if(!localeObj) locales[locale] = localeObj = {};
75255
75256 // Should we use this dict for the base locale?
75257 // In case we're overwriting a previous dict for this locale, check
75258 // whether the base matches the full locale dict now. If we're not
75259 // overwriting, locales[locale] is undefined so this just checks if
75260 // baseLocale already had a dict or not.
75261 // Same logic for dateFormats
75262 if(baseLocale !== locale) {
75263 var baseLocaleObj = locales[baseLocale];
75264 if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {};
75265
75266 if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) {
75267 baseLocaleObj.dictionary = newDict;
75268 }
75269 if(hasFormat && baseLocaleObj.format === localeObj.format) {
75270 baseLocaleObj.format = newFormat;
75271 }
75272 }
75273
75274 if(hasDict) localeObj.dictionary = newDict;
75275 if(hasFormat) localeObj.format = newFormat;
75276}
75277
75278function findArrayRegexps(_module) {
75279 if(_module.layoutAttributes) {
75280 var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps;
75281 if(arrayAttrRegexps) {
75282 for(var i = 0; i < arrayAttrRegexps.length; i++) {
75283 pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]);
75284 }
75285 }
75286 }
75287}
75288
75289function mergeComponentAttrsToTrace(componentName, traceType) {
75290 var componentSchema = exports.componentsRegistry[componentName].schema;
75291 if(!componentSchema || !componentSchema.traces) return;
75292
75293 var traceAttrs = componentSchema.traces[traceType];
75294 if(traceAttrs) {
75295 extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs);
75296 }
75297}
75298
75299function mergeComponentAttrsToTransform(componentName, transformType) {
75300 var componentSchema = exports.componentsRegistry[componentName].schema;
75301 if(!componentSchema || !componentSchema.transforms) return;
75302
75303 var transformAttrs = componentSchema.transforms[transformType];
75304 if(transformAttrs) {
75305 extendDeepAll(exports.transformsRegistry[transformType].attributes, transformAttrs);
75306 }
75307}
75308
75309function mergeComponentAttrsToSubplot(componentName, subplotName) {
75310 var componentSchema = exports.componentsRegistry[componentName].schema;
75311 if(!componentSchema || !componentSchema.subplots) return;
75312
75313 var subplotModule = exports.subplotsRegistry[subplotName];
75314 var subplotAttrs = subplotModule.layoutAttributes;
75315 var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr;
75316 if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0];
75317
75318 var componentLayoutAttrs = componentSchema.subplots[subplotAttr];
75319 if(subplotAttrs && componentLayoutAttrs) {
75320 extendDeepAll(subplotAttrs, componentLayoutAttrs);
75321 }
75322}
75323
75324function getTraceType(traceType) {
75325 if(typeof traceType === 'object') traceType = traceType.type;
75326 return traceType;
75327}
75328
75329},{"./lib/dom":168,"./lib/extend":170,"./lib/is_plain_object":178,"./lib/loggers":181,"./lib/noop":186,"./lib/push_unique":190,"./plots/attributes":219,"./plots/layout_attributes":261}],273:[function(_dereq_,module,exports){
75330/**
75331* Copyright 2012-2020, Plotly, Inc.
75332* All rights reserved.
75333*
75334* This source code is licensed under the MIT license found in the
75335* LICENSE file in the root directory of this source tree.
75336*/
75337
75338'use strict';
75339
75340var Registry = _dereq_('../registry');
75341var Lib = _dereq_('../lib');
75342
75343var extendFlat = Lib.extendFlat;
75344var extendDeep = Lib.extendDeep;
75345
75346// Put default plotTile layouts here
75347function cloneLayoutOverride(tileClass) {
75348 var override;
75349
75350 switch(tileClass) {
75351 case 'themes__thumb':
75352 override = {
75353 autosize: true,
75354 width: 150,
75355 height: 150,
75356 title: {text: ''},
75357 showlegend: false,
75358 margin: {l: 5, r: 5, t: 5, b: 5, pad: 0},
75359 annotations: []
75360 };
75361 break;
75362
75363 case 'thumbnail':
75364 override = {
75365 title: {text: ''},
75366 hidesources: true,
75367 showlegend: false,
75368 borderwidth: 0,
75369 bordercolor: '',
75370 margin: {l: 1, r: 1, t: 1, b: 1, pad: 0},
75371 annotations: []
75372 };
75373 break;
75374
75375 default:
75376 override = {};
75377 }
75378
75379
75380 return override;
75381}
75382
75383function keyIsAxis(keyName) {
75384 var types = ['xaxis', 'yaxis', 'zaxis'];
75385 return (types.indexOf(keyName.slice(0, 5)) > -1);
75386}
75387
75388
75389module.exports = function clonePlot(graphObj, options) {
75390 // Polar plot compatibility
75391 if(graphObj.framework && graphObj.framework.isPolar) {
75392 graphObj = graphObj.framework.getConfig();
75393 }
75394
75395 var i;
75396 var oldData = graphObj.data;
75397 var oldLayout = graphObj.layout;
75398 var newData = extendDeep([], oldData);
75399 var newLayout = extendDeep({}, oldLayout, cloneLayoutOverride(options.tileClass));
75400 var context = graphObj._context || {};
75401
75402 if(options.width) newLayout.width = options.width;
75403 if(options.height) newLayout.height = options.height;
75404
75405 if(options.tileClass === 'thumbnail' || options.tileClass === 'themes__thumb') {
75406 // kill annotations
75407 newLayout.annotations = [];
75408 var keys = Object.keys(newLayout);
75409
75410 for(i = 0; i < keys.length; i++) {
75411 if(keyIsAxis(keys[i])) {
75412 newLayout[keys[i]].title = {text: ''};
75413 }
75414 }
75415
75416 // kill colorbar and pie labels
75417 for(i = 0; i < newData.length; i++) {
75418 var trace = newData[i];
75419 trace.showscale = false;
75420 if(trace.marker) trace.marker.showscale = false;
75421 if(Registry.traceIs(trace, 'pie-like')) trace.textposition = 'none';
75422 }
75423 }
75424
75425 if(Array.isArray(options.annotations)) {
75426 for(i = 0; i < options.annotations.length; i++) {
75427 newLayout.annotations.push(options.annotations[i]);
75428 }
75429 }
75430
75431 // TODO: does this scene modification really belong here?
75432 // If we still need it, can it move into the gl3d module?
75433 var sceneIds = Object.keys(newLayout).filter(function(key) {
75434 return key.match(/^scene\d*$/);
75435 });
75436 if(sceneIds.length) {
75437 var axesImageOverride = {};
75438 if(options.tileClass === 'thumbnail') {
75439 axesImageOverride = {
75440 title: {text: ''},
75441 showaxeslabels: false,
75442 showticklabels: false,
75443 linetickenable: false
75444 };
75445 }
75446 for(i = 0; i < sceneIds.length; i++) {
75447 var scene = newLayout[sceneIds[i]];
75448
75449 if(!scene.xaxis) {
75450 scene.xaxis = {};
75451 }
75452
75453 if(!scene.yaxis) {
75454 scene.yaxis = {};
75455 }
75456
75457 if(!scene.zaxis) {
75458 scene.zaxis = {};
75459 }
75460
75461 extendFlat(scene.xaxis, axesImageOverride);
75462 extendFlat(scene.yaxis, axesImageOverride);
75463 extendFlat(scene.zaxis, axesImageOverride);
75464
75465 // TODO what does this do?
75466 scene._scene = null;
75467 }
75468 }
75469
75470 var gd = document.createElement('div');
75471 if(options.tileClass) gd.className = options.tileClass;
75472
75473 var plotTile = {
75474 gd: gd,
75475 td: gd, // for external (image server) compatibility
75476 layout: newLayout,
75477 data: newData,
75478 config: {
75479 staticPlot: (options.staticPlot === undefined) ?
75480 true :
75481 options.staticPlot,
75482 plotGlPixelRatio: (options.plotGlPixelRatio === undefined) ?
75483 2 :
75484 options.plotGlPixelRatio,
75485 displaylogo: options.displaylogo || false,
75486 showLink: options.showLink || false,
75487 showTips: options.showTips || false,
75488 mapboxAccessToken: context.mapboxAccessToken
75489 }
75490 };
75491
75492 if(options.setBackground !== 'transparent') {
75493 plotTile.config.setBackground = options.setBackground || 'opaque';
75494 }
75495
75496 // attaching the default Layout the gd, so you can grab it later
75497 plotTile.gd.defaultLayout = cloneLayoutOverride(options.tileClass);
75498
75499 return plotTile;
75500};
75501
75502},{"../lib":177,"../registry":272}],274:[function(_dereq_,module,exports){
75503/**
75504* Copyright 2012-2020, Plotly, Inc.
75505* All rights reserved.
75506*
75507* This source code is licensed under the MIT license found in the
75508* LICENSE file in the root directory of this source tree.
75509*/
75510
75511'use strict';
75512
75513var Lib = _dereq_('../lib');
75514
75515var toImage = _dereq_('../plot_api/to_image');
75516
75517var fileSaver = _dereq_('./filesaver');
75518var helpers = _dereq_('./helpers');
75519
75520/**
75521 * Plotly.downloadImage
75522 *
75523 * @param {object | string | HTML div} gd
75524 * can either be a data/layout/config object
75525 * or an existing graph <div>
75526 * or an id to an existing graph <div>
75527 * @param {object} opts (see Plotly.toImage in ../plot_api/to_image)
75528 * @return {promise}
75529 */
75530function downloadImage(gd, opts) {
75531 var _gd;
75532 if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd);
75533
75534 opts = opts || {};
75535 opts.format = opts.format || 'png';
75536 opts.imageDataOnly = true;
75537
75538 return new Promise(function(resolve, reject) {
75539 if(_gd && _gd._snapshotInProgress) {
75540 reject(new Error('Snapshotting already in progress.'));
75541 }
75542
75543 // see comments within svgtoimg for additional
75544 // discussion of problems with IE
75545 // can now draw to canvas, but CORS tainted canvas
75546 // does not allow toDataURL
75547 // svg format will work though
75548 if(Lib.isIE() && opts.format !== 'svg') {
75549 reject(new Error(helpers.MSG_IE_BAD_FORMAT));
75550 }
75551
75552 if(_gd) _gd._snapshotInProgress = true;
75553 var promise = toImage(gd, opts);
75554
75555 var filename = opts.filename || gd.fn || 'newplot';
75556 filename += '.' + opts.format.replace('-', '.');
75557
75558 promise.then(function(result) {
75559 if(_gd) _gd._snapshotInProgress = false;
75560 return fileSaver(result, filename, opts.format);
75561 }).then(function(name) {
75562 resolve(name);
75563 }).catch(function(err) {
75564 if(_gd) _gd._snapshotInProgress = false;
75565 reject(err);
75566 });
75567 });
75568}
75569
75570module.exports = downloadImage;
75571
75572},{"../lib":177,"../plot_api/to_image":215,"./filesaver":275,"./helpers":276}],275:[function(_dereq_,module,exports){
75573/**
75574* Copyright 2012-2020, Plotly, Inc.
75575* All rights reserved.
75576*
75577* This source code is licensed under the MIT license found in the
75578* LICENSE file in the root directory of this source tree.
75579*/
75580
75581'use strict';
75582
75583var Lib = _dereq_('../lib');
75584var helpers = _dereq_('./helpers');
75585
75586/*
75587* substantial portions of this code from FileSaver.js
75588* https://github.com/eligrey/FileSaver.js
75589* License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
75590* FileSaver.js
75591* A saveAs() FileSaver implementation.
75592* 1.1.20160328
75593*
75594* By Eli Grey, http://eligrey.com
75595* License: MIT
75596* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
75597*/
75598function fileSaver(url, name, format) {
75599 var saveLink = document.createElement('a');
75600 var canUseSaveLink = 'download' in saveLink;
75601
75602 var promise = new Promise(function(resolve, reject) {
75603 var blob;
75604 var objectUrl;
75605
75606 if(Lib.isIE9orBelow()) {
75607 reject(new Error('IE < 10 unsupported'));
75608 }
75609
75610 // Safari doesn't allow downloading of blob urls
75611 if(Lib.isSafari()) {
75612 var prefix = format === 'svg' ? ',' : ';base64,';
75613 helpers.octetStream(prefix + encodeURIComponent(url));
75614 return resolve(name);
75615 }
75616
75617 // IE 10+ (native saveAs)
75618 if(Lib.isIE()) {
75619 // At this point we are only dealing with a decoded SVG as
75620 // a data URL (since IE only supports SVG)
75621 blob = helpers.createBlob(url, 'svg');
75622 window.navigator.msSaveBlob(blob, name);
75623 blob = null;
75624 return resolve(name);
75625 }
75626
75627 if(canUseSaveLink) {
75628 blob = helpers.createBlob(url, format);
75629 objectUrl = helpers.createObjectURL(blob);
75630
75631 saveLink.href = objectUrl;
75632 saveLink.download = name;
75633 document.body.appendChild(saveLink);
75634 saveLink.click();
75635
75636 document.body.removeChild(saveLink);
75637 helpers.revokeObjectURL(objectUrl);
75638 blob = null;
75639
75640 return resolve(name);
75641 }
75642
75643 reject(new Error('download error'));
75644 });
75645
75646 return promise;
75647}
75648
75649
75650module.exports = fileSaver;
75651
75652},{"../lib":177,"./helpers":276}],276:[function(_dereq_,module,exports){
75653/**
75654* Copyright 2012-2020, Plotly, Inc.
75655* All rights reserved.
75656*
75657* This source code is licensed under the MIT license found in the
75658* LICENSE file in the root directory of this source tree.
75659*/
75660
75661'use strict';
75662
75663var Registry = _dereq_('../registry');
75664
75665exports.getDelay = function(fullLayout) {
75666 if(!fullLayout._has) return 0;
75667
75668 return (
75669 fullLayout._has('gl3d') ||
75670 fullLayout._has('gl2d') ||
75671 fullLayout._has('mapbox')
75672 ) ? 500 : 0;
75673};
75674
75675exports.getRedrawFunc = function(gd) {
75676 return function() {
75677 var fullLayout = gd._fullLayout || {};
75678 var hasPolar = fullLayout._has && fullLayout._has('polar');
75679 var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r;
75680
75681 if(!hasLegacyPolar) {
75682 Registry.getComponentMethod('colorbar', 'draw')(gd);
75683 }
75684 };
75685};
75686
75687exports.encodeSVG = function(svg) {
75688 return 'data:image/svg+xml,' + encodeURIComponent(svg);
75689};
75690
75691exports.encodeJSON = function(json) {
75692 return 'data:application/json,' + encodeURIComponent(json);
75693};
75694
75695var DOM_URL = window.URL || window.webkitURL;
75696
75697exports.createObjectURL = function(blob) {
75698 return DOM_URL.createObjectURL(blob);
75699};
75700
75701exports.revokeObjectURL = function(url) {
75702 return DOM_URL.revokeObjectURL(url);
75703};
75704
75705exports.createBlob = function(url, format) {
75706 if(format === 'svg') {
75707 return new window.Blob([url], {type: 'image/svg+xml;charset=utf-8'});
75708 } else if(format === 'full-json') {
75709 return new window.Blob([url], {type: 'application/json;charset=utf-8'});
75710 } else {
75711 var binary = fixBinary(window.atob(url));
75712 return new window.Blob([binary], {type: 'image/' + format});
75713 }
75714};
75715
75716exports.octetStream = function(s) {
75717 document.location.href = 'data:application/octet-stream' + s;
75718};
75719
75720// Taken from https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752
75721function fixBinary(b) {
75722 var len = b.length;
75723 var buf = new ArrayBuffer(len);
75724 var arr = new Uint8Array(buf);
75725 for(var i = 0; i < len; i++) {
75726 arr[i] = b.charCodeAt(i);
75727 }
75728 return buf;
75729}
75730
75731exports.IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;
75732
75733exports.MSG_IE_BAD_FORMAT = 'Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.';
75734
75735},{"../registry":272}],277:[function(_dereq_,module,exports){
75736/**
75737* Copyright 2012-2020, Plotly, Inc.
75738* All rights reserved.
75739*
75740* This source code is licensed under the MIT license found in the
75741* LICENSE file in the root directory of this source tree.
75742*/
75743
75744
75745'use strict';
75746
75747var helpers = _dereq_('./helpers');
75748
75749var Snapshot = {
75750 getDelay: helpers.getDelay,
75751 getRedrawFunc: helpers.getRedrawFunc,
75752 clone: _dereq_('./cloneplot'),
75753 toSVG: _dereq_('./tosvg'),
75754 svgToImg: _dereq_('./svgtoimg'),
75755 toImage: _dereq_('./toimage'),
75756 downloadImage: _dereq_('./download')
75757};
75758
75759module.exports = Snapshot;
75760
75761},{"./cloneplot":273,"./download":274,"./helpers":276,"./svgtoimg":278,"./toimage":279,"./tosvg":280}],278:[function(_dereq_,module,exports){
75762/**
75763* Copyright 2012-2020, Plotly, Inc.
75764* All rights reserved.
75765*
75766* This source code is licensed under the MIT license found in the
75767* LICENSE file in the root directory of this source tree.
75768*/
75769
75770'use strict';
75771
75772var Lib = _dereq_('../lib');
75773var EventEmitter = _dereq_('events').EventEmitter;
75774
75775var helpers = _dereq_('./helpers');
75776
75777function svgToImg(opts) {
75778 var ev = opts.emitter || new EventEmitter();
75779
75780 var promise = new Promise(function(resolve, reject) {
75781 var Image = window.Image;
75782 var svg = opts.svg;
75783 var format = opts.format || 'png';
75784
75785 // IE only support svg
75786 if(Lib.isIE() && format !== 'svg') {
75787 var ieSvgError = new Error(helpers.MSG_IE_BAD_FORMAT);
75788 reject(ieSvgError);
75789 // eventually remove the ev
75790 // in favor of promises
75791 if(!opts.promise) {
75792 return ev.emit('error', ieSvgError);
75793 } else {
75794 return promise;
75795 }
75796 }
75797
75798 var canvas = opts.canvas;
75799 var scale = opts.scale || 1;
75800 var w0 = opts.width || 300;
75801 var h0 = opts.height || 150;
75802 var w1 = scale * w0;
75803 var h1 = scale * h0;
75804
75805 var ctx = canvas.getContext('2d');
75806 var img = new Image();
75807 var svgBlob, url;
75808
75809 if(format === 'svg' || Lib.isIE9orBelow() || Lib.isSafari()) {
75810 url = helpers.encodeSVG(svg);
75811 } else {
75812 svgBlob = helpers.createBlob(svg, 'svg');
75813 url = helpers.createObjectURL(svgBlob);
75814 }
75815
75816 canvas.width = w1;
75817 canvas.height = h1;
75818
75819 img.onload = function() {
75820 var imgData;
75821
75822 svgBlob = null;
75823 helpers.revokeObjectURL(url);
75824
75825 // don't need to draw to canvas if svg
75826 // save some time and also avoid failure on IE
75827 if(format !== 'svg') {
75828 ctx.drawImage(img, 0, 0, w1, h1);
75829 }
75830
75831 switch(format) {
75832 case 'jpeg':
75833 imgData = canvas.toDataURL('image/jpeg');
75834 break;
75835 case 'png':
75836 imgData = canvas.toDataURL('image/png');
75837 break;
75838 case 'webp':
75839 imgData = canvas.toDataURL('image/webp');
75840 break;
75841 case 'svg':
75842 imgData = url;
75843 break;
75844 default:
75845 var errorMsg = 'Image format is not jpeg, png, svg or webp.';
75846 reject(new Error(errorMsg));
75847 // eventually remove the ev
75848 // in favor of promises
75849 if(!opts.promise) {
75850 return ev.emit('error', errorMsg);
75851 }
75852 }
75853 resolve(imgData);
75854 // eventually remove the ev
75855 // in favor of promises
75856 if(!opts.promise) {
75857 ev.emit('success', imgData);
75858 }
75859 };
75860
75861 img.onerror = function(err) {
75862 svgBlob = null;
75863 helpers.revokeObjectURL(url);
75864
75865 reject(err);
75866 // eventually remove the ev
75867 // in favor of promises
75868 if(!opts.promise) {
75869 return ev.emit('error', err);
75870 }
75871 };
75872
75873 img.src = url;
75874 });
75875
75876 // temporary for backward compatibility
75877 // move to only Promise in 2.0.0
75878 // and eliminate the EventEmitter
75879 if(opts.promise) {
75880 return promise;
75881 }
75882
75883 return ev;
75884}
75885
75886module.exports = svgToImg;
75887
75888},{"../lib":177,"./helpers":276,"events":11}],279:[function(_dereq_,module,exports){
75889/**
75890* Copyright 2012-2020, Plotly, Inc.
75891* All rights reserved.
75892*
75893* This source code is licensed under the MIT license found in the
75894* LICENSE file in the root directory of this source tree.
75895*/
75896
75897'use strict';
75898
75899var EventEmitter = _dereq_('events').EventEmitter;
75900
75901var Registry = _dereq_('../registry');
75902var Lib = _dereq_('../lib');
75903
75904var helpers = _dereq_('./helpers');
75905var clonePlot = _dereq_('./cloneplot');
75906var toSVG = _dereq_('./tosvg');
75907var svgToImg = _dereq_('./svgtoimg');
75908
75909/**
75910 * @param {object} gd figure Object
75911 * @param {object} opts option object
75912 * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
75913 */
75914function toImage(gd, opts) {
75915 // first clone the GD so we can operate in a clean environment
75916 var ev = new EventEmitter();
75917
75918 var clone = clonePlot(gd, {format: 'png'});
75919 var clonedGd = clone.gd;
75920
75921 // put the cloned div somewhere off screen before attaching to DOM
75922 clonedGd.style.position = 'absolute';
75923 clonedGd.style.left = '-5000px';
75924 document.body.appendChild(clonedGd);
75925
75926 function wait() {
75927 var delay = helpers.getDelay(clonedGd._fullLayout);
75928
75929 setTimeout(function() {
75930 var svg = toSVG(clonedGd);
75931
75932 var canvas = document.createElement('canvas');
75933 canvas.id = Lib.randstr();
75934
75935 ev = svgToImg({
75936 format: opts.format,
75937 width: clonedGd._fullLayout.width,
75938 height: clonedGd._fullLayout.height,
75939 canvas: canvas,
75940 emitter: ev,
75941 svg: svg
75942 });
75943
75944 ev.clean = function() {
75945 if(clonedGd) document.body.removeChild(clonedGd);
75946 };
75947 }, delay);
75948 }
75949
75950 var redrawFunc = helpers.getRedrawFunc(clonedGd);
75951
75952 Registry.call('plot', clonedGd, clone.data, clone.layout, clone.config)
75953 .then(redrawFunc)
75954 .then(wait)
75955 .catch(function(err) {
75956 ev.emit('error', err);
75957 });
75958
75959
75960 return ev;
75961}
75962
75963module.exports = toImage;
75964
75965},{"../lib":177,"../registry":272,"./cloneplot":273,"./helpers":276,"./svgtoimg":278,"./tosvg":280,"events":11}],280:[function(_dereq_,module,exports){
75966/**
75967* Copyright 2012-2020, Plotly, Inc.
75968* All rights reserved.
75969*
75970* This source code is licensed under the MIT license found in the
75971* LICENSE file in the root directory of this source tree.
75972*/
75973
75974
75975'use strict';
75976
75977var d3 = _dereq_('d3');
75978
75979var Lib = _dereq_('../lib');
75980var Drawing = _dereq_('../components/drawing');
75981var Color = _dereq_('../components/color');
75982
75983var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
75984var DOUBLEQUOTE_REGEX = /"/g;
75985var DUMMY_SUB = 'TOBESTRIPPED';
75986var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g');
75987
75988function htmlEntityDecode(s) {
75989 var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html('');
75990 var replaced = s.replace(/(&[^;]*;)/gi, function(d) {
75991 if(d === '&lt;') { return '&#60;'; } // special handling for brackets
75992 if(d === '&rt;') { return '&#62;'; }
75993 if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; }
75994 return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode
75995 });
75996 hiddenDiv.remove();
75997 return replaced;
75998}
75999
76000function xmlEntityEncode(str) {
76001 return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&amp;');
76002}
76003
76004module.exports = function toSVG(gd, format, scale) {
76005 var fullLayout = gd._fullLayout;
76006 var svg = fullLayout._paper;
76007 var toppaper = fullLayout._toppaper;
76008 var width = fullLayout.width;
76009 var height = fullLayout.height;
76010 var i;
76011
76012 // make background color a rect in the svg, then revert after scraping
76013 // all other alterations have been dealt with by properly preparing the svg
76014 // in the first place... like setting cursors with css classes so we don't
76015 // have to remove them, and providing the right namespaces in the svg to
76016 // begin with
76017 svg.insert('rect', ':first-child')
76018 .call(Drawing.setRect, 0, 0, width, height)
76019 .call(Color.fill, fullLayout.paper_bgcolor);
76020
76021 // subplot-specific to-SVG methods
76022 // which notably add the contents of the gl-container
76023 // into the main svg node
76024 var basePlotModules = fullLayout._basePlotModules || [];
76025 for(i = 0; i < basePlotModules.length; i++) {
76026 var _module = basePlotModules[i];
76027
76028 if(_module.toSVG) _module.toSVG(gd);
76029 }
76030
76031 // add top items above them assumes everything in toppaper is either
76032 // a group or a defs, and if it's empty (like hoverlayer) we can ignore it.
76033 if(toppaper) {
76034 var nodes = toppaper.node().childNodes;
76035
76036 // make copy of nodes as childNodes prop gets mutated in loop below
76037 var topGroups = Array.prototype.slice.call(nodes);
76038
76039 for(i = 0; i < topGroups.length; i++) {
76040 var topGroup = topGroups[i];
76041
76042 if(topGroup.childNodes.length) svg.node().appendChild(topGroup);
76043 }
76044 }
76045
76046 // remove draglayer for Adobe Illustrator compatibility
76047 if(fullLayout._draggers) {
76048 fullLayout._draggers.remove();
76049 }
76050
76051 // in case the svg element had an explicit background color, remove this
76052 // we want the rect to get the color so it's the right size; svg bg will
76053 // fill whatever container it's displayed in regardless of plot size.
76054 svg.node().style.background = '';
76055
76056 svg.selectAll('text')
76057 .attr({'data-unformatted': null, 'data-math': null})
76058 .each(function() {
76059 var txt = d3.select(this);
76060
76061 // hidden text is pre-formatting mathjax, the browser ignores it
76062 // but in a static plot it's useless and it can confuse batik
76063 // we've tried to standardize on display:none but make sure we still
76064 // catch visibility:hidden if it ever arises
76065 if(this.style.visibility === 'hidden' || this.style.display === 'none') {
76066 txt.remove();
76067 return;
76068 } else {
76069 // clear other visibility/display values to default
76070 // to not potentially confuse non-browser SVG implementations
76071 txt.style({visibility: null, display: null});
76072 }
76073
76074 // Font family styles break things because of quotation marks,
76075 // so we must remove them *after* the SVG DOM has been serialized
76076 // to a string (browsers convert singles back)
76077 var ff = this.style.fontFamily;
76078 if(ff && ff.indexOf('"') !== -1) {
76079 txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
76080 }
76081 });
76082
76083
76084 if(fullLayout._gradientUrlQueryParts) {
76085 var queryParts = [];
76086 for(var k in fullLayout._gradientUrlQueryParts) queryParts.push(k);
76087
76088 if(queryParts.length) {
76089 svg.selectAll(queryParts.join(',')).each(function() {
76090 var pt = d3.select(this);
76091
76092 // similar to font family styles above,
76093 // we must remove " after the SVG DOM has been serialized
76094 var fill = this.style.fill;
76095 if(fill && fill.indexOf('url(') !== -1) {
76096 pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
76097 }
76098
76099 var stroke = this.style.stroke;
76100 if(stroke && stroke.indexOf('url(') !== -1) {
76101 pt.style('stroke', stroke.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
76102 }
76103 });
76104 }
76105 }
76106
76107 if(format === 'pdf' || format === 'eps') {
76108 // these formats make the extra line MathJax adds around symbols look super thick in some cases
76109 // it looks better if this is removed entirely.
76110 svg.selectAll('#MathJax_SVG_glyphs path')
76111 .attr('stroke-width', 0);
76112 }
76113
76114 // fix for IE namespacing quirk?
76115 // http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with-ie
76116 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
76117 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
76118
76119 if(format === 'svg' && scale) {
76120 svg.attr('width', scale * width);
76121 svg.attr('height', scale * height);
76122 svg.attr('viewBox', '0 0 ' + width + ' ' + height);
76123 }
76124
76125 var s = new window.XMLSerializer().serializeToString(svg.node());
76126 s = htmlEntityDecode(s);
76127 s = xmlEntityEncode(s);
76128
76129 // Fix quotations around font strings and gradient URLs
76130 s = s.replace(DUMMY_REGEX, '\'');
76131
76132 // IE is very strict, so we will need to clean
76133 // svg with the following regex
76134 // yes this is messy, but do not know a better way
76135 // Even with this IE will not work due to tainted canvas
76136 // see https://github.com/kangax/fabric.js/issues/1957
76137 // http://stackoverflow.com/questions/18112047/canvas-todataurl-working-in-all-browsers-except-ie10
76138 // Leave here just in case the CORS/tainted IE issue gets resolved
76139 if(Lib.isIE()) {
76140 // replace double quote with single quote
76141 s = s.replace(/"/gi, '\'');
76142 // url in svg are single quoted
76143 // since we changed double to single
76144 // we'll need to change these to double-quoted
76145 s = s.replace(/(\('#)([^']*)('\))/gi, '(\"#$2\")');
76146 // font names with spaces will be escaped single-quoted
76147 // we'll need to change these to double-quoted
76148 s = s.replace(/(\\')/gi, '\"');
76149 }
76150
76151 return s;
76152};
76153
76154},{"../components/color":50,"../components/drawing":72,"../constants/xmlns_namespaces":156,"../lib":177,"d3":13}],281:[function(_dereq_,module,exports){
76155/**
76156* Copyright 2012-2020, Plotly, Inc.
76157* All rights reserved.
76158*
76159* This source code is licensed under the MIT license found in the
76160* LICENSE file in the root directory of this source tree.
76161*/
76162
76163'use strict';
76164
76165var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
76166var scatterGeoAttrs = _dereq_('../scattergeo/attributes');
76167var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
76168var baseAttrs = _dereq_('../../plots/attributes');
76169var defaultLine = _dereq_('../../components/color/attributes').defaultLine;
76170
76171var extendFlat = _dereq_('../../lib/extend').extendFlat;
76172
76173var scatterGeoMarkerLineAttrs = scatterGeoAttrs.marker.line;
76174
76175module.exports = extendFlat({
76176 locations: {
76177 valType: 'data_array',
76178 editType: 'calc',
76179
76180 },
76181 locationmode: scatterGeoAttrs.locationmode,
76182 z: {
76183 valType: 'data_array',
76184 editType: 'calc',
76185
76186 },
76187 geojson: extendFlat({}, scatterGeoAttrs.geojson, {
76188
76189 }),
76190 featureidkey: scatterGeoAttrs.featureidkey,
76191
76192 text: extendFlat({}, scatterGeoAttrs.text, {
76193
76194 }),
76195 hovertext: extendFlat({}, scatterGeoAttrs.hovertext, {
76196
76197 }),
76198 marker: {
76199 line: {
76200 color: extendFlat({}, scatterGeoMarkerLineAttrs.color, {dflt: defaultLine}),
76201 width: extendFlat({}, scatterGeoMarkerLineAttrs.width, {dflt: 1}),
76202 editType: 'calc'
76203 },
76204 opacity: {
76205 valType: 'number',
76206 arrayOk: true,
76207 min: 0,
76208 max: 1,
76209 dflt: 1,
76210
76211 editType: 'style',
76212
76213 },
76214 editType: 'calc'
76215 },
76216
76217 selected: {
76218 marker: {
76219 opacity: scatterGeoAttrs.selected.marker.opacity,
76220 editType: 'plot'
76221 },
76222 editType: 'plot'
76223 },
76224 unselected: {
76225 marker: {
76226 opacity: scatterGeoAttrs.unselected.marker.opacity,
76227 editType: 'plot'
76228 },
76229 editType: 'plot'
76230 },
76231
76232 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
76233 editType: 'calc',
76234 flags: ['location', 'z', 'text', 'name']
76235 }),
76236 hovertemplate: hovertemplateAttrs(),
76237 showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
76238},
76239
76240 colorScaleAttrs('', {
76241 cLetter: 'z',
76242 editTypeOverride: 'calc'
76243 })
76244);
76245
76246},{"../../components/color/attributes":49,"../../components/colorscale/attributes":57,"../../lib/extend":170,"../../plots/attributes":219,"../../plots/template_attributes":271,"../scattergeo/attributes":321}],282:[function(_dereq_,module,exports){
76247/**
76248* Copyright 2012-2020, Plotly, Inc.
76249* All rights reserved.
76250*
76251* This source code is licensed under the MIT license found in the
76252* LICENSE file in the root directory of this source tree.
76253*/
76254
76255'use strict';
76256
76257var isNumeric = _dereq_('fast-isnumeric');
76258var BADNUM = _dereq_('../../constants/numerical').BADNUM;
76259
76260var colorscaleCalc = _dereq_('../../components/colorscale/calc');
76261var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata');
76262var calcSelection = _dereq_('../scatter/calc_selection');
76263
76264function isNonBlankString(v) {
76265 return v && typeof v === 'string';
76266}
76267
76268module.exports = function calc(gd, trace) {
76269 var len = trace._length;
76270 var calcTrace = new Array(len);
76271
76272 var isValidLoc;
76273
76274 if(trace.geojson) {
76275 isValidLoc = function(v) { return isNonBlankString(v) || isNumeric(v); };
76276 } else {
76277 isValidLoc = isNonBlankString;
76278 }
76279
76280 for(var i = 0; i < len; i++) {
76281 var calcPt = calcTrace[i] = {};
76282 var loc = trace.locations[i];
76283 var z = trace.z[i];
76284
76285 if(isValidLoc(loc) && isNumeric(z)) {
76286 calcPt.loc = loc;
76287 calcPt.z = z;
76288 } else {
76289 calcPt.loc = null;
76290 calcPt.z = BADNUM;
76291 }
76292
76293 calcPt.index = i;
76294 }
76295
76296 arraysToCalcdata(calcTrace, trace);
76297 colorscaleCalc(gd, trace, {
76298 vals: trace.z,
76299 containerStr: '',
76300 cLetter: 'z'
76301 });
76302 calcSelection(calcTrace, trace);
76303
76304 return calcTrace;
76305};
76306
76307},{"../../components/colorscale/calc":58,"../../constants/numerical":155,"../scatter/arrays_to_calcdata":293,"../scatter/calc_selection":296,"fast-isnumeric":15}],283:[function(_dereq_,module,exports){
76308/**
76309* Copyright 2012-2020, Plotly, Inc.
76310* All rights reserved.
76311*
76312* This source code is licensed under the MIT license found in the
76313* LICENSE file in the root directory of this source tree.
76314*/
76315
76316'use strict';
76317
76318var Lib = _dereq_('../../lib');
76319var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
76320var attributes = _dereq_('./attributes');
76321
76322module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
76323 function coerce(attr, dflt) {
76324 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
76325 }
76326
76327 var locations = coerce('locations');
76328 var z = coerce('z');
76329
76330 if(!(locations && locations.length && Lib.isArrayOrTypedArray(z) && z.length)) {
76331 traceOut.visible = false;
76332 return;
76333 }
76334
76335 traceOut._length = Math.min(locations.length, z.length);
76336
76337 var geojson = coerce('geojson');
76338
76339 var locationmodeDflt;
76340 if((typeof geojson === 'string' && geojson !== '') || Lib.isPlainObject(geojson)) {
76341 locationmodeDflt = 'geojson-id';
76342 }
76343
76344 var locationMode = coerce('locationmode', locationmodeDflt);
76345
76346 if(locationMode === 'geojson-id') {
76347 coerce('featureidkey');
76348 }
76349
76350 coerce('text');
76351 coerce('hovertext');
76352 coerce('hovertemplate');
76353
76354 var mlw = coerce('marker.line.width');
76355 if(mlw) coerce('marker.line.color');
76356 coerce('marker.opacity');
76357
76358 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
76359
76360 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
76361};
76362
76363},{"../../components/colorscale/defaults":60,"../../lib":177,"./attributes":281}],284:[function(_dereq_,module,exports){
76364/**
76365* Copyright 2012-2020, Plotly, Inc.
76366* All rights reserved.
76367*
76368* This source code is licensed under the MIT license found in the
76369* LICENSE file in the root directory of this source tree.
76370*/
76371
76372'use strict';
76373
76374module.exports = function eventData(out, pt, trace, cd, pointNumber) {
76375 out.location = pt.location;
76376 out.z = pt.z;
76377
76378 // include feature properties from input geojson
76379 var cdi = cd[pointNumber];
76380 if(cdi.fIn && cdi.fIn.properties) {
76381 out.properties = cdi.fIn.properties;
76382 }
76383 out.ct = cdi.ct;
76384
76385 return out;
76386};
76387
76388},{}],285:[function(_dereq_,module,exports){
76389/**
76390* Copyright 2012-2020, Plotly, Inc.
76391* All rights reserved.
76392*
76393* This source code is licensed under the MIT license found in the
76394* LICENSE file in the root directory of this source tree.
76395*/
76396
76397'use strict';
76398
76399var Axes = _dereq_('../../plots/cartesian/axes');
76400var attributes = _dereq_('./attributes');
76401var fillText = _dereq_('../../lib').fillText;
76402
76403module.exports = function hoverPoints(pointData, xval, yval) {
76404 var cd = pointData.cd;
76405 var trace = cd[0].trace;
76406 var geo = pointData.subplot;
76407
76408 var pt, i, j, isInside;
76409
76410 for(i = 0; i < cd.length; i++) {
76411 pt = cd[i];
76412 isInside = false;
76413
76414 if(pt._polygons) {
76415 for(j = 0; j < pt._polygons.length; j++) {
76416 if(pt._polygons[j].contains([xval, yval])) {
76417 isInside = !isInside;
76418 }
76419 // for polygons that cross antimeridian as xval is in [-180, 180]
76420 if(pt._polygons[j].contains([xval + 360, yval])) {
76421 isInside = !isInside;
76422 }
76423 }
76424
76425 if(isInside) break;
76426 }
76427 }
76428
76429 if(!isInside || !pt) return;
76430
76431 pointData.x0 = pointData.x1 = pointData.xa.c2p(pt.ct);
76432 pointData.y0 = pointData.y1 = pointData.ya.c2p(pt.ct);
76433
76434 pointData.index = pt.index;
76435 pointData.location = pt.loc;
76436 pointData.z = pt.z;
76437 pointData.zLabel = Axes.tickText(geo.mockAxis, geo.mockAxis.c2l(pt.z), 'hover').text;
76438 pointData.hovertemplate = pt.hovertemplate;
76439
76440 makeHoverInfo(pointData, trace, pt, geo.mockAxis);
76441
76442 return [pointData];
76443};
76444
76445function makeHoverInfo(pointData, trace, pt) {
76446 if(trace.hovertemplate) return;
76447
76448 var hoverinfo = pt.hi || trace.hoverinfo;
76449 var loc = String(pt.loc);
76450
76451 var parts = (hoverinfo === 'all') ?
76452 attributes.hoverinfo.flags :
76453 hoverinfo.split('+');
76454
76455 var hasName = (parts.indexOf('name') !== -1);
76456 var hasLocation = (parts.indexOf('location') !== -1);
76457 var hasZ = (parts.indexOf('z') !== -1);
76458 var hasText = (parts.indexOf('text') !== -1);
76459 var hasIdAsNameLabel = !hasName && hasLocation;
76460
76461 var text = [];
76462
76463 if(hasIdAsNameLabel) {
76464 pointData.nameOverride = loc;
76465 } else {
76466 if(hasName) pointData.nameOverride = trace.name;
76467 if(hasLocation) text.push(loc);
76468 }
76469
76470 if(hasZ) {
76471 text.push(pointData.zLabel);
76472 }
76473 if(hasText) {
76474 fillText(pt, trace, text);
76475 }
76476
76477 pointData.extraText = text.join('<br>');
76478}
76479
76480},{"../../lib":177,"../../plots/cartesian/axes":222,"./attributes":281}],286:[function(_dereq_,module,exports){
76481/**
76482* Copyright 2012-2020, Plotly, Inc.
76483* All rights reserved.
76484*
76485* This source code is licensed under the MIT license found in the
76486* LICENSE file in the root directory of this source tree.
76487*/
76488
76489'use strict';
76490
76491module.exports = {
76492 attributes: _dereq_('./attributes'),
76493 supplyDefaults: _dereq_('./defaults'),
76494 colorbar: _dereq_('../heatmap/colorbar'),
76495 calc: _dereq_('./calc'),
76496 calcGeoJSON: _dereq_('./plot').calcGeoJSON,
76497 plot: _dereq_('./plot').plot,
76498 style: _dereq_('./style').style,
76499 styleOnSelect: _dereq_('./style').styleOnSelect,
76500 hoverPoints: _dereq_('./hover'),
76501 eventData: _dereq_('./event_data'),
76502 selectPoints: _dereq_('./select'),
76503
76504 moduleType: 'trace',
76505 name: 'choropleth',
76506 basePlotModule: _dereq_('../../plots/geo'),
76507 categories: ['geo', 'noOpacity', 'showLegend'],
76508 meta: {
76509
76510 }
76511};
76512
76513},{"../../plots/geo":254,"../heatmap/colorbar":290,"./attributes":281,"./calc":282,"./defaults":283,"./event_data":284,"./hover":285,"./plot":287,"./select":288,"./style":289}],287:[function(_dereq_,module,exports){
76514/**
76515* Copyright 2012-2020, Plotly, Inc.
76516* All rights reserved.
76517*
76518* This source code is licensed under the MIT license found in the
76519* LICENSE file in the root directory of this source tree.
76520*/
76521
76522'use strict';
76523
76524var d3 = _dereq_('d3');
76525
76526var Lib = _dereq_('../../lib');
76527var geoUtils = _dereq_('../../lib/geo_location_utils');
76528var getTopojsonFeatures = _dereq_('../../lib/topojson_utils').getTopojsonFeatures;
76529var findExtremes = _dereq_('../../plots/cartesian/autorange').findExtremes;
76530
76531var style = _dereq_('./style').style;
76532
76533function plot(gd, geo, calcData) {
76534 var choroplethLayer = geo.layers.backplot.select('.choroplethlayer');
76535
76536 Lib.makeTraceGroups(choroplethLayer, calcData, 'trace choropleth').each(function(calcTrace) {
76537 var sel = d3.select(this);
76538
76539 var paths = sel.selectAll('path.choroplethlocation')
76540 .data(Lib.identity);
76541
76542 paths.enter().append('path')
76543 .classed('choroplethlocation', true);
76544
76545 paths.exit().remove();
76546
76547 // call style here within topojson request callback
76548 style(gd, calcTrace);
76549 });
76550}
76551
76552function calcGeoJSON(calcTrace, fullLayout) {
76553 var trace = calcTrace[0].trace;
76554 var geoLayout = fullLayout[trace.geo];
76555 var geo = geoLayout._subplot;
76556 var locationmode = trace.locationmode;
76557 var len = trace._length;
76558
76559 var features = locationmode === 'geojson-id' ?
76560 geoUtils.extractTraceFeature(calcTrace) :
76561 getTopojsonFeatures(trace, geo.topojson);
76562
76563 var lonArray = [];
76564 var latArray = [];
76565
76566 for(var i = 0; i < len; i++) {
76567 var calcPt = calcTrace[i];
76568 var feature = locationmode === 'geojson-id' ?
76569 calcPt.fOut :
76570 geoUtils.locationToFeature(locationmode, calcPt.loc, features);
76571
76572 if(feature) {
76573 calcPt.geojson = feature;
76574 calcPt.ct = feature.properties.ct;
76575 calcPt._polygons = geoUtils.feature2polygons(feature);
76576
76577 var bboxFeature = geoUtils.computeBbox(feature);
76578 lonArray.push(bboxFeature[0], bboxFeature[2]);
76579 latArray.push(bboxFeature[1], bboxFeature[3]);
76580 } else {
76581 calcPt.geojson = null;
76582 }
76583 }
76584
76585 if(geoLayout.fitbounds === 'geojson' && locationmode === 'geojson-id') {
76586 var bboxGeojson = geoUtils.computeBbox(geoUtils.getTraceGeojson(trace));
76587 lonArray = [bboxGeojson[0], bboxGeojson[2]];
76588 latArray = [bboxGeojson[1], bboxGeojson[3]];
76589 }
76590
76591 var opts = {padded: true};
76592 trace._extremes.lon = findExtremes(geoLayout.lonaxis._ax, lonArray, opts);
76593 trace._extremes.lat = findExtremes(geoLayout.lataxis._ax, latArray, opts);
76594}
76595
76596module.exports = {
76597 calcGeoJSON: calcGeoJSON,
76598 plot: plot
76599};
76600
76601},{"../../lib":177,"../../lib/geo_location_utils":173,"../../lib/topojson_utils":201,"../../plots/cartesian/autorange":221,"./style":289,"d3":13}],288:[function(_dereq_,module,exports){
76602/**
76603* Copyright 2012-2020, Plotly, Inc.
76604* All rights reserved.
76605*
76606* This source code is licensed under the MIT license found in the
76607* LICENSE file in the root directory of this source tree.
76608*/
76609
76610'use strict';
76611
76612module.exports = function selectPoints(searchInfo, selectionTester) {
76613 var cd = searchInfo.cd;
76614 var xa = searchInfo.xaxis;
76615 var ya = searchInfo.yaxis;
76616 var selection = [];
76617
76618 var i, di, ct, x, y;
76619
76620 if(selectionTester === false) {
76621 for(i = 0; i < cd.length; i++) {
76622 cd[i].selected = 0;
76623 }
76624 } else {
76625 for(i = 0; i < cd.length; i++) {
76626 di = cd[i];
76627 ct = di.ct;
76628
76629 if(!ct) continue;
76630
76631 x = xa.c2p(ct);
76632 y = ya.c2p(ct);
76633
76634 if(selectionTester.contains([x, y], null, i, searchInfo)) {
76635 selection.push({
76636 pointNumber: i,
76637 lon: ct[0],
76638 lat: ct[1]
76639 });
76640 di.selected = 1;
76641 } else {
76642 di.selected = 0;
76643 }
76644 }
76645 }
76646
76647 return selection;
76648};
76649
76650},{}],289:[function(_dereq_,module,exports){
76651/**
76652* Copyright 2012-2020, Plotly, Inc.
76653* All rights reserved.
76654*
76655* This source code is licensed under the MIT license found in the
76656* LICENSE file in the root directory of this source tree.
76657*/
76658
76659'use strict';
76660
76661var d3 = _dereq_('d3');
76662var Color = _dereq_('../../components/color');
76663var Drawing = _dereq_('../../components/drawing');
76664var Colorscale = _dereq_('../../components/colorscale');
76665
76666function style(gd, calcTrace) {
76667 if(calcTrace) styleTrace(gd, calcTrace);
76668}
76669
76670function styleTrace(gd, calcTrace) {
76671 var trace = calcTrace[0].trace;
76672 var s = calcTrace[0].node3;
76673 var locs = s.selectAll('.choroplethlocation');
76674 var marker = trace.marker || {};
76675 var markerLine = marker.line || {};
76676
76677 var sclFunc = Colorscale.makeColorScaleFuncFromTrace(trace);
76678
76679 locs.each(function(d) {
76680 d3.select(this)
76681 .attr('fill', sclFunc(d.z))
76682 .call(Color.stroke, d.mlc || markerLine.color)
76683 .call(Drawing.dashLine, '', d.mlw || markerLine.width || 0)
76684 .style('opacity', marker.opacity);
76685 });
76686
76687 Drawing.selectedPointStyle(locs, trace, gd);
76688}
76689
76690function styleOnSelect(gd, calcTrace) {
76691 var s = calcTrace[0].node3;
76692 var trace = calcTrace[0].trace;
76693
76694 if(trace.selectedpoints) {
76695 Drawing.selectedPointStyle(s.selectAll('.choroplethlocation'), trace, gd);
76696 } else {
76697 styleTrace(gd, calcTrace);
76698 }
76699}
76700
76701module.exports = {
76702 style: style,
76703 styleOnSelect: styleOnSelect
76704};
76705
76706},{"../../components/color":50,"../../components/colorscale":62,"../../components/drawing":72,"d3":13}],290:[function(_dereq_,module,exports){
76707/**
76708* Copyright 2012-2020, Plotly, Inc.
76709* All rights reserved.
76710*
76711* This source code is licensed under the MIT license found in the
76712* LICENSE file in the root directory of this source tree.
76713*/
76714
76715'use strict';
76716
76717module.exports = {
76718 min: 'zmin',
76719 max: 'zmax'
76720};
76721
76722},{}],291:[function(_dereq_,module,exports){
76723/**
76724* Copyright 2012-2020, Plotly, Inc.
76725* All rights reserved.
76726*
76727* This source code is licensed under the MIT license found in the
76728* LICENSE file in the root directory of this source tree.
76729*/
76730
76731'use strict';
76732
76733var Lib = _dereq_('../../lib');
76734
76735exports.formatPiePercent = function formatPiePercent(v, separators) {
76736 var vRounded = (v * 100).toPrecision(3);
76737 if(vRounded.lastIndexOf('.') !== -1) {
76738 vRounded = vRounded.replace(/[.]?0+$/, '');
76739 }
76740 return Lib.numSeparate(vRounded, separators) + '%';
76741};
76742
76743exports.formatPieValue = function formatPieValue(v, separators) {
76744 var vRounded = v.toPrecision(10);
76745 if(vRounded.lastIndexOf('.') !== -1) {
76746 vRounded = vRounded.replace(/[.]?0+$/, '');
76747 }
76748 return Lib.numSeparate(vRounded, separators);
76749};
76750
76751exports.getFirstFilled = function getFirstFilled(array, indices) {
76752 if(!Array.isArray(array)) return;
76753 for(var i = 0; i < indices.length; i++) {
76754 var v = array[indices[i]];
76755 if(v || v === 0 || v === '') return v;
76756 }
76757};
76758
76759exports.castOption = function castOption(item, indices) {
76760 if(Array.isArray(item)) return exports.getFirstFilled(item, indices);
76761 else if(item) return item;
76762};
76763
76764},{"../../lib":177}],292:[function(_dereq_,module,exports){
76765/**
76766* Copyright 2012-2020, Plotly, Inc.
76767* All rights reserved.
76768*
76769* This source code is licensed under the MIT license found in the
76770* LICENSE file in the root directory of this source tree.
76771*/
76772
76773'use strict';
76774
76775var Color = _dereq_('../../components/color');
76776var castOption = _dereq_('./helpers').castOption;
76777
76778module.exports = function styleOne(s, pt, trace) {
76779 var line = trace.marker.line;
76780 var lineColor = castOption(line.color, pt.pts) || Color.defaultLine;
76781 var lineWidth = castOption(line.width, pt.pts) || 0;
76782
76783 s.style('stroke-width', lineWidth)
76784 .call(Color.fill, pt.color)
76785 .call(Color.stroke, lineColor);
76786};
76787
76788},{"../../components/color":50,"./helpers":291}],293:[function(_dereq_,module,exports){
76789/**
76790* Copyright 2012-2020, Plotly, Inc.
76791* All rights reserved.
76792*
76793* This source code is licensed under the MIT license found in the
76794* LICENSE file in the root directory of this source tree.
76795*/
76796
76797
76798'use strict';
76799
76800var Lib = _dereq_('../../lib');
76801
76802
76803// arrayOk attributes, merge them into calcdata array
76804module.exports = function arraysToCalcdata(cd, trace) {
76805 // so each point knows which index it originally came from
76806 for(var i = 0; i < cd.length; i++) cd[i].i = i;
76807
76808 Lib.mergeArray(trace.text, cd, 'tx');
76809 Lib.mergeArray(trace.texttemplate, cd, 'txt');
76810 Lib.mergeArray(trace.hovertext, cd, 'htx');
76811 Lib.mergeArray(trace.customdata, cd, 'data');
76812 Lib.mergeArray(trace.textposition, cd, 'tp');
76813 if(trace.textfont) {
76814 Lib.mergeArrayCastPositive(trace.textfont.size, cd, 'ts');
76815 Lib.mergeArray(trace.textfont.color, cd, 'tc');
76816 Lib.mergeArray(trace.textfont.family, cd, 'tf');
76817 }
76818
76819 var marker = trace.marker;
76820 if(marker) {
76821 Lib.mergeArrayCastPositive(marker.size, cd, 'ms');
76822 Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo');
76823 Lib.mergeArray(marker.symbol, cd, 'mx');
76824 Lib.mergeArray(marker.color, cd, 'mc');
76825
76826 var markerLine = marker.line;
76827 if(marker.line) {
76828 Lib.mergeArray(markerLine.color, cd, 'mlc');
76829 Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw');
76830 }
76831
76832 var markerGradient = marker.gradient;
76833 if(markerGradient && markerGradient.type !== 'none') {
76834 Lib.mergeArray(markerGradient.type, cd, 'mgt');
76835 Lib.mergeArray(markerGradient.color, cd, 'mgc');
76836 }
76837 }
76838};
76839
76840},{"../../lib":177}],294:[function(_dereq_,module,exports){
76841/**
76842* Copyright 2012-2020, Plotly, Inc.
76843* All rights reserved.
76844*
76845* This source code is licensed under the MIT license found in the
76846* LICENSE file in the root directory of this source tree.
76847*/
76848
76849'use strict';
76850
76851var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
76852var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
76853var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
76854var fontAttrs = _dereq_('../../plots/font_attributes');
76855var dash = _dereq_('../../components/drawing/attributes').dash;
76856
76857var Drawing = _dereq_('../../components/drawing');
76858var constants = _dereq_('./constants');
76859var extendFlat = _dereq_('../../lib/extend').extendFlat;
76860
76861module.exports = {
76862 x: {
76863 valType: 'data_array',
76864 editType: 'calc+clearAxisTypes',
76865 anim: true,
76866
76867 },
76868 x0: {
76869 valType: 'any',
76870 dflt: 0,
76871
76872 editType: 'calc+clearAxisTypes',
76873 anim: true,
76874
76875 },
76876 dx: {
76877 valType: 'number',
76878 dflt: 1,
76879
76880 editType: 'calc',
76881 anim: true,
76882
76883 },
76884 y: {
76885 valType: 'data_array',
76886 editType: 'calc+clearAxisTypes',
76887 anim: true,
76888
76889 },
76890 y0: {
76891 valType: 'any',
76892 dflt: 0,
76893
76894 editType: 'calc+clearAxisTypes',
76895 anim: true,
76896
76897 },
76898 dy: {
76899 valType: 'number',
76900 dflt: 1,
76901
76902 editType: 'calc',
76903 anim: true,
76904
76905 },
76906
76907 stackgroup: {
76908 valType: 'string',
76909
76910 dflt: '',
76911 editType: 'calc',
76912
76913 },
76914 orientation: {
76915 valType: 'enumerated',
76916
76917 values: ['v', 'h'],
76918 editType: 'calc',
76919
76920 },
76921 groupnorm: {
76922 valType: 'enumerated',
76923 values: ['', 'fraction', 'percent'],
76924 dflt: '',
76925
76926 editType: 'calc',
76927
76928 },
76929 stackgaps: {
76930 valType: 'enumerated',
76931 values: ['infer zero', 'interpolate'],
76932 dflt: 'infer zero',
76933
76934 editType: 'calc',
76935
76936 },
76937
76938 text: {
76939 valType: 'string',
76940
76941 dflt: '',
76942 arrayOk: true,
76943 editType: 'calc',
76944
76945 },
76946
76947 texttemplate: texttemplateAttrs({}, {
76948
76949 }),
76950 hovertext: {
76951 valType: 'string',
76952
76953 dflt: '',
76954 arrayOk: true,
76955 editType: 'style',
76956
76957 },
76958 mode: {
76959 valType: 'flaglist',
76960 flags: ['lines', 'markers', 'text'],
76961 extras: ['none'],
76962
76963 editType: 'calc',
76964
76965 },
76966 hoveron: {
76967 valType: 'flaglist',
76968 flags: ['points', 'fills'],
76969
76970 editType: 'style',
76971
76972 },
76973 hovertemplate: hovertemplateAttrs({}, {
76974 keys: constants.eventDataKeys
76975 }),
76976 line: {
76977 color: {
76978 valType: 'color',
76979
76980 editType: 'style',
76981 anim: true,
76982
76983 },
76984 width: {
76985 valType: 'number',
76986 min: 0,
76987 dflt: 2,
76988
76989 editType: 'style',
76990 anim: true,
76991
76992 },
76993 shape: {
76994 valType: 'enumerated',
76995 values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'],
76996 dflt: 'linear',
76997
76998 editType: 'plot',
76999
77000 },
77001 smoothing: {
77002 valType: 'number',
77003 min: 0,
77004 max: 1.3,
77005 dflt: 1,
77006
77007 editType: 'plot',
77008
77009 },
77010 dash: extendFlat({}, dash, {editType: 'style'}),
77011 simplify: {
77012 valType: 'boolean',
77013 dflt: true,
77014
77015 editType: 'plot',
77016
77017 },
77018 editType: 'plot'
77019 },
77020
77021 connectgaps: {
77022 valType: 'boolean',
77023 dflt: false,
77024
77025 editType: 'calc',
77026
77027 },
77028 cliponaxis: {
77029 valType: 'boolean',
77030 dflt: true,
77031
77032 editType: 'plot',
77033
77034 },
77035
77036 fill: {
77037 valType: 'enumerated',
77038 values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
77039
77040 editType: 'calc',
77041
77042 },
77043 fillcolor: {
77044 valType: 'color',
77045
77046 editType: 'style',
77047 anim: true,
77048
77049 },
77050 marker: extendFlat({
77051 symbol: {
77052 valType: 'enumerated',
77053 values: Drawing.symbolList,
77054 dflt: 'circle',
77055 arrayOk: true,
77056
77057 editType: 'style',
77058
77059 },
77060 opacity: {
77061 valType: 'number',
77062 min: 0,
77063 max: 1,
77064 arrayOk: true,
77065
77066 editType: 'style',
77067 anim: true,
77068
77069 },
77070 size: {
77071 valType: 'number',
77072 min: 0,
77073 dflt: 6,
77074 arrayOk: true,
77075
77076 editType: 'calc',
77077 anim: true,
77078
77079 },
77080 maxdisplayed: {
77081 valType: 'number',
77082 min: 0,
77083 dflt: 0,
77084
77085 editType: 'plot',
77086
77087 },
77088 sizeref: {
77089 valType: 'number',
77090 dflt: 1,
77091
77092 editType: 'calc',
77093
77094 },
77095 sizemin: {
77096 valType: 'number',
77097 min: 0,
77098 dflt: 0,
77099
77100 editType: 'calc',
77101
77102 },
77103 sizemode: {
77104 valType: 'enumerated',
77105 values: ['diameter', 'area'],
77106 dflt: 'diameter',
77107
77108 editType: 'calc',
77109
77110 },
77111
77112 line: extendFlat({
77113 width: {
77114 valType: 'number',
77115 min: 0,
77116 arrayOk: true,
77117
77118 editType: 'style',
77119 anim: true,
77120
77121 },
77122 editType: 'calc'
77123 },
77124 colorScaleAttrs('marker.line', {anim: true})
77125 ),
77126 gradient: {
77127 type: {
77128 valType: 'enumerated',
77129 values: ['radial', 'horizontal', 'vertical', 'none'],
77130 arrayOk: true,
77131 dflt: 'none',
77132
77133 editType: 'calc',
77134
77135 },
77136 color: {
77137 valType: 'color',
77138 arrayOk: true,
77139
77140 editType: 'calc',
77141
77142 },
77143 editType: 'calc'
77144 },
77145 editType: 'calc'
77146 },
77147 colorScaleAttrs('marker', {anim: true})
77148 ),
77149 selected: {
77150 marker: {
77151 opacity: {
77152 valType: 'number',
77153 min: 0,
77154 max: 1,
77155
77156 editType: 'style',
77157
77158 },
77159 color: {
77160 valType: 'color',
77161
77162 editType: 'style',
77163
77164 },
77165 size: {
77166 valType: 'number',
77167 min: 0,
77168
77169 editType: 'style',
77170
77171 },
77172 editType: 'style'
77173 },
77174 textfont: {
77175 color: {
77176 valType: 'color',
77177
77178 editType: 'style',
77179
77180 },
77181 editType: 'style'
77182 },
77183 editType: 'style'
77184 },
77185 unselected: {
77186 marker: {
77187 opacity: {
77188 valType: 'number',
77189 min: 0,
77190 max: 1,
77191
77192 editType: 'style',
77193
77194 },
77195 color: {
77196 valType: 'color',
77197
77198 editType: 'style',
77199
77200 },
77201 size: {
77202 valType: 'number',
77203 min: 0,
77204
77205 editType: 'style',
77206
77207 },
77208 editType: 'style'
77209 },
77210 textfont: {
77211 color: {
77212 valType: 'color',
77213
77214 editType: 'style',
77215
77216 },
77217 editType: 'style'
77218 },
77219 editType: 'style'
77220 },
77221
77222 textposition: {
77223 valType: 'enumerated',
77224 values: [
77225 'top left', 'top center', 'top right',
77226 'middle left', 'middle center', 'middle right',
77227 'bottom left', 'bottom center', 'bottom right'
77228 ],
77229 dflt: 'middle center',
77230 arrayOk: true,
77231
77232 editType: 'calc',
77233
77234 },
77235 textfont: fontAttrs({
77236 editType: 'calc',
77237 colorEditType: 'style',
77238 arrayOk: true,
77239
77240 }),
77241
77242 r: {
77243 valType: 'data_array',
77244 editType: 'calc',
77245
77246 },
77247 t: {
77248 valType: 'data_array',
77249 editType: 'calc',
77250
77251 }
77252};
77253
77254},{"../../components/colorscale/attributes":57,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../lib/extend":170,"../../plots/font_attributes":250,"../../plots/template_attributes":271,"./constants":298}],295:[function(_dereq_,module,exports){
77255/**
77256* Copyright 2012-2020, Plotly, Inc.
77257* All rights reserved.
77258*
77259* This source code is licensed under the MIT license found in the
77260* LICENSE file in the root directory of this source tree.
77261*/
77262
77263'use strict';
77264
77265var isNumeric = _dereq_('fast-isnumeric');
77266var Lib = _dereq_('../../lib');
77267
77268var Axes = _dereq_('../../plots/cartesian/axes');
77269var BADNUM = _dereq_('../../constants/numerical').BADNUM;
77270
77271var subTypes = _dereq_('./subtypes');
77272var calcColorscale = _dereq_('./colorscale_calc');
77273var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
77274var calcSelection = _dereq_('./calc_selection');
77275
77276function calc(gd, trace) {
77277 var fullLayout = gd._fullLayout;
77278 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
77279 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
77280 var x = xa.makeCalcdata(trace, 'x');
77281 var y = ya.makeCalcdata(trace, 'y');
77282 var serieslen = trace._length;
77283 var cd = new Array(serieslen);
77284 var ids = trace.ids;
77285 var stackGroupOpts = getStackOpts(trace, fullLayout, xa, ya);
77286 var interpolateGaps = false;
77287 var isV, i, j, k, interpolate, vali;
77288
77289 setFirstScatter(fullLayout, trace);
77290
77291 var xAttr = 'x';
77292 var yAttr = 'y';
77293 var posAttr;
77294 if(stackGroupOpts) {
77295 Lib.pushUnique(stackGroupOpts.traceIndices, trace._expandedIndex);
77296 isV = stackGroupOpts.orientation === 'v';
77297
77298 // size, like we use for bar
77299 if(isV) {
77300 yAttr = 's';
77301 posAttr = 'x';
77302 } else {
77303 xAttr = 's';
77304 posAttr = 'y';
77305 }
77306 interpolate = stackGroupOpts.stackgaps === 'interpolate';
77307 } else {
77308 var ppad = calcMarkerSize(trace, serieslen);
77309 calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
77310 }
77311
77312 for(i = 0; i < serieslen; i++) {
77313 var cdi = cd[i] = {};
77314 var xValid = isNumeric(x[i]);
77315 var yValid = isNumeric(y[i]);
77316 if(xValid && yValid) {
77317 cdi[xAttr] = x[i];
77318 cdi[yAttr] = y[i];
77319 } else if(stackGroupOpts && (isV ? xValid : yValid)) {
77320 // if we're stacking we need to hold on to all valid positions
77321 // even with invalid sizes
77322
77323 cdi[posAttr] = isV ? x[i] : y[i];
77324 cdi.gap = true;
77325 if(interpolate) {
77326 cdi.s = BADNUM;
77327 interpolateGaps = true;
77328 } else {
77329 cdi.s = 0;
77330 }
77331 } else {
77332 cdi[xAttr] = cdi[yAttr] = BADNUM;
77333 }
77334
77335 if(ids) {
77336 cdi.id = String(ids[i]);
77337 }
77338 }
77339
77340 arraysToCalcdata(cd, trace);
77341 calcColorscale(gd, trace);
77342 calcSelection(cd, trace);
77343
77344 if(stackGroupOpts) {
77345 // remove bad positions and sort
77346 // note that original indices get added to cd in arraysToCalcdata
77347 i = 0;
77348 while(i < cd.length) {
77349 if(cd[i][posAttr] === BADNUM) {
77350 cd.splice(i, 1);
77351 } else i++;
77352 }
77353
77354 Lib.sort(cd, function(a, b) {
77355 return (a[posAttr] - b[posAttr]) || (a.i - b.i);
77356 });
77357
77358 if(interpolateGaps) {
77359 // first fill the beginning with constant from the first point
77360 i = 0;
77361 while(i < cd.length - 1 && cd[i].gap) {
77362 i++;
77363 }
77364 vali = cd[i].s;
77365 if(!vali) vali = cd[i].s = 0; // in case of no data AT ALL in this trace - use 0
77366 for(j = 0; j < i; j++) {
77367 cd[j].s = vali;
77368 }
77369 // then fill the end with constant from the last point
77370 k = cd.length - 1;
77371 while(k > i && cd[k].gap) {
77372 k--;
77373 }
77374 vali = cd[k].s;
77375 for(j = cd.length - 1; j > k; j--) {
77376 cd[j].s = vali;
77377 }
77378 // now interpolate internal gaps linearly
77379 while(i < k) {
77380 i++;
77381 if(cd[i].gap) {
77382 j = i + 1;
77383 while(cd[j].gap) {
77384 j++;
77385 }
77386 var pos0 = cd[i - 1][posAttr];
77387 var size0 = cd[i - 1].s;
77388 var m = (cd[j].s - size0) / (cd[j][posAttr] - pos0);
77389 while(i < j) {
77390 cd[i].s = size0 + (cd[i][posAttr] - pos0) * m;
77391 i++;
77392 }
77393 }
77394 }
77395 }
77396 }
77397
77398 return cd;
77399}
77400
77401function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
77402 var serieslen = trace._length;
77403 var fullLayout = gd._fullLayout;
77404 var xId = xa._id;
77405 var yId = ya._id;
77406 var firstScatter = fullLayout._firstScatter[firstScatterGroup(trace)] === trace.uid;
77407 var stackOrientation = (getStackOpts(trace, fullLayout, xa, ya) || {}).orientation;
77408 var fill = trace.fill;
77409
77410 // cancel minimum tick spacings (only applies to bars and boxes)
77411 xa._minDtick = 0;
77412 ya._minDtick = 0;
77413
77414 // check whether bounds should be tight, padded, extended to zero...
77415 // most cases both should be padded on both ends, so start with that.
77416 var xOptions = {padded: true};
77417 var yOptions = {padded: true};
77418
77419 if(ppad) {
77420 xOptions.ppad = yOptions.ppad = ppad;
77421 }
77422
77423 // TODO: text size
77424
77425 var openEnded = serieslen < 2 || (x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]);
77426
77427 if(openEnded && (
77428 (fill === 'tozerox') ||
77429 ((fill === 'tonextx') && (firstScatter || stackOrientation === 'h'))
77430 )) {
77431 // include zero (tight) and extremes (padded) if fill to zero
77432 // (unless the shape is closed, then it's just filling the shape regardless)
77433
77434 xOptions.tozero = true;
77435 } else if(!(trace.error_y || {}).visible && (
77436 // if no error bars, markers or text, or fill to y=0 remove x padding
77437
77438 (fill === 'tonexty' || fill === 'tozeroy') ||
77439 (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace))
77440 )) {
77441 xOptions.padded = false;
77442 xOptions.ppad = 0;
77443 }
77444
77445 if(openEnded && (
77446 (fill === 'tozeroy') ||
77447 ((fill === 'tonexty') && (firstScatter || stackOrientation === 'v'))
77448 )) {
77449 // now check for y - rather different logic, though still mostly padded both ends
77450 // include zero (tight) and extremes (padded) if fill to zero
77451 // (unless the shape is closed, then it's just filling the shape regardless)
77452
77453 yOptions.tozero = true;
77454 } else if(fill === 'tonextx' || fill === 'tozerox') {
77455 // tight y: any x fill
77456
77457 yOptions.padded = false;
77458 }
77459
77460 // N.B. asymmetric splom traces call this with blank {} xa or ya
77461 if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions);
77462 if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions);
77463}
77464
77465function calcMarkerSize(trace, serieslen) {
77466 if(!subTypes.hasMarkers(trace)) return;
77467
77468 // Treat size like x or y arrays --- Run d2c
77469 // this needs to go before ppad computation
77470 var marker = trace.marker;
77471 var sizeref = 1.6 * (trace.marker.sizeref || 1);
77472 var markerTrans;
77473
77474 if(trace.marker.sizemode === 'area') {
77475 markerTrans = function(v) {
77476 return Math.max(Math.sqrt((v || 0) / sizeref), 3);
77477 };
77478 } else {
77479 markerTrans = function(v) {
77480 return Math.max((v || 0) / sizeref, 3);
77481 };
77482 }
77483
77484 if(Lib.isArrayOrTypedArray(marker.size)) {
77485 // I tried auto-type but category and dates dont make much sense.
77486 var ax = {type: 'linear'};
77487 Axes.setConvert(ax);
77488
77489 var s = ax.makeCalcdata(trace.marker, 'size');
77490
77491 var sizeOut = new Array(serieslen);
77492 for(var i = 0; i < serieslen; i++) {
77493 sizeOut[i] = markerTrans(s[i]);
77494 }
77495 return sizeOut;
77496 } else {
77497 return markerTrans(marker.size);
77498 }
77499}
77500
77501/**
77502 * mark the first scatter trace for each subplot
77503 * note that scatter and scattergl each get their own first trace
77504 * note also that I'm doing this during calc rather than supplyDefaults
77505 * so I don't need to worry about transforms, but if we ever do
77506 * per-trace calc this will get confused.
77507 */
77508function setFirstScatter(fullLayout, trace) {
77509 var group = firstScatterGroup(trace);
77510 var firstScatter = fullLayout._firstScatter;
77511 if(!firstScatter[group]) firstScatter[group] = trace.uid;
77512}
77513
77514function firstScatterGroup(trace) {
77515 var stackGroup = trace.stackgroup;
77516 return trace.xaxis + trace.yaxis + trace.type +
77517 (stackGroup ? '-' + stackGroup : '');
77518}
77519
77520function getStackOpts(trace, fullLayout, xa, ya) {
77521 var stackGroup = trace.stackgroup;
77522 if(!stackGroup) return;
77523 var stackOpts = fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup];
77524 var stackAx = stackOpts.orientation === 'v' ? ya : xa;
77525 // Allow stacking only on numeric axes
77526 // calc is a little late to be figuring this out, but during supplyDefaults
77527 // we don't know the axis type yet
77528 if(stackAx.type === 'linear' || stackAx.type === 'log') return stackOpts;
77529}
77530
77531module.exports = {
77532 calc: calc,
77533 calcMarkerSize: calcMarkerSize,
77534 calcAxisExpansion: calcAxisExpansion,
77535 setFirstScatter: setFirstScatter,
77536 getStackOpts: getStackOpts
77537};
77538
77539},{"../../constants/numerical":155,"../../lib":177,"../../plots/cartesian/axes":222,"./arrays_to_calcdata":293,"./calc_selection":296,"./colorscale_calc":297,"./subtypes":318,"fast-isnumeric":15}],296:[function(_dereq_,module,exports){
77540/**
77541* Copyright 2012-2020, Plotly, Inc.
77542* All rights reserved.
77543*
77544* This source code is licensed under the MIT license found in the
77545* LICENSE file in the root directory of this source tree.
77546*/
77547
77548'use strict';
77549
77550var Lib = _dereq_('../../lib');
77551
77552module.exports = function calcSelection(cd, trace) {
77553 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
77554 Lib.tagSelected(cd, trace);
77555 }
77556};
77557
77558},{"../../lib":177}],297:[function(_dereq_,module,exports){
77559/**
77560* Copyright 2012-2020, Plotly, Inc.
77561* All rights reserved.
77562*
77563* This source code is licensed under the MIT license found in the
77564* LICENSE file in the root directory of this source tree.
77565*/
77566
77567'use strict';
77568
77569var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
77570var calcColorscale = _dereq_('../../components/colorscale/calc');
77571
77572var subTypes = _dereq_('./subtypes');
77573
77574module.exports = function calcMarkerColorscale(gd, trace) {
77575 if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) {
77576 calcColorscale(gd, trace, {
77577 vals: trace.line.color,
77578 containerStr: 'line',
77579 cLetter: 'c'
77580 });
77581 }
77582
77583 if(subTypes.hasMarkers(trace)) {
77584 if(hasColorscale(trace, 'marker')) {
77585 calcColorscale(gd, trace, {
77586 vals: trace.marker.color,
77587 containerStr: 'marker',
77588 cLetter: 'c'
77589 });
77590 }
77591 if(hasColorscale(trace, 'marker.line')) {
77592 calcColorscale(gd, trace, {
77593 vals: trace.marker.line.color,
77594 containerStr: 'marker.line',
77595 cLetter: 'c'
77596 });
77597 }
77598 }
77599};
77600
77601},{"../../components/colorscale/calc":58,"../../components/colorscale/helpers":61,"./subtypes":318}],298:[function(_dereq_,module,exports){
77602/**
77603* Copyright 2012-2020, Plotly, Inc.
77604* All rights reserved.
77605*
77606* This source code is licensed under the MIT license found in the
77607* LICENSE file in the root directory of this source tree.
77608*/
77609
77610
77611'use strict';
77612
77613module.exports = {
77614 PTS_LINESONLY: 20,
77615
77616 // fixed parameters of clustering and clipping algorithms
77617
77618 // fraction of clustering tolerance "so close we don't even consider it a new point"
77619 minTolerance: 0.2,
77620 // how fast does clustering tolerance increase as you get away from the visible region
77621 toleranceGrowth: 10,
77622
77623 // number of viewport sizes away from the visible region
77624 // at which we clip all lines to the perimeter
77625 maxScreensAway: 20,
77626
77627 eventDataKeys: []
77628};
77629
77630},{}],299:[function(_dereq_,module,exports){
77631/**
77632* Copyright 2012-2020, Plotly, Inc.
77633* All rights reserved.
77634*
77635* This source code is licensed under the MIT license found in the
77636* LICENSE file in the root directory of this source tree.
77637*/
77638
77639
77640'use strict';
77641
77642var calc = _dereq_('./calc');
77643
77644/*
77645 * Scatter stacking & normalization calculations
77646 * runs per subplot, and can handle multiple stacking groups
77647 */
77648
77649module.exports = function crossTraceCalc(gd, plotinfo) {
77650 var xa = plotinfo.xaxis;
77651 var ya = plotinfo.yaxis;
77652 var subplot = xa._id + ya._id;
77653
77654 var subplotStackOpts = gd._fullLayout._scatterStackOpts[subplot];
77655 if(!subplotStackOpts) return;
77656
77657 var calcTraces = gd.calcdata;
77658
77659 var i, j, k, i2, cd, cd0, posj, sumj, norm;
77660 var groupOpts, interpolate, groupnorm, posAttr, valAttr;
77661 var hasAnyBlanks;
77662
77663 for(var stackGroup in subplotStackOpts) {
77664 groupOpts = subplotStackOpts[stackGroup];
77665 var indices = groupOpts.traceIndices;
77666
77667 // can get here with no indices if the stack axis is non-numeric
77668 if(!indices.length) continue;
77669
77670 interpolate = groupOpts.stackgaps === 'interpolate';
77671 groupnorm = groupOpts.groupnorm;
77672 if(groupOpts.orientation === 'v') {
77673 posAttr = 'x';
77674 valAttr = 'y';
77675 } else {
77676 posAttr = 'y';
77677 valAttr = 'x';
77678 }
77679 hasAnyBlanks = new Array(indices.length);
77680 for(i = 0; i < hasAnyBlanks.length; i++) {
77681 hasAnyBlanks[i] = false;
77682 }
77683
77684 // Collect the complete set of all positions across ALL traces.
77685 // Start with the first trace, then interleave items from later traces
77686 // as needed.
77687 // Fill in mising items as we go.
77688 cd0 = calcTraces[indices[0]];
77689 var allPositions = new Array(cd0.length);
77690 for(i = 0; i < cd0.length; i++) {
77691 allPositions[i] = cd0[i][posAttr];
77692 }
77693
77694 for(i = 1; i < indices.length; i++) {
77695 cd = calcTraces[indices[i]];
77696
77697 for(j = k = 0; j < cd.length; j++) {
77698 posj = cd[j][posAttr];
77699 for(; posj > allPositions[k] && k < allPositions.length; k++) {
77700 // the current trace is missing a position from some previous trace(s)
77701 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
77702 j++;
77703 }
77704 if(posj !== allPositions[k]) {
77705 // previous trace(s) are missing a position from the current trace
77706 for(i2 = 0; i2 < i; i2++) {
77707 insertBlank(calcTraces[indices[i2]], k, posj, i2, hasAnyBlanks, interpolate, posAttr);
77708 }
77709 allPositions.splice(k, 0, posj);
77710 }
77711 k++;
77712 }
77713 for(; k < allPositions.length; k++) {
77714 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
77715 j++;
77716 }
77717 }
77718
77719 var serieslen = allPositions.length;
77720
77721 // stack (and normalize)!
77722 for(j = 0; j < cd0.length; j++) {
77723 sumj = cd0[j][valAttr] = cd0[j].s;
77724 for(i = 1; i < indices.length; i++) {
77725 cd = calcTraces[indices[i]];
77726 cd[0].trace._rawLength = cd[0].trace._length;
77727 cd[0].trace._length = serieslen;
77728 sumj += cd[j].s;
77729 cd[j][valAttr] = sumj;
77730 }
77731
77732 if(groupnorm) {
77733 norm = ((groupnorm === 'fraction') ? sumj : (sumj / 100)) || 1;
77734 for(i = 0; i < indices.length; i++) {
77735 var cdj = calcTraces[indices[i]][j];
77736 cdj[valAttr] /= norm;
77737 cdj.sNorm = cdj.s / norm;
77738 }
77739 }
77740 }
77741
77742 // autorange
77743 for(i = 0; i < indices.length; i++) {
77744 cd = calcTraces[indices[i]];
77745 var trace = cd[0].trace;
77746 var ppad = calc.calcMarkerSize(trace, trace._rawLength);
77747 var arrayPad = Array.isArray(ppad);
77748 if((ppad && hasAnyBlanks[i]) || arrayPad) {
77749 var ppadRaw = ppad;
77750 ppad = new Array(serieslen);
77751 for(j = 0; j < serieslen; j++) {
77752 ppad[j] = cd[j].gap ? 0 : (arrayPad ? ppadRaw[cd[j].i] : ppadRaw);
77753 }
77754 }
77755 var x = new Array(serieslen);
77756 var y = new Array(serieslen);
77757 for(j = 0; j < serieslen; j++) {
77758 x[j] = cd[j].x;
77759 y[j] = cd[j].y;
77760 }
77761 calc.calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
77762
77763 // while we're here (in a loop over all traces in the stack)
77764 // record the orientation, so hover can find it easily
77765 cd[0].t.orientation = groupOpts.orientation;
77766 }
77767 }
77768};
77769
77770function insertBlank(calcTrace, index, position, traceIndex, hasAnyBlanks, interpolate, posAttr) {
77771 hasAnyBlanks[traceIndex] = true;
77772 var newEntry = {
77773 i: null,
77774 gap: true,
77775 s: 0
77776 };
77777 newEntry[posAttr] = position;
77778 calcTrace.splice(index, 0, newEntry);
77779 // Even if we're not interpolating, if one trace has multiple
77780 // values at the same position and this trace only has one value there,
77781 // we just duplicate that one value rather than insert a zero.
77782 // We also make it look like a real point - because it's ambiguous which
77783 // one really is the real one!
77784 if(index && position === calcTrace[index - 1][posAttr]) {
77785 var prevEntry = calcTrace[index - 1];
77786 newEntry.s = prevEntry.s;
77787 // TODO is it going to cause any problems to have multiple
77788 // calcdata points with the same index?
77789 newEntry.i = prevEntry.i;
77790 newEntry.gap = prevEntry.gap;
77791 } else if(interpolate) {
77792 newEntry.s = getInterp(calcTrace, index, position, posAttr);
77793 }
77794 if(!index) {
77795 // t and trace need to stay on the first cd entry
77796 calcTrace[0].t = calcTrace[1].t;
77797 calcTrace[0].trace = calcTrace[1].trace;
77798 delete calcTrace[1].t;
77799 delete calcTrace[1].trace;
77800 }
77801}
77802
77803function getInterp(calcTrace, index, position, posAttr) {
77804 var pt0 = calcTrace[index - 1];
77805 var pt1 = calcTrace[index + 1];
77806 if(!pt1) return pt0.s;
77807 if(!pt0) return pt1.s;
77808 return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]);
77809}
77810
77811},{"./calc":295}],300:[function(_dereq_,module,exports){
77812/**
77813* Copyright 2012-2020, Plotly, Inc.
77814* All rights reserved.
77815*
77816* This source code is licensed under the MIT license found in the
77817* LICENSE file in the root directory of this source tree.
77818*/
77819
77820
77821'use strict';
77822
77823
77824// remove opacity for any trace that has a fill or is filled to
77825module.exports = function crossTraceDefaults(fullData) {
77826 for(var i = 0; i < fullData.length; i++) {
77827 var tracei = fullData[i];
77828 if(tracei.type !== 'scatter') continue;
77829
77830 var filli = tracei.fill;
77831 if(filli === 'none' || filli === 'toself') continue;
77832
77833 tracei.opacity = undefined;
77834
77835 if(filli === 'tonexty' || filli === 'tonextx') {
77836 for(var j = i - 1; j >= 0; j--) {
77837 var tracej = fullData[j];
77838
77839 if((tracej.type === 'scatter') &&
77840 (tracej.xaxis === tracei.xaxis) &&
77841 (tracej.yaxis === tracei.yaxis)) {
77842 tracej.opacity = undefined;
77843 break;
77844 }
77845 }
77846 }
77847 }
77848};
77849
77850},{}],301:[function(_dereq_,module,exports){
77851/**
77852* Copyright 2012-2020, Plotly, Inc.
77853* All rights reserved.
77854*
77855* This source code is licensed under the MIT license found in the
77856* LICENSE file in the root directory of this source tree.
77857*/
77858
77859'use strict';
77860
77861var Lib = _dereq_('../../lib');
77862var Registry = _dereq_('../../registry');
77863
77864var attributes = _dereq_('./attributes');
77865var constants = _dereq_('./constants');
77866var subTypes = _dereq_('./subtypes');
77867var handleXYDefaults = _dereq_('./xy_defaults');
77868var handleStackDefaults = _dereq_('./stack_defaults');
77869var handleMarkerDefaults = _dereq_('./marker_defaults');
77870var handleLineDefaults = _dereq_('./line_defaults');
77871var handleLineShapeDefaults = _dereq_('./line_shape_defaults');
77872var handleTextDefaults = _dereq_('./text_defaults');
77873var handleFillColorDefaults = _dereq_('./fillcolor_defaults');
77874
77875module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
77876 function coerce(attr, dflt) {
77877 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
77878 }
77879
77880 var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
77881 if(!len) traceOut.visible = false;
77882
77883 if(!traceOut.visible) return;
77884
77885 var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
77886
77887 var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
77888 'lines+markers' : 'lines';
77889 coerce('text');
77890 coerce('hovertext');
77891 coerce('mode', defaultMode);
77892
77893 if(subTypes.hasLines(traceOut)) {
77894 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
77895 handleLineShapeDefaults(traceIn, traceOut, coerce);
77896 coerce('connectgaps');
77897 coerce('line.simplify');
77898 }
77899
77900 if(subTypes.hasMarkers(traceOut)) {
77901 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
77902 }
77903
77904 if(subTypes.hasText(traceOut)) {
77905 coerce('texttemplate');
77906 handleTextDefaults(traceIn, traceOut, layout, coerce);
77907 }
77908
77909 var dfltHoverOn = [];
77910
77911 if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
77912 coerce('cliponaxis');
77913 coerce('marker.maxdisplayed');
77914 dfltHoverOn.push('points');
77915 }
77916
77917 // It's possible for this default to be changed by a later trace.
77918 // We handle that case in some hacky code inside handleStackDefaults.
77919 coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none');
77920 if(traceOut.fill !== 'none') {
77921 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
77922 if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
77923 }
77924
77925 var lineColor = (traceOut.line || {}).color;
77926 var markerColor = (traceOut.marker || {}).color;
77927
77928 if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
77929 dfltHoverOn.push('fills');
77930 }
77931 coerce('hoveron', dfltHoverOn.join('+') || 'points');
77932 if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
77933 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
77934 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'});
77935 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'});
77936
77937 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
77938};
77939
77940},{"../../lib":177,"../../registry":272,"./attributes":294,"./constants":298,"./fillcolor_defaults":302,"./line_defaults":307,"./line_shape_defaults":309,"./marker_defaults":313,"./stack_defaults":316,"./subtypes":318,"./text_defaults":319,"./xy_defaults":320}],302:[function(_dereq_,module,exports){
77941/**
77942* Copyright 2012-2020, Plotly, Inc.
77943* All rights reserved.
77944*
77945* This source code is licensed under the MIT license found in the
77946* LICENSE file in the root directory of this source tree.
77947*/
77948
77949
77950'use strict';
77951
77952var Color = _dereq_('../../components/color');
77953var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
77954
77955module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) {
77956 var inheritColorFromMarker = false;
77957
77958 if(traceOut.marker) {
77959 // don't try to inherit a color array
77960 var markerColor = traceOut.marker.color;
77961 var markerLineColor = (traceOut.marker.line || {}).color;
77962
77963 if(markerColor && !isArrayOrTypedArray(markerColor)) {
77964 inheritColorFromMarker = markerColor;
77965 } else if(markerLineColor && !isArrayOrTypedArray(markerLineColor)) {
77966 inheritColorFromMarker = markerLineColor;
77967 }
77968 }
77969
77970 coerce('fillcolor', Color.addOpacity(
77971 (traceOut.line || {}).color ||
77972 inheritColorFromMarker ||
77973 defaultColor, 0.5
77974 ));
77975};
77976
77977},{"../../components/color":50,"../../lib":177}],303:[function(_dereq_,module,exports){
77978/**
77979* Copyright 2012-2020, Plotly, Inc.
77980* All rights reserved.
77981*
77982* This source code is licensed under the MIT license found in the
77983* LICENSE file in the root directory of this source tree.
77984*/
77985
77986'use strict';
77987
77988var Axes = _dereq_('../../plots/cartesian/axes');
77989
77990module.exports = function formatLabels(cdi, trace, fullLayout) {
77991 var labels = {};
77992
77993 var mockGd = {_fullLayout: fullLayout};
77994 var xa = Axes.getFromTrace(mockGd, trace, 'x');
77995 var ya = Axes.getFromTrace(mockGd, trace, 'y');
77996
77997 labels.xLabel = Axes.tickText(xa, cdi.x, true).text;
77998 labels.yLabel = Axes.tickText(ya, cdi.y, true).text;
77999
78000 return labels;
78001};
78002
78003},{"../../plots/cartesian/axes":222}],304:[function(_dereq_,module,exports){
78004/**
78005* Copyright 2012-2020, Plotly, Inc.
78006* All rights reserved.
78007*
78008* This source code is licensed under the MIT license found in the
78009* LICENSE file in the root directory of this source tree.
78010*/
78011
78012
78013'use strict';
78014
78015var Color = _dereq_('../../components/color');
78016var subtypes = _dereq_('./subtypes');
78017
78018
78019module.exports = function getTraceColor(trace, di) {
78020 var lc, tc;
78021
78022 // TODO: text modes
78023
78024 if(trace.mode === 'lines') {
78025 lc = trace.line.color;
78026 return (lc && Color.opacity(lc)) ?
78027 lc : trace.fillcolor;
78028 } else if(trace.mode === 'none') {
78029 return trace.fill ? trace.fillcolor : '';
78030 } else {
78031 var mc = di.mcc || (trace.marker || {}).color;
78032 var mlc = di.mlcc || ((trace.marker || {}).line || {}).color;
78033
78034 tc = (mc && Color.opacity(mc)) ? mc :
78035 (mlc && Color.opacity(mlc) &&
78036 (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : '';
78037
78038 if(tc) {
78039 // make sure the points aren't TOO transparent
78040 if(Color.opacity(tc) < 0.3) {
78041 return Color.addOpacity(tc, 0.3);
78042 } else return tc;
78043 } else {
78044 lc = (trace.line || {}).color;
78045 return (lc && Color.opacity(lc) &&
78046 subtypes.hasLines(trace) && trace.line.width) ?
78047 lc : trace.fillcolor;
78048 }
78049 }
78050};
78051
78052},{"../../components/color":50,"./subtypes":318}],305:[function(_dereq_,module,exports){
78053/**
78054* Copyright 2012-2020, Plotly, Inc.
78055* All rights reserved.
78056*
78057* This source code is licensed under the MIT license found in the
78058* LICENSE file in the root directory of this source tree.
78059*/
78060
78061'use strict';
78062
78063var Lib = _dereq_('../../lib');
78064var Fx = _dereq_('../../components/fx');
78065var Registry = _dereq_('../../registry');
78066var getTraceColor = _dereq_('./get_trace_color');
78067var Color = _dereq_('../../components/color');
78068var fillText = Lib.fillText;
78069
78070module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
78071 var cd = pointData.cd;
78072 var trace = cd[0].trace;
78073 var xa = pointData.xa;
78074 var ya = pointData.ya;
78075 var xpx = xa.c2p(xval);
78076 var ypx = ya.c2p(yval);
78077 var pt = [xpx, ypx];
78078 var hoveron = trace.hoveron || '';
78079 var minRad = (trace.mode.indexOf('markers') !== -1) ? 3 : 0.5;
78080
78081 // look for points to hover on first, then take fills only if we
78082 // didn't find a point
78083 if(hoveron.indexOf('points') !== -1) {
78084 var dx = function(di) {
78085 // dx and dy are used in compare modes - here we want to always
78086 // prioritize the closest data point, at least as long as markers are
78087 // the same size or nonexistent, but still try to prioritize small markers too.
78088 var rad = Math.max(3, di.mrc || 0);
78089 var kink = 1 - 1 / rad;
78090 var dxRaw = Math.abs(xa.c2p(di.x) - xpx);
78091 var d = (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
78092 return d;
78093 };
78094 var dy = function(di) {
78095 var rad = Math.max(3, di.mrc || 0);
78096 var kink = 1 - 1 / rad;
78097 var dyRaw = Math.abs(ya.c2p(di.y) - ypx);
78098 return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink);
78099 };
78100 var dxy = function(di) {
78101 // scatter points: d.mrc is the calculated marker radius
78102 // adjust the distance so if you're inside the marker it
78103 // always will show up regardless of point size, but
78104 // prioritize smaller points
78105 var rad = Math.max(minRad, di.mrc || 0);
78106 var dx = xa.c2p(di.x) - xpx;
78107 var dy = ya.c2p(di.y) - ypx;
78108 return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad);
78109 };
78110 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
78111
78112 Fx.getClosest(cd, distfn, pointData);
78113
78114 // skip the rest (for this trace) if we didn't find a close point
78115 if(pointData.index !== false) {
78116 // the closest data point
78117 var di = cd[pointData.index];
78118 var xc = xa.c2p(di.x, true);
78119 var yc = ya.c2p(di.y, true);
78120 var rad = di.mrc || 1;
78121
78122 // now we're done using the whole `calcdata` array, replace the
78123 // index with the original index (in case of inserted point from
78124 // stacked area)
78125 pointData.index = di.i;
78126
78127 var orientation = cd[0].t.orientation;
78128 // TODO: for scatter and bar, option to show (sub)totals and
78129 // raw data? Currently stacked and/or normalized bars just show
78130 // the normalized individual sizes, so that's what I'm doing here
78131 // for now.
78132 var sizeVal = orientation && (di.sNorm || di.s);
78133 var xLabelVal = (orientation === 'h') ? sizeVal : di.x;
78134 var yLabelVal = (orientation === 'v') ? sizeVal : di.y;
78135
78136 Lib.extendFlat(pointData, {
78137 color: getTraceColor(trace, di),
78138
78139 x0: xc - rad,
78140 x1: xc + rad,
78141 xLabelVal: xLabelVal,
78142
78143 y0: yc - rad,
78144 y1: yc + rad,
78145 yLabelVal: yLabelVal,
78146
78147 spikeDistance: dxy(di),
78148 hovertemplate: trace.hovertemplate
78149 });
78150
78151 fillText(di, trace, pointData);
78152 Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
78153
78154 return [pointData];
78155 }
78156 }
78157
78158 // even if hoveron is 'fills', only use it if we have polygons too
78159 if(hoveron.indexOf('fills') !== -1 && trace._polygons) {
78160 var polygons = trace._polygons;
78161 var polygonsIn = [];
78162 var inside = false;
78163 var xmin = Infinity;
78164 var xmax = -Infinity;
78165 var ymin = Infinity;
78166 var ymax = -Infinity;
78167
78168 var i, j, polygon, pts, xCross, x0, x1, y0, y1;
78169
78170 for(i = 0; i < polygons.length; i++) {
78171 polygon = polygons[i];
78172 // TODO: this is not going to work right for curved edges, it will
78173 // act as though they're straight. That's probably going to need
78174 // the elements themselves to capture the events. Worth it?
78175 if(polygon.contains(pt)) {
78176 inside = !inside;
78177 // TODO: need better than just the overall bounding box
78178 polygonsIn.push(polygon);
78179 ymin = Math.min(ymin, polygon.ymin);
78180 ymax = Math.max(ymax, polygon.ymax);
78181 }
78182 }
78183
78184 if(inside) {
78185 // constrain ymin/max to the visible plot, so the label goes
78186 // at the middle of the piece you can see
78187 ymin = Math.max(ymin, 0);
78188 ymax = Math.min(ymax, ya._length);
78189
78190 // find the overall left-most and right-most points of the
78191 // polygon(s) we're inside at their combined vertical midpoint.
78192 // This is where we will draw the hover label.
78193 // Note that this might not be the vertical midpoint of the
78194 // whole trace, if it's disjoint.
78195 var yAvg = (ymin + ymax) / 2;
78196 for(i = 0; i < polygonsIn.length; i++) {
78197 pts = polygonsIn[i].pts;
78198 for(j = 1; j < pts.length; j++) {
78199 y0 = pts[j - 1][1];
78200 y1 = pts[j][1];
78201 if((y0 > yAvg) !== (y1 >= yAvg)) {
78202 x0 = pts[j - 1][0];
78203 x1 = pts[j][0];
78204 if(y1 - y0) {
78205 xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0);
78206 xmin = Math.min(xmin, xCross);
78207 xmax = Math.max(xmax, xCross);
78208 }
78209 }
78210 }
78211 }
78212
78213 // constrain xmin/max to the visible plot now too
78214 xmin = Math.max(xmin, 0);
78215 xmax = Math.min(xmax, xa._length);
78216
78217 // get only fill or line color for the hover color
78218 var color = Color.defaultLine;
78219 if(Color.opacity(trace.fillcolor)) color = trace.fillcolor;
78220 else if(Color.opacity((trace.line || {}).color)) {
78221 color = trace.line.color;
78222 }
78223
78224 Lib.extendFlat(pointData, {
78225 // never let a 2D override 1D type as closest point
78226 // also: no spikeDistance, it's not allowed for fills
78227 distance: pointData.maxHoverDistance,
78228 x0: xmin,
78229 x1: xmax,
78230 y0: yAvg,
78231 y1: yAvg,
78232 color: color,
78233 hovertemplate: false
78234 });
78235
78236 delete pointData.index;
78237
78238 if(trace.text && !Array.isArray(trace.text)) {
78239 pointData.text = String(trace.text);
78240 } else pointData.text = trace.name;
78241
78242 return [pointData];
78243 }
78244 }
78245};
78246
78247},{"../../components/color":50,"../../components/fx":90,"../../lib":177,"../../registry":272,"./get_trace_color":304}],306:[function(_dereq_,module,exports){
78248/**
78249* Copyright 2012-2020, Plotly, Inc.
78250* All rights reserved.
78251*
78252* This source code is licensed under the MIT license found in the
78253* LICENSE file in the root directory of this source tree.
78254*/
78255
78256'use strict';
78257
78258var subtypes = _dereq_('./subtypes');
78259
78260module.exports = {
78261 hasLines: subtypes.hasLines,
78262 hasMarkers: subtypes.hasMarkers,
78263 hasText: subtypes.hasText,
78264 isBubble: subtypes.isBubble,
78265
78266 attributes: _dereq_('./attributes'),
78267 supplyDefaults: _dereq_('./defaults'),
78268 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
78269 calc: _dereq_('./calc').calc,
78270 crossTraceCalc: _dereq_('./cross_trace_calc'),
78271 arraysToCalcdata: _dereq_('./arrays_to_calcdata'),
78272 plot: _dereq_('./plot'),
78273 colorbar: _dereq_('./marker_colorbar'),
78274 formatLabels: _dereq_('./format_labels'),
78275 style: _dereq_('./style').style,
78276 styleOnSelect: _dereq_('./style').styleOnSelect,
78277 hoverPoints: _dereq_('./hover'),
78278 selectPoints: _dereq_('./select'),
78279 animatable: true,
78280
78281 moduleType: 'trace',
78282 name: 'scatter',
78283 basePlotModule: _dereq_('../../plots/cartesian'),
78284 categories: [
78285 'cartesian', 'svg', 'symbols', 'errorBarsOK', 'showLegend', 'scatter-like',
78286 'zoomScale'
78287 ],
78288 meta: {
78289
78290 }
78291};
78292
78293},{"../../plots/cartesian":235,"./arrays_to_calcdata":293,"./attributes":294,"./calc":295,"./cross_trace_calc":299,"./cross_trace_defaults":300,"./defaults":301,"./format_labels":303,"./hover":305,"./marker_colorbar":312,"./plot":314,"./select":315,"./style":317,"./subtypes":318}],307:[function(_dereq_,module,exports){
78294/**
78295* Copyright 2012-2020, Plotly, Inc.
78296* All rights reserved.
78297*
78298* This source code is licensed under the MIT license found in the
78299* LICENSE file in the root directory of this source tree.
78300*/
78301
78302'use strict';
78303
78304var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
78305var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
78306var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
78307
78308module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
78309 var markerColor = (traceIn.marker || {}).color;
78310
78311 coerce('line.color', defaultColor);
78312
78313 if(hasColorscale(traceIn, 'line')) {
78314 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'line.', cLetter: 'c'});
78315 } else {
78316 var lineColorDflt = (isArrayOrTypedArray(markerColor) ? false : markerColor) || defaultColor;
78317 coerce('line.color', lineColorDflt);
78318 }
78319
78320 coerce('line.width');
78321 if(!(opts || {}).noDash) coerce('line.dash');
78322};
78323
78324},{"../../components/colorscale/defaults":60,"../../components/colorscale/helpers":61,"../../lib":177}],308:[function(_dereq_,module,exports){
78325/**
78326* Copyright 2012-2020, Plotly, Inc.
78327* All rights reserved.
78328*
78329* This source code is licensed under the MIT license found in the
78330* LICENSE file in the root directory of this source tree.
78331*/
78332
78333
78334'use strict';
78335
78336var numConstants = _dereq_('../../constants/numerical');
78337var BADNUM = numConstants.BADNUM;
78338var LOG_CLIP = numConstants.LOG_CLIP;
78339var LOG_CLIP_PLUS = LOG_CLIP + 0.5;
78340var LOG_CLIP_MINUS = LOG_CLIP - 0.5;
78341var Lib = _dereq_('../../lib');
78342var segmentsIntersect = Lib.segmentsIntersect;
78343var constrain = Lib.constrain;
78344var constants = _dereq_('./constants');
78345
78346
78347module.exports = function linePoints(d, opts) {
78348 var xa = opts.xaxis;
78349 var ya = opts.yaxis;
78350 var xLog = xa.type === 'log';
78351 var yLog = ya.type === 'log';
78352 var xLen = xa._length;
78353 var yLen = ya._length;
78354 var connectGaps = opts.connectGaps;
78355 var baseTolerance = opts.baseTolerance;
78356 var shape = opts.shape;
78357 var linear = shape === 'linear';
78358 var fill = opts.fill && opts.fill !== 'none';
78359 var segments = [];
78360 var minTolerance = constants.minTolerance;
78361 var len = d.length;
78362 var pts = new Array(len);
78363 var pti = 0;
78364
78365 var i;
78366
78367 // pt variables are pixel coordinates [x,y] of one point
78368 // these four are the outputs of clustering on a line
78369 var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt;
78370
78371 // "this" is the next point we're considering adding to the cluster
78372 var thisPt;
78373
78374 // did we encounter the high point first, then a low point, or vice versa?
78375 var clusterHighFirst;
78376
78377 // the first two points in the cluster determine its unit vector
78378 // so the second is always in the "High" direction
78379 var clusterUnitVector;
78380
78381 // the pixel delta from clusterStartPt
78382 var thisVector;
78383
78384 // val variables are (signed) pixel distances along the cluster vector
78385 var clusterRefDist, clusterHighVal, clusterLowVal, thisVal;
78386
78387 // deviation variables are (signed) pixel distances normal to the cluster vector
78388 var clusterMinDeviation, clusterMaxDeviation, thisDeviation;
78389
78390 // turn one calcdata point into pixel coordinates
78391 function getPt(index) {
78392 var di = d[index];
78393 if(!di) return false;
78394 var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x);
78395 var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y);
78396
78397 // if non-positive log values, set them VERY far off-screen
78398 // so the line looks essentially straight from the previous point.
78399 if(x === BADNUM) {
78400 if(xLog) x = xa.c2p(di.x, true);
78401 if(x === BADNUM) return false;
78402 // If BOTH were bad log values, make the line follow a constant
78403 // exponent rather than a constant slope
78404 if(yLog && y === BADNUM) {
78405 x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) /
78406 (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS)));
78407 }
78408 x *= 1000;
78409 }
78410 if(y === BADNUM) {
78411 if(yLog) y = ya.c2p(di.y, true);
78412 if(y === BADNUM) return false;
78413 y *= 1000;
78414 }
78415 return [x, y];
78416 }
78417
78418 function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) {
78419 var dx = xFrac1 - xFrac0;
78420 var dy = yFrac1 - yFrac0;
78421 var dx0 = 0.5 - xFrac0;
78422 var dy0 = 0.5 - yFrac0;
78423 var norm2 = dx * dx + dy * dy;
78424 var dot = dx * dx0 + dy * dy0;
78425 if(dot > 0 && dot < norm2) {
78426 var cross = dx0 * dy - dy0 * dx;
78427 if(cross * cross < norm2) return true;
78428 }
78429 }
78430
78431 var latestXFrac, latestYFrac;
78432 // if we're off-screen, increase tolerance over baseTolerance
78433 function getTolerance(pt, nextPt) {
78434 var xFrac = pt[0] / xLen;
78435 var yFrac = pt[1] / yLen;
78436 var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1);
78437 if(offScreenFraction && (latestXFrac !== undefined) &&
78438 crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac)
78439 ) {
78440 offScreenFraction = 0;
78441 }
78442 if(offScreenFraction && nextPt &&
78443 crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen)
78444 ) {
78445 offScreenFraction = 0;
78446 }
78447
78448 return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance;
78449 }
78450
78451 function ptDist(pt1, pt2) {
78452 var dx = pt1[0] - pt2[0];
78453 var dy = pt1[1] - pt2[1];
78454 return Math.sqrt(dx * dx + dy * dy);
78455 }
78456
78457 // last bit of filtering: clip paths that are VERY far off-screen
78458 // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF)
78459
78460 var maxScreensAway = constants.maxScreensAway;
78461
78462 // find the intersections between the segment from pt1 to pt2
78463 // and the large rectangle maxScreensAway around the viewport
78464 // if one of pt1 and pt2 is inside and the other outside, there
78465 // will be only one intersection.
78466 // if both are outside there will be 0 or 2 intersections
78467 // (or 1 if it's right at a corner - we'll treat that like 0)
78468 // returns an array of intersection pts
78469 var xEdge0 = -xLen * maxScreensAway;
78470 var xEdge1 = xLen * (1 + maxScreensAway);
78471 var yEdge0 = -yLen * maxScreensAway;
78472 var yEdge1 = yLen * (1 + maxScreensAway);
78473 var edges = [
78474 [xEdge0, yEdge0, xEdge1, yEdge0],
78475 [xEdge1, yEdge0, xEdge1, yEdge1],
78476 [xEdge1, yEdge1, xEdge0, yEdge1],
78477 [xEdge0, yEdge1, xEdge0, yEdge0]
78478 ];
78479 var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt;
78480
78481 // for linear line shape, edge intersections should be linearly interpolated
78482 // spline uses this too, which isn't precisely correct but is actually pretty
78483 // good, because Catmull-Rom weights far-away points less in creating the curvature
78484 function getLinearEdgeIntersections(pt1, pt2) {
78485 var out = [];
78486 var ptCount = 0;
78487 for(var i = 0; i < 4; i++) {
78488 var edge = edges[i];
78489 var ptInt = segmentsIntersect(
78490 pt1[0], pt1[1], pt2[0], pt2[1],
78491 edge[0], edge[1], edge[2], edge[3]
78492 );
78493 if(ptInt && (!ptCount ||
78494 Math.abs(ptInt.x - out[0][0]) > 1 ||
78495 Math.abs(ptInt.y - out[0][1]) > 1
78496 )) {
78497 ptInt = [ptInt.x, ptInt.y];
78498 // if we have 2 intersections, make sure the closest one to pt1 comes first
78499 if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt);
78500 else out.push(ptInt);
78501 ptCount++;
78502 }
78503 }
78504 return out;
78505 }
78506
78507 function onlyConstrainedPoint(pt) {
78508 if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) {
78509 return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)];
78510 }
78511 }
78512
78513 function sameEdge(pt1, pt2) {
78514 if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true;
78515 if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true;
78516 }
78517
78518 // for line shapes hv and vh, movement in the two dimensions is decoupled,
78519 // so all we need to do is constrain each dimension independently
78520 function getHVEdgeIntersections(pt1, pt2) {
78521 var out = [];
78522 var ptInt1 = onlyConstrainedPoint(pt1);
78523 var ptInt2 = onlyConstrainedPoint(pt2);
78524 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
78525
78526 if(ptInt1) out.push(ptInt1);
78527 if(ptInt2) out.push(ptInt2);
78528 return out;
78529 }
78530
78531 // hvh and vhv we sometimes have to move one of the intersection points
78532 // out BEYOND the clipping rect, by a maximum of a factor of 2, so that
78533 // the midpoint line is drawn in the right place
78534 function getABAEdgeIntersections(dim, limit0, limit1) {
78535 return function(pt1, pt2) {
78536 var ptInt1 = onlyConstrainedPoint(pt1);
78537 var ptInt2 = onlyConstrainedPoint(pt2);
78538
78539 var out = [];
78540 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
78541
78542 if(ptInt1) out.push(ptInt1);
78543 if(ptInt2) out.push(ptInt2);
78544
78545 var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) -
78546 ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]);
78547 if(midShift) {
78548 var ptToAlter;
78549 if(ptInt1 && ptInt2) {
78550 ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2;
78551 } else ptToAlter = ptInt1 || ptInt2;
78552
78553 ptToAlter[dim] += midShift;
78554 }
78555
78556 return out;
78557 };
78558 }
78559
78560 var getEdgeIntersections;
78561 if(shape === 'linear' || shape === 'spline') {
78562 getEdgeIntersections = getLinearEdgeIntersections;
78563 } else if(shape === 'hv' || shape === 'vh') {
78564 getEdgeIntersections = getHVEdgeIntersections;
78565 } else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1);
78566 else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1);
78567
78568 // a segment pt1->pt2 entirely outside the nearby region:
78569 // find the corner it gets closest to touching
78570 function getClosestCorner(pt1, pt2) {
78571 var dx = pt2[0] - pt1[0];
78572 var m = (pt2[1] - pt1[1]) / dx;
78573 var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx;
78574
78575 if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1];
78576 else return [m > 0 ? xEdge1 : xEdge0, yEdge0];
78577 }
78578
78579 function updateEdge(pt) {
78580 var x = pt[0];
78581 var y = pt[1];
78582 var xSame = x === pts[pti - 1][0];
78583 var ySame = y === pts[pti - 1][1];
78584 // duplicate point?
78585 if(xSame && ySame) return;
78586 if(pti > 1) {
78587 // backtracking along an edge?
78588 var xSame2 = x === pts[pti - 2][0];
78589 var ySame2 = y === pts[pti - 2][1];
78590 if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) {
78591 if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add
78592 else pts[pti - 1] = pt; // not exact: replace the prev pt
78593 } else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) {
78594 if(xSame2) pti--;
78595 else pts[pti - 1] = pt;
78596 } else pts[pti++] = pt;
78597 } else pts[pti++] = pt;
78598 }
78599
78600 function updateEdgesForReentry(pt) {
78601 // if we're outside the nearby region and going back in,
78602 // we may need to loop around a corner point
78603 if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) {
78604 updateEdge([lastXEdge, lastYEdge]);
78605 }
78606 updateEdge(pt);
78607 lastFarPt = null;
78608 lastXEdge = lastYEdge = 0;
78609 }
78610
78611 function addPt(pt) {
78612 latestXFrac = pt[0] / xLen;
78613 latestYFrac = pt[1] / yLen;
78614 // Are we more than maxScreensAway off-screen any direction?
78615 // if so, clip to this box, but in such a way that on-screen
78616 // drawing is unchanged
78617 xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0;
78618 yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0;
78619 if(xEdge || yEdge) {
78620 if(!pti) {
78621 // to get fills right - if first point is far, push it toward the
78622 // screen in whichever direction(s) are far
78623
78624 pts[pti++] = [xEdge || pt[0], yEdge || pt[1]];
78625 } else if(lastFarPt) {
78626 // both this point and the last are outside the nearby region
78627 // check if we're crossing the nearby region
78628 var intersections = getEdgeIntersections(lastFarPt, pt);
78629 if(intersections.length > 1) {
78630 updateEdgesForReentry(intersections[0]);
78631 pts[pti++] = intersections[1];
78632 }
78633 } else {
78634 // we're leaving the nearby region - add the point where we left it
78635
78636 edgePt = getEdgeIntersections(pts[pti - 1], pt)[0];
78637 pts[pti++] = edgePt;
78638 }
78639
78640 var lastPt = pts[pti - 1];
78641 if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) {
78642 // we've gone out beyond a new corner: add the corner too
78643 // so that the next point will take the right winding
78644 if(lastFarPt) {
78645 if(lastXEdge !== xEdge && lastYEdge !== yEdge) {
78646 if(lastXEdge && lastYEdge) {
78647 // we've gone around to an opposite corner - we
78648 // need to add the correct extra corner
78649 // in order to get the right winding
78650 updateEdge(getClosestCorner(lastFarPt, pt));
78651 } else {
78652 // we're coming from a far edge - the extra corner
78653 // we need is determined uniquely by the sectors
78654 updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]);
78655 }
78656 } else if(lastXEdge && lastYEdge) {
78657 updateEdge([lastXEdge, lastYEdge]);
78658 }
78659 }
78660 updateEdge([xEdge, yEdge]);
78661 } else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) {
78662 // we're coming from an edge or far corner to an edge - again the
78663 // extra corner we need is uniquely determined by the sectors
78664 updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]);
78665 }
78666 lastFarPt = pt;
78667 lastXEdge = xEdge;
78668 lastYEdge = yEdge;
78669 } else {
78670 if(lastFarPt) {
78671 // this point is in range but the previous wasn't: add its entry pt first
78672 updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]);
78673 }
78674
78675 pts[pti++] = pt;
78676 }
78677 }
78678
78679 // loop over ALL points in this trace
78680 for(i = 0; i < len; i++) {
78681 clusterStartPt = getPt(i);
78682 if(!clusterStartPt) continue;
78683
78684 pti = 0;
78685 lastFarPt = null;
78686 addPt(clusterStartPt);
78687
78688 // loop over one segment of the trace
78689 for(i++; i < len; i++) {
78690 clusterHighPt = getPt(i);
78691 if(!clusterHighPt) {
78692 if(connectGaps) continue;
78693 else break;
78694 }
78695
78696 // can't decimate if nonlinear line shape
78697 // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again
78698 // but spline would be verrry awkward to decimate
78699 if(!linear || !opts.simplify) {
78700 addPt(clusterHighPt);
78701 continue;
78702 }
78703
78704 var nextPt = getPt(i + 1);
78705
78706 clusterRefDist = ptDist(clusterHighPt, clusterStartPt);
78707
78708 // #3147 - always include the very first and last points for fills
78709 if(!(fill && (pti === 0 || pti === len - 1)) &&
78710 clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue;
78711
78712 clusterUnitVector = [
78713 (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist,
78714 (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist
78715 ];
78716
78717 clusterLowPt = clusterStartPt;
78718 clusterHighVal = clusterRefDist;
78719 clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0;
78720 clusterHighFirst = false;
78721 clusterEndPt = clusterHighPt;
78722
78723 // loop over one cluster of points that collapse onto one line
78724 for(i++; i < d.length; i++) {
78725 thisPt = nextPt;
78726 nextPt = getPt(i + 1);
78727 if(!thisPt) {
78728 if(connectGaps) continue;
78729 else break;
78730 }
78731 thisVector = [
78732 thisPt[0] - clusterStartPt[0],
78733 thisPt[1] - clusterStartPt[1]
78734 ];
78735 // cross product (or dot with normal to the cluster vector)
78736 thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0];
78737 clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation);
78738 clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation);
78739
78740 if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break;
78741
78742 clusterEndPt = thisPt;
78743 thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1];
78744
78745 if(thisVal > clusterHighVal) {
78746 clusterHighVal = thisVal;
78747 clusterHighPt = thisPt;
78748 clusterHighFirst = false;
78749 } else if(thisVal < clusterLowVal) {
78750 clusterLowVal = thisVal;
78751 clusterLowPt = thisPt;
78752 clusterHighFirst = true;
78753 }
78754 }
78755
78756 // insert this cluster into pts
78757 // we've already inserted the start pt, now check if we have high and low pts
78758 if(clusterHighFirst) {
78759 addPt(clusterHighPt);
78760 if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt);
78761 } else {
78762 if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt);
78763 if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt);
78764 }
78765 // and finally insert the end pt
78766 addPt(clusterEndPt);
78767
78768 // have we reached the end of this segment?
78769 if(i >= d.length || !thisPt) break;
78770
78771 // otherwise we have an out-of-cluster point to insert as next clusterStartPt
78772 addPt(thisPt);
78773 clusterStartPt = thisPt;
78774 }
78775
78776 // to get fills right - repeat what we did at the start
78777 if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]);
78778
78779 segments.push(pts.slice(0, pti));
78780 }
78781
78782 return segments;
78783};
78784
78785},{"../../constants/numerical":155,"../../lib":177,"./constants":298}],309:[function(_dereq_,module,exports){
78786/**
78787* Copyright 2012-2020, Plotly, Inc.
78788* All rights reserved.
78789*
78790* This source code is licensed under the MIT license found in the
78791* LICENSE file in the root directory of this source tree.
78792*/
78793
78794
78795'use strict';
78796
78797
78798// common to 'scatter' and 'scatterternary'
78799module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) {
78800 var shape = coerce('line.shape');
78801 if(shape === 'spline') coerce('line.smoothing');
78802};
78803
78804},{}],310:[function(_dereq_,module,exports){
78805/**
78806* Copyright 2012-2020, Plotly, Inc.
78807* All rights reserved.
78808*
78809* This source code is licensed under the MIT license found in the
78810* LICENSE file in the root directory of this source tree.
78811*/
78812
78813'use strict';
78814
78815var LINKEDFILLS = {tonextx: 1, tonexty: 1, tonext: 1};
78816
78817module.exports = function linkTraces(gd, plotinfo, cdscatter) {
78818 var trace, i, group, prevtrace, groupIndex;
78819
78820 // first sort traces to keep stacks & filled-together groups together
78821 var groupIndices = {};
78822 var needsSort = false;
78823 var prevGroupIndex = -1;
78824 var nextGroupIndex = 0;
78825 var prevUnstackedGroupIndex = -1;
78826 for(i = 0; i < cdscatter.length; i++) {
78827 trace = cdscatter[i][0].trace;
78828 group = trace.stackgroup || '';
78829 if(group) {
78830 if(group in groupIndices) {
78831 groupIndex = groupIndices[group];
78832 } else {
78833 groupIndex = groupIndices[group] = nextGroupIndex;
78834 nextGroupIndex++;
78835 }
78836 } else if(trace.fill in LINKEDFILLS && prevUnstackedGroupIndex >= 0) {
78837 groupIndex = prevUnstackedGroupIndex;
78838 } else {
78839 groupIndex = prevUnstackedGroupIndex = nextGroupIndex;
78840 nextGroupIndex++;
78841 }
78842
78843 if(groupIndex < prevGroupIndex) needsSort = true;
78844 trace._groupIndex = prevGroupIndex = groupIndex;
78845 }
78846
78847 var cdscatterSorted = cdscatter.slice();
78848 if(needsSort) {
78849 cdscatterSorted.sort(function(a, b) {
78850 var traceA = a[0].trace;
78851 var traceB = b[0].trace;
78852 return (traceA._groupIndex - traceB._groupIndex) ||
78853 (traceA.index - traceB.index);
78854 });
78855 }
78856
78857 // now link traces to each other
78858 var prevtraces = {};
78859 for(i = 0; i < cdscatterSorted.length; i++) {
78860 trace = cdscatterSorted[i][0].trace;
78861 group = trace.stackgroup || '';
78862
78863 // Note: The check which ensures all cdscatter here are for the same axis and
78864 // are either cartesian or scatterternary has been removed. This code assumes
78865 // the passed scattertraces have been filtered to the proper plot types and
78866 // the proper subplots.
78867 if(trace.visible === true) {
78868 trace._nexttrace = null;
78869
78870 if(trace.fill in LINKEDFILLS) {
78871 prevtrace = prevtraces[group];
78872 trace._prevtrace = prevtrace || null;
78873
78874 if(prevtrace) {
78875 prevtrace._nexttrace = trace;
78876 }
78877 }
78878
78879 trace._ownfill = (trace.fill && (
78880 trace.fill.substr(0, 6) === 'tozero' ||
78881 trace.fill === 'toself' ||
78882 (trace.fill.substr(0, 2) === 'to' && !trace._prevtrace)
78883 ));
78884
78885 prevtraces[group] = trace;
78886 } else {
78887 trace._prevtrace = trace._nexttrace = trace._ownfill = null;
78888 }
78889 }
78890
78891 return cdscatterSorted;
78892};
78893
78894},{}],311:[function(_dereq_,module,exports){
78895/**
78896* Copyright 2012-2020, Plotly, Inc.
78897* All rights reserved.
78898*
78899* This source code is licensed under the MIT license found in the
78900* LICENSE file in the root directory of this source tree.
78901*/
78902
78903
78904'use strict';
78905
78906var isNumeric = _dereq_('fast-isnumeric');
78907
78908
78909// used in the drawing step for 'scatter' and 'scattegeo' and
78910// in the convert step for 'scatter3d'
78911module.exports = function makeBubbleSizeFn(trace) {
78912 var marker = trace.marker;
78913 var sizeRef = marker.sizeref || 1;
78914 var sizeMin = marker.sizemin || 0;
78915
78916 // for bubble charts, allow scaling the provided value linearly
78917 // and by area or diameter.
78918 // Note this only applies to the array-value sizes
78919
78920 var baseFn = (marker.sizemode === 'area') ?
78921 function(v) { return Math.sqrt(v / sizeRef); } :
78922 function(v) { return v / sizeRef; };
78923
78924 // TODO add support for position/negative bubbles?
78925 // TODO add 'sizeoffset' attribute?
78926 return function(v) {
78927 var baseSize = baseFn(v / 2);
78928
78929 // don't show non-numeric and negative sizes
78930 return (isNumeric(baseSize) && (baseSize > 0)) ?
78931 Math.max(baseSize, sizeMin) :
78932 0;
78933 };
78934};
78935
78936},{"fast-isnumeric":15}],312:[function(_dereq_,module,exports){
78937/**
78938* Copyright 2012-2020, Plotly, Inc.
78939* All rights reserved.
78940*
78941* This source code is licensed under the MIT license found in the
78942* LICENSE file in the root directory of this source tree.
78943*/
78944
78945
78946'use strict';
78947
78948module.exports = {
78949 container: 'marker',
78950 min: 'cmin',
78951 max: 'cmax'
78952};
78953
78954},{}],313:[function(_dereq_,module,exports){
78955/**
78956* Copyright 2012-2020, Plotly, Inc.
78957* All rights reserved.
78958*
78959* This source code is licensed under the MIT license found in the
78960* LICENSE file in the root directory of this source tree.
78961*/
78962
78963'use strict';
78964
78965var Color = _dereq_('../../components/color');
78966var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
78967var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
78968
78969var subTypes = _dereq_('./subtypes');
78970
78971/*
78972 * opts: object of flags to control features not all marker users support
78973 * noLine: caller does not support marker lines
78974 * gradient: caller supports gradients
78975 * noSelect: caller does not support selected/unselected attribute containers
78976 */
78977module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
78978 var isBubble = subTypes.isBubble(traceIn);
78979 var lineColor = (traceIn.line || {}).color;
78980 var defaultMLC;
78981
78982 opts = opts || {};
78983
78984 // marker.color inherit from line.color (even if line.color is an array)
78985 if(lineColor) defaultColor = lineColor;
78986
78987 coerce('marker.symbol');
78988 coerce('marker.opacity', isBubble ? 0.7 : 1);
78989 coerce('marker.size');
78990
78991 coerce('marker.color', defaultColor);
78992 if(hasColorscale(traceIn, 'marker')) {
78993 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
78994 }
78995
78996 if(!opts.noSelect) {
78997 coerce('selected.marker.color');
78998 coerce('unselected.marker.color');
78999 coerce('selected.marker.size');
79000 coerce('unselected.marker.size');
79001 }
79002
79003 if(!opts.noLine) {
79004 // if there's a line with a different color than the marker, use
79005 // that line color as the default marker line color
79006 // (except when it's an array)
79007 // mostly this is for transparent markers to behave nicely
79008 if(lineColor && !Array.isArray(lineColor) && (traceOut.marker.color !== lineColor)) {
79009 defaultMLC = lineColor;
79010 } else if(isBubble) defaultMLC = Color.background;
79011 else defaultMLC = Color.defaultLine;
79012
79013 coerce('marker.line.color', defaultMLC);
79014 if(hasColorscale(traceIn, 'marker.line')) {
79015 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'});
79016 }
79017
79018 coerce('marker.line.width', isBubble ? 1 : 0);
79019 }
79020
79021 if(isBubble) {
79022 coerce('marker.sizeref');
79023 coerce('marker.sizemin');
79024 coerce('marker.sizemode');
79025 }
79026
79027 if(opts.gradient) {
79028 var gradientType = coerce('marker.gradient.type');
79029 if(gradientType !== 'none') {
79030 coerce('marker.gradient.color');
79031 }
79032 }
79033};
79034
79035},{"../../components/color":50,"../../components/colorscale/defaults":60,"../../components/colorscale/helpers":61,"./subtypes":318}],314:[function(_dereq_,module,exports){
79036/**
79037* Copyright 2012-2020, Plotly, Inc.
79038* All rights reserved.
79039*
79040* This source code is licensed under the MIT license found in the
79041* LICENSE file in the root directory of this source tree.
79042*/
79043
79044
79045'use strict';
79046
79047var d3 = _dereq_('d3');
79048
79049var Registry = _dereq_('../../registry');
79050var Lib = _dereq_('../../lib');
79051var ensureSingle = Lib.ensureSingle;
79052var identity = Lib.identity;
79053var Drawing = _dereq_('../../components/drawing');
79054
79055var subTypes = _dereq_('./subtypes');
79056var linePoints = _dereq_('./line_points');
79057var linkTraces = _dereq_('./link_traces');
79058var polygonTester = _dereq_('../../lib/polygon').tester;
79059
79060module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transitionOpts, makeOnCompleteCallback) {
79061 var join, onComplete;
79062
79063 // If transition config is provided, then it is only a partial replot and traces not
79064 // updated are removed.
79065 var isFullReplot = !transitionOpts;
79066 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
79067
79068 // Link traces so the z-order of fill layers is correct
79069 var cdscatterSorted = linkTraces(gd, plotinfo, cdscatter);
79070
79071 join = scatterLayer.selectAll('g.trace')
79072 .data(cdscatterSorted, function(d) { return d[0].trace.uid; });
79073
79074 // Append new traces:
79075 join.enter().append('g')
79076 .attr('class', function(d) {
79077 return 'trace scatter trace' + d[0].trace.uid;
79078 })
79079 .style('stroke-miterlimit', 2);
79080 join.order();
79081
79082 createFills(gd, join, plotinfo);
79083
79084 if(hasTransition) {
79085 if(makeOnCompleteCallback) {
79086 // If it was passed a callback to register completion, make a callback. If
79087 // this is created, then it must be executed on completion, otherwise the
79088 // pos-transition redraw will not execute:
79089 onComplete = makeOnCompleteCallback();
79090 }
79091
79092 var transition = d3.transition()
79093 .duration(transitionOpts.duration)
79094 .ease(transitionOpts.easing)
79095 .each('end', function() {
79096 onComplete && onComplete();
79097 })
79098 .each('interrupt', function() {
79099 onComplete && onComplete();
79100 });
79101
79102 transition.each(function() {
79103 // Must run the selection again since otherwise enters/updates get grouped together
79104 // and these get executed out of order. Except we need them in order!
79105 scatterLayer.selectAll('g.trace').each(function(d, i) {
79106 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
79107 });
79108 });
79109 } else {
79110 join.each(function(d, i) {
79111 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
79112 });
79113 }
79114
79115 if(isFullReplot) {
79116 join.exit().remove();
79117 }
79118
79119 // remove paths that didn't get used
79120 scatterLayer.selectAll('path:not([d])').remove();
79121};
79122
79123function createFills(gd, traceJoin, plotinfo) {
79124 traceJoin.each(function(d) {
79125 var fills = ensureSingle(d3.select(this), 'g', 'fills');
79126 Drawing.setClipUrl(fills, plotinfo.layerClipId, gd);
79127
79128 var trace = d[0].trace;
79129
79130 var fillData = [];
79131 if(trace._ownfill) fillData.push('_ownFill');
79132 if(trace._nexttrace) fillData.push('_nextFill');
79133
79134 var fillJoin = fills.selectAll('g').data(fillData, identity);
79135
79136 fillJoin.enter().append('g');
79137
79138 fillJoin.exit()
79139 .each(function(d) { trace[d] = null; })
79140 .remove();
79141
79142 fillJoin.order().each(function(d) {
79143 // make a path element inside the fill group, just so
79144 // we can give it its own data later on and the group can
79145 // keep its simple '_*Fill' data
79146 trace[d] = ensureSingle(d3.select(this), 'path', 'js-fill');
79147 });
79148 });
79149}
79150
79151function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transitionOpts) {
79152 var i;
79153
79154 // Since this has been reorganized and we're executing this on individual traces,
79155 // we need to pass it the full list of cdscatter as well as this trace's index (idx)
79156 // since it does an internal n^2 loop over comparisons with other traces:
79157 selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll);
79158
79159 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
79160
79161 function transition(selection) {
79162 return hasTransition ? selection.transition() : selection;
79163 }
79164
79165 var xa = plotinfo.xaxis;
79166 var ya = plotinfo.yaxis;
79167
79168 var trace = cdscatter[0].trace;
79169 var line = trace.line;
79170 var tr = d3.select(element);
79171
79172 var errorBarGroup = ensureSingle(tr, 'g', 'errorbars');
79173 var lines = ensureSingle(tr, 'g', 'lines');
79174 var points = ensureSingle(tr, 'g', 'points');
79175 var text = ensureSingle(tr, 'g', 'text');
79176
79177 // error bars are at the bottom
79178 Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts);
79179
79180 if(trace.visible !== true) return;
79181
79182 transition(tr).style('opacity', trace.opacity);
79183
79184 // BUILD LINES AND FILLS
79185 var ownFillEl3, tonext;
79186 var ownFillDir = trace.fill.charAt(trace.fill.length - 1);
79187 if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = '';
79188
79189 // store node for tweaking by selectPoints
79190 cdscatter[0][plotinfo.isRangePlot ? 'nodeRangePlot3' : 'node3'] = tr;
79191
79192 var prevRevpath = '';
79193 var prevPolygons = [];
79194 var prevtrace = trace._prevtrace;
79195
79196 if(prevtrace) {
79197 prevRevpath = prevtrace._prevRevpath || '';
79198 tonext = prevtrace._nextFill;
79199 prevPolygons = prevtrace._polygons;
79200 }
79201
79202 var thispath;
79203 var thisrevpath;
79204 // fullpath is all paths for this curve, joined together straight
79205 // across gaps, for filling
79206 var fullpath = '';
79207 // revpath is fullpath reversed, for fill-to-next
79208 var revpath = '';
79209 // functions for converting a point array to a path
79210 var pathfn, revpathbase, revpathfn;
79211 // variables used before and after the data join
79212 var pt0, lastSegment, pt1, thisPolygons;
79213
79214 // initialize line join data / method
79215 var segments = [];
79216 var makeUpdate = Lib.noop;
79217
79218 ownFillEl3 = trace._ownFill;
79219
79220 if(subTypes.hasLines(trace) || trace.fill !== 'none') {
79221 if(tonext) {
79222 // This tells .style which trace to use for fill information:
79223 tonext.datum(cdscatter);
79224 }
79225
79226 if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) {
79227 pathfn = Drawing.steps(line.shape);
79228 revpathbase = Drawing.steps(
79229 line.shape.split('').reverse().join('')
79230 );
79231 } else if(line.shape === 'spline') {
79232 pathfn = revpathbase = function(pts) {
79233 var pLast = pts[pts.length - 1];
79234 if(pts.length > 1 && pts[0][0] === pLast[0] && pts[0][1] === pLast[1]) {
79235 // identical start and end points: treat it as a
79236 // closed curve so we don't get a kink
79237 return Drawing.smoothclosed(pts.slice(1), line.smoothing);
79238 } else {
79239 return Drawing.smoothopen(pts, line.smoothing);
79240 }
79241 };
79242 } else {
79243 pathfn = revpathbase = function(pts) {
79244 return 'M' + pts.join('L');
79245 };
79246 }
79247
79248 revpathfn = function(pts) {
79249 // note: this is destructive (reverses pts in place) so can't use pts after this
79250 return revpathbase(pts.reverse());
79251 };
79252
79253 segments = linePoints(cdscatter, {
79254 xaxis: xa,
79255 yaxis: ya,
79256 connectGaps: trace.connectgaps,
79257 baseTolerance: Math.max(line.width || 1, 3) / 4,
79258 shape: line.shape,
79259 simplify: line.simplify,
79260 fill: trace.fill
79261 });
79262
79263 // since we already have the pixel segments here, use them to make
79264 // polygons for hover on fill
79265 // TODO: can we skip this if hoveron!=fills? That would mean we
79266 // need to redraw when you change hoveron...
79267 thisPolygons = trace._polygons = new Array(segments.length);
79268 for(i = 0; i < segments.length; i++) {
79269 trace._polygons[i] = polygonTester(segments[i]);
79270 }
79271
79272 if(segments.length) {
79273 pt0 = segments[0][0];
79274 lastSegment = segments[segments.length - 1];
79275 pt1 = lastSegment[lastSegment.length - 1];
79276 }
79277
79278 makeUpdate = function(isEnter) {
79279 return function(pts) {
79280 thispath = pathfn(pts);
79281 thisrevpath = revpathfn(pts);
79282 if(!fullpath) {
79283 fullpath = thispath;
79284 revpath = thisrevpath;
79285 } else if(ownFillDir) {
79286 fullpath += 'L' + thispath.substr(1);
79287 revpath = thisrevpath + ('L' + revpath.substr(1));
79288 } else {
79289 fullpath += 'Z' + thispath;
79290 revpath = thisrevpath + 'Z' + revpath;
79291 }
79292
79293 if(subTypes.hasLines(trace) && pts.length > 1) {
79294 var el = d3.select(this);
79295
79296 // This makes the coloring work correctly:
79297 el.datum(cdscatter);
79298
79299 if(isEnter) {
79300 transition(el.style('opacity', 0)
79301 .attr('d', thispath)
79302 .call(Drawing.lineGroupStyle))
79303 .style('opacity', 1);
79304 } else {
79305 var sel = transition(el);
79306 sel.attr('d', thispath);
79307 Drawing.singleLineStyle(cdscatter, sel);
79308 }
79309 }
79310 };
79311 };
79312 }
79313
79314 var lineJoin = lines.selectAll('.js-line').data(segments);
79315
79316 transition(lineJoin.exit())
79317 .style('opacity', 0)
79318 .remove();
79319
79320 lineJoin.each(makeUpdate(false));
79321
79322 lineJoin.enter().append('path')
79323 .classed('js-line', true)
79324 .style('vector-effect', 'non-scaling-stroke')
79325 .call(Drawing.lineGroupStyle)
79326 .each(makeUpdate(true));
79327
79328 Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd);
79329
79330 function clearFill(selection) {
79331 transition(selection).attr('d', 'M0,0Z');
79332 }
79333
79334 if(segments.length) {
79335 if(ownFillEl3) {
79336 ownFillEl3.datum(cdscatter);
79337 if(pt0 && pt1) {
79338 if(ownFillDir) {
79339 if(ownFillDir === 'y') {
79340 pt0[1] = pt1[1] = ya.c2p(0, true);
79341 } else if(ownFillDir === 'x') {
79342 pt0[0] = pt1[0] = xa.c2p(0, true);
79343 }
79344
79345 // fill to zero: full trace path, plus extension of
79346 // the endpoints to the appropriate axis
79347 // For the sake of animations, wrap the points around so that
79348 // the points on the axes are the first two points. Otherwise
79349 // animations get a little crazy if the number of points changes.
79350 transition(ownFillEl3).attr('d', 'M' + pt1 + 'L' + pt0 + 'L' + fullpath.substr(1))
79351 .call(Drawing.singleFillStyle);
79352 } else {
79353 // fill to self: just join the path to itself
79354 transition(ownFillEl3).attr('d', fullpath + 'Z')
79355 .call(Drawing.singleFillStyle);
79356 }
79357 }
79358 } else if(tonext) {
79359 if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
79360 // fill to next: full trace path, plus the previous path reversed
79361 if(trace.fill === 'tonext') {
79362 // tonext: for use by concentric shapes, like manually constructed
79363 // contours, we just add the two paths closed on themselves.
79364 // This makes strange results if one path is *not* entirely
79365 // inside the other, but then that is a strange usage.
79366 transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
79367 .call(Drawing.singleFillStyle);
79368 } else {
79369 // tonextx/y: for now just connect endpoints with lines. This is
79370 // the correct behavior if the endpoints are at the same value of
79371 // y/x, but if they *aren't*, we should ideally do more complicated
79372 // things depending on whether the new endpoint projects onto the
79373 // existing curve or off the end of it
79374 transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
79375 .call(Drawing.singleFillStyle);
79376 }
79377 trace._polygons = trace._polygons.concat(prevPolygons);
79378 } else {
79379 clearFill(tonext);
79380 trace._polygons = null;
79381 }
79382 }
79383 trace._prevRevpath = revpath;
79384 trace._prevPolygons = thisPolygons;
79385 } else {
79386 if(ownFillEl3) clearFill(ownFillEl3);
79387 else if(tonext) clearFill(tonext);
79388 trace._polygons = trace._prevRevpath = trace._prevPolygons = null;
79389 }
79390
79391
79392 function visFilter(d) {
79393 return d.filter(function(v) { return !v.gap && v.vis; });
79394 }
79395
79396 function visFilterWithGaps(d) {
79397 return d.filter(function(v) { return v.vis; });
79398 }
79399
79400 function gapFilter(d) {
79401 return d.filter(function(v) { return !v.gap; });
79402 }
79403
79404 function keyFunc(d) {
79405 return d.id;
79406 }
79407
79408 // Returns a function if the trace is keyed, otherwise returns undefined
79409 function getKeyFunc(trace) {
79410 if(trace.ids) {
79411 return keyFunc;
79412 }
79413 }
79414
79415 function hideFilter() {
79416 return false;
79417 }
79418
79419 function makePoints(points, text, cdscatter) {
79420 var join, selection, hasNode;
79421
79422 var trace = cdscatter[0].trace;
79423 var showMarkers = subTypes.hasMarkers(trace);
79424 var showText = subTypes.hasText(trace);
79425
79426 var keyFunc = getKeyFunc(trace);
79427 var markerFilter = hideFilter;
79428 var textFilter = hideFilter;
79429
79430 if(showMarkers || showText) {
79431 var showFilter = identity;
79432 // if we're stacking, "infer zero" gap mode gets markers in the
79433 // gap points - because we've inferred a zero there - but other
79434 // modes (currently "interpolate", later "interrupt" hopefully)
79435 // we don't draw generated markers
79436 var stackGroup = trace.stackgroup;
79437 var isInferZero = stackGroup && (
79438 gd._fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup].stackgaps === 'infer zero');
79439 if(trace.marker.maxdisplayed || trace._needsCull) {
79440 showFilter = isInferZero ? visFilterWithGaps : visFilter;
79441 } else if(stackGroup && !isInferZero) {
79442 showFilter = gapFilter;
79443 }
79444
79445 if(showMarkers) markerFilter = showFilter;
79446 if(showText) textFilter = showFilter;
79447 }
79448
79449 // marker points
79450
79451 selection = points.selectAll('path.point');
79452
79453 join = selection.data(markerFilter, keyFunc);
79454
79455 var enter = join.enter().append('path')
79456 .classed('point', true);
79457
79458 if(hasTransition) {
79459 enter
79460 .call(Drawing.pointStyle, trace, gd)
79461 .call(Drawing.translatePoints, xa, ya)
79462 .style('opacity', 0)
79463 .transition()
79464 .style('opacity', 1);
79465 }
79466
79467 join.order();
79468
79469 var styleFns;
79470 if(showMarkers) {
79471 styleFns = Drawing.makePointStyleFns(trace);
79472 }
79473
79474 join.each(function(d) {
79475 var el = d3.select(this);
79476 var sel = transition(el);
79477 hasNode = Drawing.translatePoint(d, sel, xa, ya);
79478
79479 if(hasNode) {
79480 Drawing.singlePointStyle(d, sel, trace, styleFns, gd);
79481
79482 if(plotinfo.layerClipId) {
79483 Drawing.hideOutsideRangePoint(d, sel, xa, ya, trace.xcalendar, trace.ycalendar);
79484 }
79485
79486 if(trace.customdata) {
79487 el.classed('plotly-customdata', d.data !== null && d.data !== undefined);
79488 }
79489 } else {
79490 sel.remove();
79491 }
79492 });
79493
79494 if(hasTransition) {
79495 join.exit().transition()
79496 .style('opacity', 0)
79497 .remove();
79498 } else {
79499 join.exit().remove();
79500 }
79501
79502 // text points
79503 selection = text.selectAll('g');
79504 join = selection.data(textFilter, keyFunc);
79505
79506 // each text needs to go in its own 'g' in case
79507 // it gets converted to mathjax
79508 join.enter().append('g').classed('textpoint', true).append('text');
79509
79510 join.order();
79511
79512 join.each(function(d) {
79513 var g = d3.select(this);
79514 var sel = transition(g.select('text'));
79515 hasNode = Drawing.translatePoint(d, sel, xa, ya);
79516
79517 if(hasNode) {
79518 if(plotinfo.layerClipId) {
79519 Drawing.hideOutsideRangePoint(d, g, xa, ya, trace.xcalendar, trace.ycalendar);
79520 }
79521 } else {
79522 g.remove();
79523 }
79524 });
79525
79526 join.selectAll('text')
79527 .call(Drawing.textPointStyle, trace, gd)
79528 .each(function(d) {
79529 // This just *has* to be totally custom becuase of SVG text positioning :(
79530 // It's obviously copied from translatePoint; we just can't use that
79531 var x = xa.c2p(d.x);
79532 var y = ya.c2p(d.y);
79533
79534 d3.select(this).selectAll('tspan.line').each(function() {
79535 transition(d3.select(this)).attr({x: x, y: y});
79536 });
79537 });
79538
79539 join.exit().remove();
79540 }
79541
79542 points.datum(cdscatter);
79543 text.datum(cdscatter);
79544 makePoints(points, text, cdscatter);
79545
79546 // lastly, clip points groups of `cliponaxis !== false` traces
79547 // on `plotinfo._hasClipOnAxisFalse === true` subplots
79548 var hasClipOnAxisFalse = trace.cliponaxis === false;
79549 var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId;
79550 Drawing.setClipUrl(points, clipUrl, gd);
79551 Drawing.setClipUrl(text, clipUrl, gd);
79552}
79553
79554function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) {
79555 var xa = plotinfo.xaxis;
79556 var ya = plotinfo.yaxis;
79557 var xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c));
79558 var yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c));
79559
79560 var trace = cdscatter[0].trace;
79561 if(!subTypes.hasMarkers(trace)) return;
79562 // if marker.maxdisplayed is used, select a maximum of
79563 // mnum markers to show, from the set that are in the viewport
79564 var mnum = trace.marker.maxdisplayed;
79565
79566 // TODO: remove some as we get away from the viewport?
79567 if(mnum === 0) return;
79568
79569 var cd = cdscatter.filter(function(v) {
79570 return v.x >= xr[0] && v.x <= xr[1] && v.y >= yr[0] && v.y <= yr[1];
79571 });
79572 var inc = Math.ceil(cd.length / mnum);
79573 var tnum = 0;
79574 cdscatterAll.forEach(function(cdj, j) {
79575 var tracei = cdj[0].trace;
79576 if(subTypes.hasMarkers(tracei) &&
79577 tracei.marker.maxdisplayed > 0 && j < idx) {
79578 tnum++;
79579 }
79580 });
79581
79582 // if multiple traces use maxdisplayed, stagger which markers we
79583 // display this formula offsets successive traces by 1/3 of the
79584 // increment, adding an extra small amount after each triplet so
79585 // it's not quite periodic
79586 var i0 = Math.round(tnum * inc / 3 + Math.floor(tnum / 3) * inc / 7.1);
79587
79588 // for error bars: save in cd which markers to show
79589 // so we don't have to repeat this
79590 cdscatter.forEach(function(v) { delete v.vis; });
79591 cd.forEach(function(v, i) {
79592 if(Math.round((i + i0) % inc) === 0) v.vis = true;
79593 });
79594}
79595
79596},{"../../components/drawing":72,"../../lib":177,"../../lib/polygon":189,"../../registry":272,"./line_points":308,"./link_traces":310,"./subtypes":318,"d3":13}],315:[function(_dereq_,module,exports){
79597/**
79598* Copyright 2012-2020, Plotly, Inc.
79599* All rights reserved.
79600*
79601* This source code is licensed under the MIT license found in the
79602* LICENSE file in the root directory of this source tree.
79603*/
79604
79605
79606'use strict';
79607
79608var subtypes = _dereq_('./subtypes');
79609
79610module.exports = function selectPoints(searchInfo, selectionTester) {
79611 var cd = searchInfo.cd;
79612 var xa = searchInfo.xaxis;
79613 var ya = searchInfo.yaxis;
79614 var selection = [];
79615 var trace = cd[0].trace;
79616 var i;
79617 var di;
79618 var x;
79619 var y;
79620
79621 var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
79622 if(hasOnlyLines) return [];
79623
79624 if(selectionTester === false) { // clear selection
79625 for(i = 0; i < cd.length; i++) {
79626 cd[i].selected = 0;
79627 }
79628 } else {
79629 for(i = 0; i < cd.length; i++) {
79630 di = cd[i];
79631 x = xa.c2p(di.x);
79632 y = ya.c2p(di.y);
79633
79634 if((di.i !== null) && selectionTester.contains([x, y], false, i, searchInfo)) {
79635 selection.push({
79636 pointNumber: di.i,
79637 x: xa.c2d(di.x),
79638 y: ya.c2d(di.y)
79639 });
79640 di.selected = 1;
79641 } else {
79642 di.selected = 0;
79643 }
79644 }
79645 }
79646
79647 return selection;
79648};
79649
79650},{"./subtypes":318}],316:[function(_dereq_,module,exports){
79651/**
79652* Copyright 2012-2020, Plotly, Inc.
79653* All rights reserved.
79654*
79655* This source code is licensed under the MIT license found in the
79656* LICENSE file in the root directory of this source tree.
79657*/
79658
79659'use strict';
79660
79661var perStackAttrs = ['orientation', 'groupnorm', 'stackgaps'];
79662
79663module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) {
79664 var stackOpts = layout._scatterStackOpts;
79665
79666 var stackGroup = coerce('stackgroup');
79667 if(stackGroup) {
79668 // use independent stacking options per subplot
79669 var subplot = traceOut.xaxis + traceOut.yaxis;
79670 var subplotStackOpts = stackOpts[subplot];
79671 if(!subplotStackOpts) subplotStackOpts = stackOpts[subplot] = {};
79672
79673 var groupOpts = subplotStackOpts[stackGroup];
79674 var firstTrace = false;
79675 if(groupOpts) {
79676 groupOpts.traces.push(traceOut);
79677 } else {
79678 groupOpts = subplotStackOpts[stackGroup] = {
79679 // keep track of trace indices for use during stacking calculations
79680 // this will be filled in during `calc` and used during `crossTraceCalc`
79681 // so it's OK if we don't recreate it during a non-calc edit
79682 traceIndices: [],
79683 // Hold on to the whole set of prior traces
79684 // First one is most important, so we can clear defaults
79685 // there if we find explicit values only in later traces.
79686 // We're only going to *use* the values stored in groupOpts,
79687 // but for the editor and validate we want things self-consistent
79688 // The full set of traces is used only to fix `fill` default if
79689 // we find `orientation: 'h'` beyond the first trace
79690 traces: [traceOut]
79691 };
79692 firstTrace = true;
79693 }
79694 // TODO: how is this going to work with groupby transforms?
79695 // in principle it should be OK I guess, as long as explicit group styles
79696 // don't override explicit base-trace styles?
79697
79698 var dflts = {
79699 orientation: (traceOut.x && !traceOut.y) ? 'h' : 'v'
79700 };
79701
79702 for(var i = 0; i < perStackAttrs.length; i++) {
79703 var attr = perStackAttrs[i];
79704 var attrFound = attr + 'Found';
79705 if(!groupOpts[attrFound]) {
79706 var traceHasAttr = traceIn[attr] !== undefined;
79707 var isOrientation = attr === 'orientation';
79708 if(traceHasAttr || firstTrace) {
79709 groupOpts[attr] = coerce(attr, dflts[attr]);
79710
79711 if(isOrientation) {
79712 groupOpts.fillDflt = groupOpts[attr] === 'h' ?
79713 'tonextx' : 'tonexty';
79714 }
79715
79716 if(traceHasAttr) {
79717 // Note: this will show a value here even if it's invalid
79718 // in which case it will revert to default.
79719 groupOpts[attrFound] = true;
79720
79721 // Note: only one trace in the stack will get a _fullData
79722 // entry for a given stack-wide attribute. If no traces
79723 // (or the first trace) specify that attribute, the
79724 // first trace will get it. If the first trace does NOT
79725 // specify it but some later trace does, then it gets
79726 // removed from the first trace and only included in the
79727 // one that specified it. This is mostly important for
79728 // editors (that want to see the full values to know
79729 // what settings are available) and Plotly.react diffing.
79730 // Editors may want to use fullLayout._scatterStackOpts
79731 // directly and make these settings available from all
79732 // traces in the stack... then set the new value into
79733 // the first trace, and clear all later traces.
79734 if(!firstTrace) {
79735 delete groupOpts.traces[0][attr];
79736
79737 // orientation can affect default fill of previous traces
79738 if(isOrientation) {
79739 for(var j = 0; j < groupOpts.traces.length - 1; j++) {
79740 var trace2 = groupOpts.traces[j];
79741 if(trace2._input.fill !== trace2.fill) {
79742 trace2.fill = groupOpts.fillDflt;
79743 }
79744 }
79745 }
79746 }
79747 }
79748 }
79749 }
79750 }
79751 return groupOpts;
79752 }
79753};
79754
79755},{}],317:[function(_dereq_,module,exports){
79756/**
79757* Copyright 2012-2020, Plotly, Inc.
79758* All rights reserved.
79759*
79760* This source code is licensed under the MIT license found in the
79761* LICENSE file in the root directory of this source tree.
79762*/
79763
79764
79765'use strict';
79766
79767var d3 = _dereq_('d3');
79768var Drawing = _dereq_('../../components/drawing');
79769var Registry = _dereq_('../../registry');
79770
79771function style(gd) {
79772 var s = d3.select(gd).selectAll('g.trace.scatter');
79773
79774 s.style('opacity', function(d) {
79775 return d[0].trace.opacity;
79776 });
79777
79778 s.selectAll('g.points').each(function(d) {
79779 var sel = d3.select(this);
79780 var trace = d.trace || d[0].trace;
79781 stylePoints(sel, trace, gd);
79782 });
79783
79784 s.selectAll('g.text').each(function(d) {
79785 var sel = d3.select(this);
79786 var trace = d.trace || d[0].trace;
79787 styleText(sel, trace, gd);
79788 });
79789
79790 s.selectAll('g.trace path.js-line')
79791 .call(Drawing.lineGroupStyle);
79792
79793 s.selectAll('g.trace path.js-fill')
79794 .call(Drawing.fillGroupStyle);
79795
79796 Registry.getComponentMethod('errorbars', 'style')(s);
79797}
79798
79799function stylePoints(sel, trace, gd) {
79800 Drawing.pointStyle(sel.selectAll('path.point'), trace, gd);
79801}
79802
79803function styleText(sel, trace, gd) {
79804 Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
79805}
79806
79807function styleOnSelect(gd, cd, sel) {
79808 var trace = cd[0].trace;
79809
79810 if(trace.selectedpoints) {
79811 Drawing.selectedPointStyle(sel.selectAll('path.point'), trace);
79812 Drawing.selectedTextStyle(sel.selectAll('text'), trace);
79813 } else {
79814 stylePoints(sel, trace, gd);
79815 styleText(sel, trace, gd);
79816 }
79817}
79818
79819module.exports = {
79820 style: style,
79821 stylePoints: stylePoints,
79822 styleText: styleText,
79823 styleOnSelect: styleOnSelect
79824};
79825
79826},{"../../components/drawing":72,"../../registry":272,"d3":13}],318:[function(_dereq_,module,exports){
79827/**
79828* Copyright 2012-2020, Plotly, Inc.
79829* All rights reserved.
79830*
79831* This source code is licensed under the MIT license found in the
79832* LICENSE file in the root directory of this source tree.
79833*/
79834
79835
79836'use strict';
79837
79838var Lib = _dereq_('../../lib');
79839
79840module.exports = {
79841 hasLines: function(trace) {
79842 return trace.visible && trace.mode &&
79843 trace.mode.indexOf('lines') !== -1;
79844 },
79845
79846 hasMarkers: function(trace) {
79847 return trace.visible && (
79848 (trace.mode && trace.mode.indexOf('markers') !== -1) ||
79849 // until splom implements 'mode'
79850 trace.type === 'splom'
79851 );
79852 },
79853
79854 hasText: function(trace) {
79855 return trace.visible && trace.mode &&
79856 trace.mode.indexOf('text') !== -1;
79857 },
79858
79859 isBubble: function(trace) {
79860 return Lib.isPlainObject(trace.marker) &&
79861 Lib.isArrayOrTypedArray(trace.marker.size);
79862 }
79863};
79864
79865},{"../../lib":177}],319:[function(_dereq_,module,exports){
79866/**
79867* Copyright 2012-2020, Plotly, Inc.
79868* All rights reserved.
79869*
79870* This source code is licensed under the MIT license found in the
79871* LICENSE file in the root directory of this source tree.
79872*/
79873
79874
79875'use strict';
79876
79877var Lib = _dereq_('../../lib');
79878
79879/*
79880 * opts: object of flags to control features not all text users support
79881 * noSelect: caller does not support selected/unselected attribute containers
79882 */
79883module.exports = function(traceIn, traceOut, layout, coerce, opts) {
79884 opts = opts || {};
79885
79886 coerce('textposition');
79887 Lib.coerceFont(coerce, 'textfont', layout.font);
79888
79889 if(!opts.noSelect) {
79890 coerce('selected.textfont.color');
79891 coerce('unselected.textfont.color');
79892 }
79893};
79894
79895},{"../../lib":177}],320:[function(_dereq_,module,exports){
79896/**
79897* Copyright 2012-2020, Plotly, Inc.
79898* All rights reserved.
79899*
79900* This source code is licensed under the MIT license found in the
79901* LICENSE file in the root directory of this source tree.
79902*/
79903
79904'use strict';
79905
79906var Lib = _dereq_('../../lib');
79907var Registry = _dereq_('../../registry');
79908
79909module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) {
79910 var x = coerce('x');
79911 var y = coerce('y');
79912 var len;
79913
79914 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
79915 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
79916
79917 if(x) {
79918 var xlen = Lib.minRowLength(x);
79919 if(y) {
79920 len = Math.min(xlen, Lib.minRowLength(y));
79921 } else {
79922 len = xlen;
79923 coerce('y0');
79924 coerce('dy');
79925 }
79926 } else {
79927 if(!y) return 0;
79928
79929 len = Lib.minRowLength(y);
79930 coerce('x0');
79931 coerce('dx');
79932 }
79933
79934 traceOut._length = len;
79935
79936 return len;
79937};
79938
79939},{"../../lib":177,"../../registry":272}],321:[function(_dereq_,module,exports){
79940/**
79941* Copyright 2012-2020, Plotly, Inc.
79942* All rights reserved.
79943*
79944* This source code is licensed under the MIT license found in the
79945* LICENSE file in the root directory of this source tree.
79946*/
79947
79948'use strict';
79949
79950var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
79951var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
79952var scatterAttrs = _dereq_('../scatter/attributes');
79953var baseAttrs = _dereq_('../../plots/attributes');
79954var colorAttributes = _dereq_('../../components/colorscale/attributes');
79955var dash = _dereq_('../../components/drawing/attributes').dash;
79956
79957var extendFlat = _dereq_('../../lib/extend').extendFlat;
79958var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
79959
79960var scatterMarkerAttrs = scatterAttrs.marker;
79961var scatterLineAttrs = scatterAttrs.line;
79962var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
79963
79964module.exports = overrideAll({
79965 lon: {
79966 valType: 'data_array',
79967
79968 },
79969 lat: {
79970 valType: 'data_array',
79971
79972 },
79973
79974 locations: {
79975 valType: 'data_array',
79976
79977 },
79978 locationmode: {
79979 valType: 'enumerated',
79980 values: ['ISO-3', 'USA-states', 'country names', 'geojson-id'],
79981
79982 dflt: 'ISO-3',
79983
79984 },
79985
79986 geojson: {
79987 valType: 'any',
79988
79989 editType: 'calc',
79990
79991 },
79992 featureidkey: {
79993 valType: 'string',
79994
79995 editType: 'calc',
79996 dflt: 'id',
79997
79998 },
79999
80000 mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}),
80001
80002 text: extendFlat({}, scatterAttrs.text, {
80003
80004 }),
80005 texttemplate: texttemplateAttrs({editType: 'plot'}, {
80006 keys: ['lat', 'lon', 'location', 'text']
80007 }),
80008 hovertext: extendFlat({}, scatterAttrs.hovertext, {
80009
80010 }),
80011
80012 textfont: scatterAttrs.textfont,
80013 textposition: scatterAttrs.textposition,
80014
80015 line: {
80016 color: scatterLineAttrs.color,
80017 width: scatterLineAttrs.width,
80018 dash: dash
80019 },
80020 connectgaps: scatterAttrs.connectgaps,
80021
80022 marker: extendFlat({
80023 symbol: scatterMarkerAttrs.symbol,
80024 opacity: scatterMarkerAttrs.opacity,
80025 size: scatterMarkerAttrs.size,
80026 sizeref: scatterMarkerAttrs.sizeref,
80027 sizemin: scatterMarkerAttrs.sizemin,
80028 sizemode: scatterMarkerAttrs.sizemode,
80029 colorbar: scatterMarkerAttrs.colorbar,
80030 line: extendFlat({
80031 width: scatterMarkerLineAttrs.width
80032 },
80033 colorAttributes('marker.line')
80034 ),
80035 gradient: scatterMarkerAttrs.gradient
80036 },
80037 colorAttributes('marker')
80038 ),
80039
80040 fill: {
80041 valType: 'enumerated',
80042 values: ['none', 'toself'],
80043 dflt: 'none',
80044
80045
80046 },
80047 fillcolor: scatterAttrs.fillcolor,
80048
80049 selected: scatterAttrs.selected,
80050 unselected: scatterAttrs.unselected,
80051
80052 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
80053 flags: ['lon', 'lat', 'location', 'text', 'name']
80054 }),
80055 hovertemplate: hovertemplateAttrs(),
80056}, 'calc', 'nested');
80057
80058},{"../../components/colorscale/attributes":57,"../../components/drawing/attributes":71,"../../lib/extend":170,"../../plot_api/edit_types":205,"../../plots/attributes":219,"../../plots/template_attributes":271,"../scatter/attributes":294}],322:[function(_dereq_,module,exports){
80059/**
80060* Copyright 2012-2020, Plotly, Inc.
80061* All rights reserved.
80062*
80063* This source code is licensed under the MIT license found in the
80064* LICENSE file in the root directory of this source tree.
80065*/
80066
80067
80068'use strict';
80069
80070var isNumeric = _dereq_('fast-isnumeric');
80071var BADNUM = _dereq_('../../constants/numerical').BADNUM;
80072
80073var calcMarkerColorscale = _dereq_('../scatter/colorscale_calc');
80074var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata');
80075var calcSelection = _dereq_('../scatter/calc_selection');
80076
80077var _ = _dereq_('../../lib')._;
80078
80079function isNonBlankString(v) {
80080 return v && typeof v === 'string';
80081}
80082
80083module.exports = function calc(gd, trace) {
80084 var hasLocationData = Array.isArray(trace.locations);
80085 var len = hasLocationData ? trace.locations.length : trace._length;
80086 var calcTrace = new Array(len);
80087
80088 var isValidLoc;
80089 if(trace.geojson) {
80090 isValidLoc = function(v) { return isNonBlankString(v) || isNumeric(v); };
80091 } else {
80092 isValidLoc = isNonBlankString;
80093 }
80094
80095 for(var i = 0; i < len; i++) {
80096 var calcPt = calcTrace[i] = {};
80097
80098 if(hasLocationData) {
80099 var loc = trace.locations[i];
80100 calcPt.loc = isValidLoc(loc) ? loc : null;
80101 } else {
80102 var lon = trace.lon[i];
80103 var lat = trace.lat[i];
80104
80105 if(isNumeric(lon) && isNumeric(lat)) calcPt.lonlat = [+lon, +lat];
80106 else calcPt.lonlat = [BADNUM, BADNUM];
80107 }
80108 }
80109
80110 arraysToCalcdata(calcTrace, trace);
80111 calcMarkerColorscale(gd, trace);
80112 calcSelection(calcTrace, trace);
80113
80114 if(len) {
80115 calcTrace[0].t = {
80116 labels: {
80117 lat: _(gd, 'lat:') + ' ',
80118 lon: _(gd, 'lon:') + ' '
80119 }
80120 };
80121 }
80122
80123 return calcTrace;
80124};
80125
80126},{"../../constants/numerical":155,"../../lib":177,"../scatter/arrays_to_calcdata":293,"../scatter/calc_selection":296,"../scatter/colorscale_calc":297,"fast-isnumeric":15}],323:[function(_dereq_,module,exports){
80127/**
80128* Copyright 2012-2020, Plotly, Inc.
80129* All rights reserved.
80130*
80131* This source code is licensed under the MIT license found in the
80132* LICENSE file in the root directory of this source tree.
80133*/
80134
80135'use strict';
80136
80137var Lib = _dereq_('../../lib');
80138
80139var subTypes = _dereq_('../scatter/subtypes');
80140var handleMarkerDefaults = _dereq_('../scatter/marker_defaults');
80141var handleLineDefaults = _dereq_('../scatter/line_defaults');
80142var handleTextDefaults = _dereq_('../scatter/text_defaults');
80143var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults');
80144
80145var attributes = _dereq_('./attributes');
80146
80147module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
80148 function coerce(attr, dflt) {
80149 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
80150 }
80151
80152 var locations = coerce('locations');
80153 var len;
80154
80155 if(locations && locations.length) {
80156 var geojson = coerce('geojson');
80157 var locationmodeDflt;
80158 if((typeof geojson === 'string' && geojson !== '') || Lib.isPlainObject(geojson)) {
80159 locationmodeDflt = 'geojson-id';
80160 }
80161
80162 var locationMode = coerce('locationmode', locationmodeDflt);
80163
80164 if(locationMode === 'geojson-id') {
80165 coerce('featureidkey');
80166 }
80167
80168 len = locations.length;
80169 } else {
80170 var lon = coerce('lon') || [];
80171 var lat = coerce('lat') || [];
80172 len = Math.min(lon.length, lat.length);
80173 }
80174
80175 if(!len) {
80176 traceOut.visible = false;
80177 return;
80178 }
80179
80180 traceOut._length = len;
80181
80182 coerce('text');
80183 coerce('hovertext');
80184 coerce('hovertemplate');
80185 coerce('mode');
80186
80187 if(subTypes.hasLines(traceOut)) {
80188 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
80189 coerce('connectgaps');
80190 }
80191
80192 if(subTypes.hasMarkers(traceOut)) {
80193 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
80194 }
80195
80196 if(subTypes.hasText(traceOut)) {
80197 coerce('texttemplate');
80198 handleTextDefaults(traceIn, traceOut, layout, coerce);
80199 }
80200
80201 coerce('fill');
80202 if(traceOut.fill !== 'none') {
80203 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
80204 }
80205
80206 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
80207};
80208
80209},{"../../lib":177,"../scatter/fillcolor_defaults":302,"../scatter/line_defaults":307,"../scatter/marker_defaults":313,"../scatter/subtypes":318,"../scatter/text_defaults":319,"./attributes":321}],324:[function(_dereq_,module,exports){
80210/**
80211* Copyright 2012-2020, Plotly, Inc.
80212* All rights reserved.
80213*
80214* This source code is licensed under the MIT license found in the
80215* LICENSE file in the root directory of this source tree.
80216*/
80217
80218
80219'use strict';
80220
80221
80222module.exports = function eventData(out, pt, trace, cd, pointNumber) {
80223 out.lon = pt.lon;
80224 out.lat = pt.lat;
80225 out.location = pt.loc ? pt.loc : null;
80226
80227 // include feature properties from input geojson
80228 var cdi = cd[pointNumber];
80229 if(cdi.fIn && cdi.fIn.properties) {
80230 out.properties = cdi.fIn.properties;
80231 }
80232
80233 return out;
80234};
80235
80236},{}],325:[function(_dereq_,module,exports){
80237/**
80238* Copyright 2012-2020, Plotly, Inc.
80239* All rights reserved.
80240*
80241* This source code is licensed under the MIT license found in the
80242* LICENSE file in the root directory of this source tree.
80243*/
80244
80245'use strict';
80246
80247var Axes = _dereq_('../../plots/cartesian/axes');
80248
80249module.exports = function formatLabels(cdi, trace, fullLayout) {
80250 var labels = {};
80251
80252 var geo = fullLayout[trace.geo]._subplot;
80253 var ax = geo.mockAxis;
80254 var lonlat = cdi.lonlat;
80255 labels.lonLabel = Axes.tickText(ax, ax.c2l(lonlat[0]), true).text;
80256 labels.latLabel = Axes.tickText(ax, ax.c2l(lonlat[1]), true).text;
80257
80258 return labels;
80259};
80260
80261},{"../../plots/cartesian/axes":222}],326:[function(_dereq_,module,exports){
80262/**
80263* Copyright 2012-2020, Plotly, Inc.
80264* All rights reserved.
80265*
80266* This source code is licensed under the MIT license found in the
80267* LICENSE file in the root directory of this source tree.
80268*/
80269
80270'use strict';
80271
80272var Fx = _dereq_('../../components/fx');
80273var BADNUM = _dereq_('../../constants/numerical').BADNUM;
80274
80275var getTraceColor = _dereq_('../scatter/get_trace_color');
80276var fillText = _dereq_('../../lib').fillText;
80277var attributes = _dereq_('./attributes');
80278
80279module.exports = function hoverPoints(pointData, xval, yval) {
80280 var cd = pointData.cd;
80281 var trace = cd[0].trace;
80282 var xa = pointData.xa;
80283 var ya = pointData.ya;
80284 var geo = pointData.subplot;
80285
80286 var isLonLatOverEdges = geo.projection.isLonLatOverEdges;
80287 var project = geo.project;
80288
80289 function distFn(d) {
80290 var lonlat = d.lonlat;
80291
80292 if(lonlat[0] === BADNUM) return Infinity;
80293 if(isLonLatOverEdges(lonlat)) return Infinity;
80294
80295 var pt = project(lonlat);
80296 var px = project([xval, yval]);
80297 var dx = Math.abs(pt[0] - px[0]);
80298 var dy = Math.abs(pt[1] - px[1]);
80299 var rad = Math.max(3, d.mrc || 0);
80300
80301 // N.B. d.mrc is the calculated marker radius
80302 // which is only set for trace with 'markers' mode.
80303
80304 return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - 3 / rad);
80305 }
80306
80307 Fx.getClosest(cd, distFn, pointData);
80308
80309 // skip the rest (for this trace) if we didn't find a close point
80310 if(pointData.index === false) return;
80311
80312 var di = cd[pointData.index];
80313 var lonlat = di.lonlat;
80314 var pos = [xa.c2p(lonlat), ya.c2p(lonlat)];
80315 var rad = di.mrc || 1;
80316
80317 pointData.x0 = pos[0] - rad;
80318 pointData.x1 = pos[0] + rad;
80319 pointData.y0 = pos[1] - rad;
80320 pointData.y1 = pos[1] + rad;
80321
80322 pointData.loc = di.loc;
80323 pointData.lon = lonlat[0];
80324 pointData.lat = lonlat[1];
80325
80326 var fullLayout = {};
80327 fullLayout[trace.geo] = {_subplot: geo};
80328 var labels = trace._module.formatLabels(di, trace, fullLayout);
80329 pointData.lonLabel = labels.lonLabel;
80330 pointData.latLabel = labels.latLabel;
80331
80332 pointData.color = getTraceColor(trace, di);
80333 pointData.extraText = getExtraText(trace, di, pointData, cd[0].t.labels);
80334 pointData.hovertemplate = trace.hovertemplate;
80335
80336 return [pointData];
80337};
80338
80339function getExtraText(trace, pt, pointData, labels) {
80340 if(trace.hovertemplate) return;
80341
80342 var hoverinfo = pt.hi || trace.hoverinfo;
80343
80344 var parts = hoverinfo === 'all' ?
80345 attributes.hoverinfo.flags :
80346 hoverinfo.split('+');
80347
80348 var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations);
80349 var hasLon = (parts.indexOf('lon') !== -1);
80350 var hasLat = (parts.indexOf('lat') !== -1);
80351 var hasText = (parts.indexOf('text') !== -1);
80352 var text = [];
80353
80354 function format(val) { return val + '\u00B0'; }
80355
80356 if(hasLocation) {
80357 text.push(pt.loc);
80358 } else if(hasLon && hasLat) {
80359 text.push('(' + format(pointData.lonLabel) + ', ' + format(pointData.latLabel) + ')');
80360 } else if(hasLon) {
80361 text.push(labels.lon + format(pointData.lonLabel));
80362 } else if(hasLat) {
80363 text.push(labels.lat + format(pointData.latLabel));
80364 }
80365
80366 if(hasText) {
80367 fillText(pt, trace, text);
80368 }
80369
80370 return text.join('<br>');
80371}
80372
80373},{"../../components/fx":90,"../../constants/numerical":155,"../../lib":177,"../scatter/get_trace_color":304,"./attributes":321}],327:[function(_dereq_,module,exports){
80374/**
80375* Copyright 2012-2020, Plotly, Inc.
80376* All rights reserved.
80377*
80378* This source code is licensed under the MIT license found in the
80379* LICENSE file in the root directory of this source tree.
80380*/
80381
80382'use strict';
80383
80384module.exports = {
80385 attributes: _dereq_('./attributes'),
80386 supplyDefaults: _dereq_('./defaults'),
80387 colorbar: _dereq_('../scatter/marker_colorbar'),
80388 formatLabels: _dereq_('./format_labels'),
80389 calc: _dereq_('./calc'),
80390 calcGeoJSON: _dereq_('./plot').calcGeoJSON,
80391 plot: _dereq_('./plot').plot,
80392 style: _dereq_('./style'),
80393 styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
80394 hoverPoints: _dereq_('./hover'),
80395 eventData: _dereq_('./event_data'),
80396 selectPoints: _dereq_('./select'),
80397
80398 moduleType: 'trace',
80399 name: 'scattergeo',
80400 basePlotModule: _dereq_('../../plots/geo'),
80401 categories: ['geo', 'symbols', 'showLegend', 'scatter-like'],
80402 meta: {
80403
80404
80405 }
80406};
80407
80408},{"../../plots/geo":254,"../scatter/marker_colorbar":312,"../scatter/style":317,"./attributes":321,"./calc":322,"./defaults":323,"./event_data":324,"./format_labels":325,"./hover":326,"./plot":328,"./select":329,"./style":330}],328:[function(_dereq_,module,exports){
80409/**
80410* Copyright 2012-2020, Plotly, Inc.
80411* All rights reserved.
80412*
80413* This source code is licensed under the MIT license found in the
80414* LICENSE file in the root directory of this source tree.
80415*/
80416
80417'use strict';
80418
80419var d3 = _dereq_('d3');
80420
80421var Lib = _dereq_('../../lib');
80422var getTopojsonFeatures = _dereq_('../../lib/topojson_utils').getTopojsonFeatures;
80423var geoJsonUtils = _dereq_('../../lib/geojson_utils');
80424var geoUtils = _dereq_('../../lib/geo_location_utils');
80425var findExtremes = _dereq_('../../plots/cartesian/autorange').findExtremes;
80426var BADNUM = _dereq_('../../constants/numerical').BADNUM;
80427
80428var calcMarkerSize = _dereq_('../scatter/calc').calcMarkerSize;
80429var subTypes = _dereq_('../scatter/subtypes');
80430var style = _dereq_('./style');
80431
80432function plot(gd, geo, calcData) {
80433 var scatterLayer = geo.layers.frontplot.select('.scatterlayer');
80434 var gTraces = Lib.makeTraceGroups(scatterLayer, calcData, 'trace scattergeo');
80435
80436 function removeBADNUM(d, node) {
80437 if(d.lonlat[0] === BADNUM) {
80438 d3.select(node).remove();
80439 }
80440 }
80441
80442 // TODO find a way to order the inner nodes on update
80443 gTraces.selectAll('*').remove();
80444
80445 gTraces.each(function(calcTrace) {
80446 var s = d3.select(this);
80447 var trace = calcTrace[0].trace;
80448
80449 if(subTypes.hasLines(trace) || trace.fill !== 'none') {
80450 var lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace);
80451
80452 var lineData = (trace.fill !== 'none') ?
80453 geoJsonUtils.makePolygon(lineCoords) :
80454 geoJsonUtils.makeLine(lineCoords);
80455
80456 s.selectAll('path.js-line')
80457 .data([{geojson: lineData, trace: trace}])
80458 .enter().append('path')
80459 .classed('js-line', true)
80460 .style('stroke-miterlimit', 2);
80461 }
80462
80463 if(subTypes.hasMarkers(trace)) {
80464 s.selectAll('path.point')
80465 .data(Lib.identity)
80466 .enter().append('path')
80467 .classed('point', true)
80468 .each(function(calcPt) { removeBADNUM(calcPt, this); });
80469 }
80470
80471 if(subTypes.hasText(trace)) {
80472 s.selectAll('g')
80473 .data(Lib.identity)
80474 .enter().append('g')
80475 .append('text')
80476 .each(function(calcPt) { removeBADNUM(calcPt, this); });
80477 }
80478
80479 // call style here within topojson request callback
80480 style(gd, calcTrace);
80481 });
80482}
80483
80484function calcGeoJSON(calcTrace, fullLayout) {
80485 var trace = calcTrace[0].trace;
80486 var geoLayout = fullLayout[trace.geo];
80487 var geo = geoLayout._subplot;
80488 var len = trace._length;
80489 var i, calcPt;
80490
80491 if(Array.isArray(trace.locations)) {
80492 var locationmode = trace.locationmode;
80493 var features = locationmode === 'geojson-id' ?
80494 geoUtils.extractTraceFeature(calcTrace) :
80495 getTopojsonFeatures(trace, geo.topojson);
80496
80497 for(i = 0; i < len; i++) {
80498 calcPt = calcTrace[i];
80499
80500 var feature = locationmode === 'geojson-id' ?
80501 calcPt.fOut :
80502 geoUtils.locationToFeature(locationmode, calcPt.loc, features);
80503
80504 calcPt.lonlat = feature ? feature.properties.ct : [BADNUM, BADNUM];
80505 }
80506 }
80507
80508 var opts = {padded: true};
80509 var lonArray;
80510 var latArray;
80511
80512 if(geoLayout.fitbounds === 'geojson' && trace.locationmode === 'geojson-id') {
80513 var bboxGeojson = geoUtils.computeBbox(geoUtils.getTraceGeojson(trace));
80514 lonArray = [bboxGeojson[0], bboxGeojson[2]];
80515 latArray = [bboxGeojson[1], bboxGeojson[3]];
80516 } else {
80517 lonArray = new Array(len);
80518 latArray = new Array(len);
80519 for(i = 0; i < len; i++) {
80520 calcPt = calcTrace[i];
80521 lonArray[i] = calcPt.lonlat[0];
80522 latArray[i] = calcPt.lonlat[1];
80523 }
80524
80525 opts.ppad = calcMarkerSize(trace, len);
80526 }
80527
80528 trace._extremes.lon = findExtremes(geoLayout.lonaxis._ax, lonArray, opts);
80529 trace._extremes.lat = findExtremes(geoLayout.lataxis._ax, latArray, opts);
80530}
80531
80532module.exports = {
80533 calcGeoJSON: calcGeoJSON,
80534 plot: plot
80535};
80536
80537},{"../../constants/numerical":155,"../../lib":177,"../../lib/geo_location_utils":173,"../../lib/geojson_utils":174,"../../lib/topojson_utils":201,"../../plots/cartesian/autorange":221,"../scatter/calc":295,"../scatter/subtypes":318,"./style":330,"d3":13}],329:[function(_dereq_,module,exports){
80538/**
80539* Copyright 2012-2020, Plotly, Inc.
80540* All rights reserved.
80541*
80542* This source code is licensed under the MIT license found in the
80543* LICENSE file in the root directory of this source tree.
80544*/
80545
80546'use strict';
80547
80548var subtypes = _dereq_('../scatter/subtypes');
80549var BADNUM = _dereq_('../../constants/numerical').BADNUM;
80550
80551module.exports = function selectPoints(searchInfo, selectionTester) {
80552 var cd = searchInfo.cd;
80553 var xa = searchInfo.xaxis;
80554 var ya = searchInfo.yaxis;
80555 var selection = [];
80556 var trace = cd[0].trace;
80557
80558 var di, lonlat, x, y, i;
80559
80560 var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
80561 if(hasOnlyLines) return [];
80562
80563 if(selectionTester === false) {
80564 for(i = 0; i < cd.length; i++) {
80565 cd[i].selected = 0;
80566 }
80567 } else {
80568 for(i = 0; i < cd.length; i++) {
80569 di = cd[i];
80570 lonlat = di.lonlat;
80571
80572 // some projection types can't handle BADNUMs
80573 if(lonlat[0] === BADNUM) continue;
80574
80575 x = xa.c2p(lonlat);
80576 y = ya.c2p(lonlat);
80577
80578 if(selectionTester.contains([x, y], null, i, searchInfo)) {
80579 selection.push({
80580 pointNumber: i,
80581 lon: lonlat[0],
80582 lat: lonlat[1]
80583 });
80584 di.selected = 1;
80585 } else {
80586 di.selected = 0;
80587 }
80588 }
80589 }
80590
80591 return selection;
80592};
80593
80594},{"../../constants/numerical":155,"../scatter/subtypes":318}],330:[function(_dereq_,module,exports){
80595/**
80596* Copyright 2012-2020, Plotly, Inc.
80597* All rights reserved.
80598*
80599* This source code is licensed under the MIT license found in the
80600* LICENSE file in the root directory of this source tree.
80601*/
80602
80603'use strict';
80604
80605var d3 = _dereq_('d3');
80606var Drawing = _dereq_('../../components/drawing');
80607var Color = _dereq_('../../components/color');
80608
80609var scatterStyle = _dereq_('../scatter/style');
80610var stylePoints = scatterStyle.stylePoints;
80611var styleText = scatterStyle.styleText;
80612
80613module.exports = function style(gd, calcTrace) {
80614 if(calcTrace) styleTrace(gd, calcTrace);
80615};
80616
80617function styleTrace(gd, calcTrace) {
80618 var trace = calcTrace[0].trace;
80619 var s = calcTrace[0].node3;
80620
80621 s.style('opacity', calcTrace[0].trace.opacity);
80622
80623 stylePoints(s, trace, gd);
80624 styleText(s, trace, gd);
80625
80626 // this part is incompatible with Drawing.lineGroupStyle
80627 s.selectAll('path.js-line')
80628 .style('fill', 'none')
80629 .each(function(d) {
80630 var path = d3.select(this);
80631 var trace = d.trace;
80632 var line = trace.line || {};
80633
80634 path.call(Color.stroke, line.color)
80635 .call(Drawing.dashLine, line.dash || '', line.width || 0);
80636
80637 if(trace.fill !== 'none') {
80638 path.call(Color.fill, trace.fillcolor);
80639 }
80640 });
80641}
80642
80643},{"../../components/color":50,"../../components/drawing":72,"../scatter/style":317,"d3":13}],331:[function(_dereq_,module,exports){
80644/**
80645* Copyright 2012-2020, Plotly, Inc.
80646* All rights reserved.
80647*
80648* This source code is licensed under the MIT license found in the
80649* LICENSE file in the root directory of this source tree.
80650*/
80651
80652'use strict';
80653
80654// package version injected by `npm run preprocess`
80655exports.version = '1.54.2';
80656
80657},{}]},{},[4])(4)
80658});