UNPKG

2.95 MBJavaScriptView Raw
1/**
2* plotly.js (cartesian) v1.54.0
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":178}],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/bar');
87
88},{"../src/traces/bar":287}],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/traces/box');
100
101},{"../src/traces/box":302}],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
112module.exports = _dereq_('../src/traces/contour');
113
114},{"../src/traces/contour":322}],5:[function(_dereq_,module,exports){
115/**
116* Copyright 2012-2020, Plotly, Inc.
117* All rights reserved.
118*
119* This source code is licensed under the MIT license found in the
120* LICENSE file in the root directory of this source tree.
121*/
122
123'use strict';
124
125module.exports = _dereq_('../src/core');
126
127},{"../src/core":160}],6:[function(_dereq_,module,exports){
128/**
129* Copyright 2012-2020, Plotly, Inc.
130* All rights reserved.
131*
132* This source code is licensed under the MIT license found in the
133* LICENSE file in the root directory of this source tree.
134*/
135
136'use strict';
137
138module.exports = _dereq_('../src/traces/heatmap');
139
140},{"../src/traces/heatmap":338}],7:[function(_dereq_,module,exports){
141/**
142* Copyright 2012-2020, Plotly, Inc.
143* All rights reserved.
144*
145* This source code is licensed under the MIT license found in the
146* LICENSE file in the root directory of this source tree.
147*/
148
149'use strict';
150
151module.exports = _dereq_('../src/traces/histogram');
152
153},{"../src/traces/histogram":356}],8:[function(_dereq_,module,exports){
154/**
155* Copyright 2012-2020, Plotly, Inc.
156* All rights reserved.
157*
158* This source code is licensed under the MIT license found in the
159* LICENSE file in the root directory of this source tree.
160*/
161
162'use strict';
163
164module.exports = _dereq_('../src/traces/histogram2d');
165
166},{"../src/traces/histogram2d":362}],9:[function(_dereq_,module,exports){
167/**
168* Copyright 2012-2020, Plotly, Inc.
169* All rights reserved.
170*
171* This source code is licensed under the MIT license found in the
172* LICENSE file in the root directory of this source tree.
173*/
174
175'use strict';
176
177module.exports = _dereq_('../src/traces/histogram2dcontour');
178
179},{"../src/traces/histogram2dcontour":366}],10:[function(_dereq_,module,exports){
180/**
181* Copyright 2012-2020, Plotly, Inc.
182* All rights reserved.
183*
184* This source code is licensed under the MIT license found in the
185* LICENSE file in the root directory of this source tree.
186*/
187
188'use strict';
189
190module.exports = _dereq_('../src/traces/image');
191
192},{"../src/traces/image":373}],11:[function(_dereq_,module,exports){
193/**
194* Copyright 2012-2020, Plotly, Inc.
195* All rights reserved.
196*
197* This source code is licensed under the MIT license found in the
198* LICENSE file in the root directory of this source tree.
199*/
200
201'use strict';
202
203var Plotly = _dereq_('./core');
204
205Plotly.register([
206 _dereq_('./bar'),
207 _dereq_('./box'),
208 _dereq_('./heatmap'),
209 _dereq_('./histogram'),
210 _dereq_('./histogram2d'),
211 _dereq_('./histogram2dcontour'),
212 _dereq_('./image'),
213 _dereq_('./pie'),
214 _dereq_('./contour'),
215 _dereq_('./scatterternary'),
216 _dereq_('./violin')
217]);
218
219module.exports = Plotly;
220
221},{"./bar":2,"./box":3,"./contour":4,"./core":5,"./heatmap":6,"./histogram":7,"./histogram2d":8,"./histogram2dcontour":9,"./image":10,"./pie":12,"./scatterternary":13,"./violin":14}],12:[function(_dereq_,module,exports){
222/**
223* Copyright 2012-2020, Plotly, Inc.
224* All rights reserved.
225*
226* This source code is licensed under the MIT license found in the
227* LICENSE file in the root directory of this source tree.
228*/
229
230'use strict';
231
232module.exports = _dereq_('../src/traces/pie');
233
234},{"../src/traces/pie":382}],13:[function(_dereq_,module,exports){
235/**
236* Copyright 2012-2020, Plotly, Inc.
237* All rights reserved.
238*
239* This source code is licensed under the MIT license found in the
240* LICENSE file in the root directory of this source tree.
241*/
242
243'use strict';
244
245module.exports = _dereq_('../src/traces/scatterternary');
246
247},{"../src/traces/scatterternary":422}],14:[function(_dereq_,module,exports){
248/**
249* Copyright 2012-2020, Plotly, Inc.
250* All rights reserved.
251*
252* This source code is licensed under the MIT license found in the
253* LICENSE file in the root directory of this source tree.
254*/
255
256'use strict';
257
258module.exports = _dereq_('../src/traces/violin');
259
260},{"../src/traces/violin":430}],15:[function(_dereq_,module,exports){
261// Copyright Joyent, Inc. and other Node contributors.
262//
263// Permission is hereby granted, free of charge, to any person obtaining a
264// copy of this software and associated documentation files (the
265// "Software"), to deal in the Software without restriction, including
266// without limitation the rights to use, copy, modify, merge, publish,
267// distribute, sublicense, and/or sell copies of the Software, and to permit
268// persons to whom the Software is furnished to do so, subject to the
269// following conditions:
270//
271// The above copyright notice and this permission notice shall be included
272// in all copies or substantial portions of the Software.
273//
274// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
275// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
276// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
277// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
278// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
279// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
280// USE OR OTHER DEALINGS IN THE SOFTWARE.
281
282var objectCreate = Object.create || objectCreatePolyfill
283var objectKeys = Object.keys || objectKeysPolyfill
284var bind = Function.prototype.bind || functionBindPolyfill
285
286function EventEmitter() {
287 if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
288 this._events = objectCreate(null);
289 this._eventsCount = 0;
290 }
291
292 this._maxListeners = this._maxListeners || undefined;
293}
294module.exports = EventEmitter;
295
296// Backwards-compat with node 0.10.x
297EventEmitter.EventEmitter = EventEmitter;
298
299EventEmitter.prototype._events = undefined;
300EventEmitter.prototype._maxListeners = undefined;
301
302// By default EventEmitters will print a warning if more than 10 listeners are
303// added to it. This is a useful default which helps finding memory leaks.
304var defaultMaxListeners = 10;
305
306var hasDefineProperty;
307try {
308 var o = {};
309 if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
310 hasDefineProperty = o.x === 0;
311} catch (err) { hasDefineProperty = false }
312if (hasDefineProperty) {
313 Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
314 enumerable: true,
315 get: function() {
316 return defaultMaxListeners;
317 },
318 set: function(arg) {
319 // check whether the input is a positive number (whose value is zero or
320 // greater and not a NaN).
321 if (typeof arg !== 'number' || arg < 0 || arg !== arg)
322 throw new TypeError('"defaultMaxListeners" must be a positive number');
323 defaultMaxListeners = arg;
324 }
325 });
326} else {
327 EventEmitter.defaultMaxListeners = defaultMaxListeners;
328}
329
330// Obviously not all Emitters should be limited to 10. This function allows
331// that to be increased. Set to zero for unlimited.
332EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
333 if (typeof n !== 'number' || n < 0 || isNaN(n))
334 throw new TypeError('"n" argument must be a positive number');
335 this._maxListeners = n;
336 return this;
337};
338
339function $getMaxListeners(that) {
340 if (that._maxListeners === undefined)
341 return EventEmitter.defaultMaxListeners;
342 return that._maxListeners;
343}
344
345EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
346 return $getMaxListeners(this);
347};
348
349// These standalone emit* functions are used to optimize calling of event
350// handlers for fast cases because emit() itself often has a variable number of
351// arguments and can be deoptimized because of that. These functions always have
352// the same number of arguments and thus do not get deoptimized, so the code
353// inside them can execute faster.
354function emitNone(handler, isFn, self) {
355 if (isFn)
356 handler.call(self);
357 else {
358 var len = handler.length;
359 var listeners = arrayClone(handler, len);
360 for (var i = 0; i < len; ++i)
361 listeners[i].call(self);
362 }
363}
364function emitOne(handler, isFn, self, arg1) {
365 if (isFn)
366 handler.call(self, arg1);
367 else {
368 var len = handler.length;
369 var listeners = arrayClone(handler, len);
370 for (var i = 0; i < len; ++i)
371 listeners[i].call(self, arg1);
372 }
373}
374function emitTwo(handler, isFn, self, arg1, arg2) {
375 if (isFn)
376 handler.call(self, arg1, arg2);
377 else {
378 var len = handler.length;
379 var listeners = arrayClone(handler, len);
380 for (var i = 0; i < len; ++i)
381 listeners[i].call(self, arg1, arg2);
382 }
383}
384function emitThree(handler, isFn, self, arg1, arg2, arg3) {
385 if (isFn)
386 handler.call(self, arg1, arg2, arg3);
387 else {
388 var len = handler.length;
389 var listeners = arrayClone(handler, len);
390 for (var i = 0; i < len; ++i)
391 listeners[i].call(self, arg1, arg2, arg3);
392 }
393}
394
395function emitMany(handler, isFn, self, args) {
396 if (isFn)
397 handler.apply(self, args);
398 else {
399 var len = handler.length;
400 var listeners = arrayClone(handler, len);
401 for (var i = 0; i < len; ++i)
402 listeners[i].apply(self, args);
403 }
404}
405
406EventEmitter.prototype.emit = function emit(type) {
407 var er, handler, len, args, i, events;
408 var doError = (type === 'error');
409
410 events = this._events;
411 if (events)
412 doError = (doError && events.error == null);
413 else if (!doError)
414 return false;
415
416 // If there is no 'error' event listener then throw.
417 if (doError) {
418 if (arguments.length > 1)
419 er = arguments[1];
420 if (er instanceof Error) {
421 throw er; // Unhandled 'error' event
422 } else {
423 // At least give some kind of context to the user
424 var err = new Error('Unhandled "error" event. (' + er + ')');
425 err.context = er;
426 throw err;
427 }
428 return false;
429 }
430
431 handler = events[type];
432
433 if (!handler)
434 return false;
435
436 var isFn = typeof handler === 'function';
437 len = arguments.length;
438 switch (len) {
439 // fast cases
440 case 1:
441 emitNone(handler, isFn, this);
442 break;
443 case 2:
444 emitOne(handler, isFn, this, arguments[1]);
445 break;
446 case 3:
447 emitTwo(handler, isFn, this, arguments[1], arguments[2]);
448 break;
449 case 4:
450 emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
451 break;
452 // slower
453 default:
454 args = new Array(len - 1);
455 for (i = 1; i < len; i++)
456 args[i - 1] = arguments[i];
457 emitMany(handler, isFn, this, args);
458 }
459
460 return true;
461};
462
463function _addListener(target, type, listener, prepend) {
464 var m;
465 var events;
466 var existing;
467
468 if (typeof listener !== 'function')
469 throw new TypeError('"listener" argument must be a function');
470
471 events = target._events;
472 if (!events) {
473 events = target._events = objectCreate(null);
474 target._eventsCount = 0;
475 } else {
476 // To avoid recursion in the case that type === "newListener"! Before
477 // adding it to the listeners, first emit "newListener".
478 if (events.newListener) {
479 target.emit('newListener', type,
480 listener.listener ? listener.listener : listener);
481
482 // Re-assign `events` because a newListener handler could have caused the
483 // this._events to be assigned to a new object
484 events = target._events;
485 }
486 existing = events[type];
487 }
488
489 if (!existing) {
490 // Optimize the case of one listener. Don't need the extra array object.
491 existing = events[type] = listener;
492 ++target._eventsCount;
493 } else {
494 if (typeof existing === 'function') {
495 // Adding the second element, need to change to array.
496 existing = events[type] =
497 prepend ? [listener, existing] : [existing, listener];
498 } else {
499 // If we've already got an array, just append.
500 if (prepend) {
501 existing.unshift(listener);
502 } else {
503 existing.push(listener);
504 }
505 }
506
507 // Check for listener leak
508 if (!existing.warned) {
509 m = $getMaxListeners(target);
510 if (m && m > 0 && existing.length > m) {
511 existing.warned = true;
512 var w = new Error('Possible EventEmitter memory leak detected. ' +
513 existing.length + ' "' + String(type) + '" listeners ' +
514 'added. Use emitter.setMaxListeners() to ' +
515 'increase limit.');
516 w.name = 'MaxListenersExceededWarning';
517 w.emitter = target;
518 w.type = type;
519 w.count = existing.length;
520 if (typeof console === 'object' && console.warn) {
521 console.warn('%s: %s', w.name, w.message);
522 }
523 }
524 }
525 }
526
527 return target;
528}
529
530EventEmitter.prototype.addListener = function addListener(type, listener) {
531 return _addListener(this, type, listener, false);
532};
533
534EventEmitter.prototype.on = EventEmitter.prototype.addListener;
535
536EventEmitter.prototype.prependListener =
537 function prependListener(type, listener) {
538 return _addListener(this, type, listener, true);
539 };
540
541function onceWrapper() {
542 if (!this.fired) {
543 this.target.removeListener(this.type, this.wrapFn);
544 this.fired = true;
545 switch (arguments.length) {
546 case 0:
547 return this.listener.call(this.target);
548 case 1:
549 return this.listener.call(this.target, arguments[0]);
550 case 2:
551 return this.listener.call(this.target, arguments[0], arguments[1]);
552 case 3:
553 return this.listener.call(this.target, arguments[0], arguments[1],
554 arguments[2]);
555 default:
556 var args = new Array(arguments.length);
557 for (var i = 0; i < args.length; ++i)
558 args[i] = arguments[i];
559 this.listener.apply(this.target, args);
560 }
561 }
562}
563
564function _onceWrap(target, type, listener) {
565 var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
566 var wrapped = bind.call(onceWrapper, state);
567 wrapped.listener = listener;
568 state.wrapFn = wrapped;
569 return wrapped;
570}
571
572EventEmitter.prototype.once = function once(type, listener) {
573 if (typeof listener !== 'function')
574 throw new TypeError('"listener" argument must be a function');
575 this.on(type, _onceWrap(this, type, listener));
576 return this;
577};
578
579EventEmitter.prototype.prependOnceListener =
580 function prependOnceListener(type, listener) {
581 if (typeof listener !== 'function')
582 throw new TypeError('"listener" argument must be a function');
583 this.prependListener(type, _onceWrap(this, type, listener));
584 return this;
585 };
586
587// Emits a 'removeListener' event if and only if the listener was removed.
588EventEmitter.prototype.removeListener =
589 function removeListener(type, listener) {
590 var list, events, position, i, originalListener;
591
592 if (typeof listener !== 'function')
593 throw new TypeError('"listener" argument must be a function');
594
595 events = this._events;
596 if (!events)
597 return this;
598
599 list = events[type];
600 if (!list)
601 return this;
602
603 if (list === listener || list.listener === listener) {
604 if (--this._eventsCount === 0)
605 this._events = objectCreate(null);
606 else {
607 delete events[type];
608 if (events.removeListener)
609 this.emit('removeListener', type, list.listener || listener);
610 }
611 } else if (typeof list !== 'function') {
612 position = -1;
613
614 for (i = list.length - 1; i >= 0; i--) {
615 if (list[i] === listener || list[i].listener === listener) {
616 originalListener = list[i].listener;
617 position = i;
618 break;
619 }
620 }
621
622 if (position < 0)
623 return this;
624
625 if (position === 0)
626 list.shift();
627 else
628 spliceOne(list, position);
629
630 if (list.length === 1)
631 events[type] = list[0];
632
633 if (events.removeListener)
634 this.emit('removeListener', type, originalListener || listener);
635 }
636
637 return this;
638 };
639
640EventEmitter.prototype.removeAllListeners =
641 function removeAllListeners(type) {
642 var listeners, events, i;
643
644 events = this._events;
645 if (!events)
646 return this;
647
648 // not listening for removeListener, no need to emit
649 if (!events.removeListener) {
650 if (arguments.length === 0) {
651 this._events = objectCreate(null);
652 this._eventsCount = 0;
653 } else if (events[type]) {
654 if (--this._eventsCount === 0)
655 this._events = objectCreate(null);
656 else
657 delete events[type];
658 }
659 return this;
660 }
661
662 // emit removeListener for all listeners on all events
663 if (arguments.length === 0) {
664 var keys = objectKeys(events);
665 var key;
666 for (i = 0; i < keys.length; ++i) {
667 key = keys[i];
668 if (key === 'removeListener') continue;
669 this.removeAllListeners(key);
670 }
671 this.removeAllListeners('removeListener');
672 this._events = objectCreate(null);
673 this._eventsCount = 0;
674 return this;
675 }
676
677 listeners = events[type];
678
679 if (typeof listeners === 'function') {
680 this.removeListener(type, listeners);
681 } else if (listeners) {
682 // LIFO order
683 for (i = listeners.length - 1; i >= 0; i--) {
684 this.removeListener(type, listeners[i]);
685 }
686 }
687
688 return this;
689 };
690
691function _listeners(target, type, unwrap) {
692 var events = target._events;
693
694 if (!events)
695 return [];
696
697 var evlistener = events[type];
698 if (!evlistener)
699 return [];
700
701 if (typeof evlistener === 'function')
702 return unwrap ? [evlistener.listener || evlistener] : [evlistener];
703
704 return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
705}
706
707EventEmitter.prototype.listeners = function listeners(type) {
708 return _listeners(this, type, true);
709};
710
711EventEmitter.prototype.rawListeners = function rawListeners(type) {
712 return _listeners(this, type, false);
713};
714
715EventEmitter.listenerCount = function(emitter, type) {
716 if (typeof emitter.listenerCount === 'function') {
717 return emitter.listenerCount(type);
718 } else {
719 return listenerCount.call(emitter, type);
720 }
721};
722
723EventEmitter.prototype.listenerCount = listenerCount;
724function listenerCount(type) {
725 var events = this._events;
726
727 if (events) {
728 var evlistener = events[type];
729
730 if (typeof evlistener === 'function') {
731 return 1;
732 } else if (evlistener) {
733 return evlistener.length;
734 }
735 }
736
737 return 0;
738}
739
740EventEmitter.prototype.eventNames = function eventNames() {
741 return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
742};
743
744// About 1.5x faster than the two-arg version of Array#splice().
745function spliceOne(list, index) {
746 for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
747 list[i] = list[k];
748 list.pop();
749}
750
751function arrayClone(arr, n) {
752 var copy = new Array(n);
753 for (var i = 0; i < n; ++i)
754 copy[i] = arr[i];
755 return copy;
756}
757
758function unwrapListeners(arr) {
759 var ret = new Array(arr.length);
760 for (var i = 0; i < ret.length; ++i) {
761 ret[i] = arr[i].listener || arr[i];
762 }
763 return ret;
764}
765
766function objectCreatePolyfill(proto) {
767 var F = function() {};
768 F.prototype = proto;
769 return new F;
770}
771function objectKeysPolyfill(obj) {
772 var keys = [];
773 for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
774 keys.push(k);
775 }
776 return k;
777}
778function functionBindPolyfill(context) {
779 var fn = this;
780 return function () {
781 return fn.apply(context, arguments);
782 };
783}
784
785},{}],16:[function(_dereq_,module,exports){
786!function() {
787 var d3 = {
788 version: "3.5.17"
789 };
790 var d3_arraySlice = [].slice, d3_array = function(list) {
791 return d3_arraySlice.call(list);
792 };
793 var d3_document = this.document;
794 function d3_documentElement(node) {
795 return node && (node.ownerDocument || node.document || node).documentElement;
796 }
797 function d3_window(node) {
798 return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView);
799 }
800 if (d3_document) {
801 try {
802 d3_array(d3_document.documentElement.childNodes)[0].nodeType;
803 } catch (e) {
804 d3_array = function(list) {
805 var i = list.length, array = new Array(i);
806 while (i--) array[i] = list[i];
807 return array;
808 };
809 }
810 }
811 if (!Date.now) Date.now = function() {
812 return +new Date();
813 };
814 if (d3_document) {
815 try {
816 d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
817 } catch (error) {
818 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;
819 d3_element_prototype.setAttribute = function(name, value) {
820 d3_element_setAttribute.call(this, name, value + "");
821 };
822 d3_element_prototype.setAttributeNS = function(space, local, value) {
823 d3_element_setAttributeNS.call(this, space, local, value + "");
824 };
825 d3_style_prototype.setProperty = function(name, value, priority) {
826 d3_style_setProperty.call(this, name, value + "", priority);
827 };
828 }
829 }
830 d3.ascending = d3_ascending;
831 function d3_ascending(a, b) {
832 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
833 }
834 d3.descending = function(a, b) {
835 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
836 };
837 d3.min = function(array, f) {
838 var i = -1, n = array.length, a, b;
839 if (arguments.length === 1) {
840 while (++i < n) if ((b = array[i]) != null && b >= b) {
841 a = b;
842 break;
843 }
844 while (++i < n) if ((b = array[i]) != null && a > b) a = b;
845 } else {
846 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
847 a = b;
848 break;
849 }
850 while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
851 }
852 return a;
853 };
854 d3.max = function(array, f) {
855 var i = -1, n = array.length, a, b;
856 if (arguments.length === 1) {
857 while (++i < n) if ((b = array[i]) != null && b >= b) {
858 a = b;
859 break;
860 }
861 while (++i < n) if ((b = array[i]) != null && b > a) a = b;
862 } else {
863 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
864 a = b;
865 break;
866 }
867 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
868 }
869 return a;
870 };
871 d3.extent = function(array, f) {
872 var i = -1, n = array.length, a, b, c;
873 if (arguments.length === 1) {
874 while (++i < n) if ((b = array[i]) != null && b >= b) {
875 a = c = b;
876 break;
877 }
878 while (++i < n) if ((b = array[i]) != null) {
879 if (a > b) a = b;
880 if (c < b) c = b;
881 }
882 } else {
883 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
884 a = c = b;
885 break;
886 }
887 while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
888 if (a > b) a = b;
889 if (c < b) c = b;
890 }
891 }
892 return [ a, c ];
893 };
894 function d3_number(x) {
895 return x === null ? NaN : +x;
896 }
897 function d3_numeric(x) {
898 return !isNaN(x);
899 }
900 d3.sum = function(array, f) {
901 var s = 0, n = array.length, a, i = -1;
902 if (arguments.length === 1) {
903 while (++i < n) if (d3_numeric(a = +array[i])) s += a;
904 } else {
905 while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
906 }
907 return s;
908 };
909 d3.mean = function(array, f) {
910 var s = 0, n = array.length, a, i = -1, j = n;
911 if (arguments.length === 1) {
912 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
913 } else {
914 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
915 }
916 if (j) return s / j;
917 };
918 d3.quantile = function(values, p) {
919 var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
920 return e ? v + e * (values[h] - v) : v;
921 };
922 d3.median = function(array, f) {
923 var numbers = [], n = array.length, a, i = -1;
924 if (arguments.length === 1) {
925 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
926 } else {
927 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
928 }
929 if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
930 };
931 d3.variance = function(array, f) {
932 var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
933 if (arguments.length === 1) {
934 while (++i < n) {
935 if (d3_numeric(a = d3_number(array[i]))) {
936 d = a - m;
937 m += d / ++j;
938 s += d * (a - m);
939 }
940 }
941 } else {
942 while (++i < n) {
943 if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
944 d = a - m;
945 m += d / ++j;
946 s += d * (a - m);
947 }
948 }
949 }
950 if (j > 1) return s / (j - 1);
951 };
952 d3.deviation = function() {
953 var v = d3.variance.apply(this, arguments);
954 return v ? Math.sqrt(v) : v;
955 };
956 function d3_bisector(compare) {
957 return {
958 left: function(a, x, lo, hi) {
959 if (arguments.length < 3) lo = 0;
960 if (arguments.length < 4) hi = a.length;
961 while (lo < hi) {
962 var mid = lo + hi >>> 1;
963 if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
964 }
965 return lo;
966 },
967 right: function(a, x, lo, hi) {
968 if (arguments.length < 3) lo = 0;
969 if (arguments.length < 4) hi = a.length;
970 while (lo < hi) {
971 var mid = lo + hi >>> 1;
972 if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
973 }
974 return lo;
975 }
976 };
977 }
978 var d3_bisect = d3_bisector(d3_ascending);
979 d3.bisectLeft = d3_bisect.left;
980 d3.bisect = d3.bisectRight = d3_bisect.right;
981 d3.bisector = function(f) {
982 return d3_bisector(f.length === 1 ? function(d, x) {
983 return d3_ascending(f(d), x);
984 } : f);
985 };
986 d3.shuffle = function(array, i0, i1) {
987 if ((m = arguments.length) < 3) {
988 i1 = array.length;
989 if (m < 2) i0 = 0;
990 }
991 var m = i1 - i0, t, i;
992 while (m) {
993 i = Math.random() * m-- | 0;
994 t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
995 }
996 return array;
997 };
998 d3.permute = function(array, indexes) {
999 var i = indexes.length, permutes = new Array(i);
1000 while (i--) permutes[i] = array[indexes[i]];
1001 return permutes;
1002 };
1003 d3.pairs = function(array) {
1004 var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
1005 while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
1006 return pairs;
1007 };
1008 d3.transpose = function(matrix) {
1009 if (!(n = matrix.length)) return [];
1010 for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) {
1011 for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) {
1012 row[j] = matrix[j][i];
1013 }
1014 }
1015 return transpose;
1016 };
1017 function d3_transposeLength(d) {
1018 return d.length;
1019 }
1020 d3.zip = function() {
1021 return d3.transpose(arguments);
1022 };
1023 d3.keys = function(map) {
1024 var keys = [];
1025 for (var key in map) keys.push(key);
1026 return keys;
1027 };
1028 d3.values = function(map) {
1029 var values = [];
1030 for (var key in map) values.push(map[key]);
1031 return values;
1032 };
1033 d3.entries = function(map) {
1034 var entries = [];
1035 for (var key in map) entries.push({
1036 key: key,
1037 value: map[key]
1038 });
1039 return entries;
1040 };
1041 d3.merge = function(arrays) {
1042 var n = arrays.length, m, i = -1, j = 0, merged, array;
1043 while (++i < n) j += arrays[i].length;
1044 merged = new Array(j);
1045 while (--n >= 0) {
1046 array = arrays[n];
1047 m = array.length;
1048 while (--m >= 0) {
1049 merged[--j] = array[m];
1050 }
1051 }
1052 return merged;
1053 };
1054 var abs = Math.abs;
1055 d3.range = function(start, stop, step) {
1056 if (arguments.length < 3) {
1057 step = 1;
1058 if (arguments.length < 2) {
1059 stop = start;
1060 start = 0;
1061 }
1062 }
1063 if ((stop - start) / step === Infinity) throw new Error("infinite range");
1064 var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
1065 start *= k, stop *= k, step *= k;
1066 if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
1067 return range;
1068 };
1069 function d3_range_integerScale(x) {
1070 var k = 1;
1071 while (x * k % 1) k *= 10;
1072 return k;
1073 }
1074 function d3_class(ctor, properties) {
1075 for (var key in properties) {
1076 Object.defineProperty(ctor.prototype, key, {
1077 value: properties[key],
1078 enumerable: false
1079 });
1080 }
1081 }
1082 d3.map = function(object, f) {
1083 var map = new d3_Map();
1084 if (object instanceof d3_Map) {
1085 object.forEach(function(key, value) {
1086 map.set(key, value);
1087 });
1088 } else if (Array.isArray(object)) {
1089 var i = -1, n = object.length, o;
1090 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);
1091 } else {
1092 for (var key in object) map.set(key, object[key]);
1093 }
1094 return map;
1095 };
1096 function d3_Map() {
1097 this._ = Object.create(null);
1098 }
1099 var d3_map_proto = "__proto__", d3_map_zero = "\x00";
1100 d3_class(d3_Map, {
1101 has: d3_map_has,
1102 get: function(key) {
1103 return this._[d3_map_escape(key)];
1104 },
1105 set: function(key, value) {
1106 return this._[d3_map_escape(key)] = value;
1107 },
1108 remove: d3_map_remove,
1109 keys: d3_map_keys,
1110 values: function() {
1111 var values = [];
1112 for (var key in this._) values.push(this._[key]);
1113 return values;
1114 },
1115 entries: function() {
1116 var entries = [];
1117 for (var key in this._) entries.push({
1118 key: d3_map_unescape(key),
1119 value: this._[key]
1120 });
1121 return entries;
1122 },
1123 size: d3_map_size,
1124 empty: d3_map_empty,
1125 forEach: function(f) {
1126 for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
1127 }
1128 });
1129 function d3_map_escape(key) {
1130 return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
1131 }
1132 function d3_map_unescape(key) {
1133 return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
1134 }
1135 function d3_map_has(key) {
1136 return d3_map_escape(key) in this._;
1137 }
1138 function d3_map_remove(key) {
1139 return (key = d3_map_escape(key)) in this._ && delete this._[key];
1140 }
1141 function d3_map_keys() {
1142 var keys = [];
1143 for (var key in this._) keys.push(d3_map_unescape(key));
1144 return keys;
1145 }
1146 function d3_map_size() {
1147 var size = 0;
1148 for (var key in this._) ++size;
1149 return size;
1150 }
1151 function d3_map_empty() {
1152 for (var key in this._) return false;
1153 return true;
1154 }
1155 d3.nest = function() {
1156 var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
1157 function map(mapType, array, depth) {
1158 if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
1159 var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
1160 while (++i < n) {
1161 if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
1162 values.push(object);
1163 } else {
1164 valuesByKey.set(keyValue, [ object ]);
1165 }
1166 }
1167 if (mapType) {
1168 object = mapType();
1169 setter = function(keyValue, values) {
1170 object.set(keyValue, map(mapType, values, depth));
1171 };
1172 } else {
1173 object = {};
1174 setter = function(keyValue, values) {
1175 object[keyValue] = map(mapType, values, depth);
1176 };
1177 }
1178 valuesByKey.forEach(setter);
1179 return object;
1180 }
1181 function entries(map, depth) {
1182 if (depth >= keys.length) return map;
1183 var array = [], sortKey = sortKeys[depth++];
1184 map.forEach(function(key, keyMap) {
1185 array.push({
1186 key: key,
1187 values: entries(keyMap, depth)
1188 });
1189 });
1190 return sortKey ? array.sort(function(a, b) {
1191 return sortKey(a.key, b.key);
1192 }) : array;
1193 }
1194 nest.map = function(array, mapType) {
1195 return map(mapType, array, 0);
1196 };
1197 nest.entries = function(array) {
1198 return entries(map(d3.map, array, 0), 0);
1199 };
1200 nest.key = function(d) {
1201 keys.push(d);
1202 return nest;
1203 };
1204 nest.sortKeys = function(order) {
1205 sortKeys[keys.length - 1] = order;
1206 return nest;
1207 };
1208 nest.sortValues = function(order) {
1209 sortValues = order;
1210 return nest;
1211 };
1212 nest.rollup = function(f) {
1213 rollup = f;
1214 return nest;
1215 };
1216 return nest;
1217 };
1218 d3.set = function(array) {
1219 var set = new d3_Set();
1220 if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
1221 return set;
1222 };
1223 function d3_Set() {
1224 this._ = Object.create(null);
1225 }
1226 d3_class(d3_Set, {
1227 has: d3_map_has,
1228 add: function(key) {
1229 this._[d3_map_escape(key += "")] = true;
1230 return key;
1231 },
1232 remove: d3_map_remove,
1233 values: d3_map_keys,
1234 size: d3_map_size,
1235 empty: d3_map_empty,
1236 forEach: function(f) {
1237 for (var key in this._) f.call(this, d3_map_unescape(key));
1238 }
1239 });
1240 d3.behavior = {};
1241 function d3_identity(d) {
1242 return d;
1243 }
1244 d3.rebind = function(target, source) {
1245 var i = 1, n = arguments.length, method;
1246 while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
1247 return target;
1248 };
1249 function d3_rebind(target, source, method) {
1250 return function() {
1251 var value = method.apply(source, arguments);
1252 return value === source ? target : value;
1253 };
1254 }
1255 function d3_vendorSymbol(object, name) {
1256 if (name in object) return name;
1257 name = name.charAt(0).toUpperCase() + name.slice(1);
1258 for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
1259 var prefixName = d3_vendorPrefixes[i] + name;
1260 if (prefixName in object) return prefixName;
1261 }
1262 }
1263 var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
1264 function d3_noop() {}
1265 d3.dispatch = function() {
1266 var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
1267 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
1268 return dispatch;
1269 };
1270 function d3_dispatch() {}
1271 d3_dispatch.prototype.on = function(type, listener) {
1272 var i = type.indexOf("."), name = "";
1273 if (i >= 0) {
1274 name = type.slice(i + 1);
1275 type = type.slice(0, i);
1276 }
1277 if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
1278 if (arguments.length === 2) {
1279 if (listener == null) for (type in this) {
1280 if (this.hasOwnProperty(type)) this[type].on(name, null);
1281 }
1282 return this;
1283 }
1284 };
1285 function d3_dispatch_event(dispatch) {
1286 var listeners = [], listenerByName = new d3_Map();
1287 function event() {
1288 var z = listeners, i = -1, n = z.length, l;
1289 while (++i < n) if (l = z[i].on) l.apply(this, arguments);
1290 return dispatch;
1291 }
1292 event.on = function(name, listener) {
1293 var l = listenerByName.get(name), i;
1294 if (arguments.length < 2) return l && l.on;
1295 if (l) {
1296 l.on = null;
1297 listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
1298 listenerByName.remove(name);
1299 }
1300 if (listener) listeners.push(listenerByName.set(name, {
1301 on: listener
1302 }));
1303 return dispatch;
1304 };
1305 return event;
1306 }
1307 d3.event = null;
1308 function d3_eventPreventDefault() {
1309 d3.event.preventDefault();
1310 }
1311 function d3_eventSource() {
1312 var e = d3.event, s;
1313 while (s = e.sourceEvent) e = s;
1314 return e;
1315 }
1316 function d3_eventDispatch(target) {
1317 var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
1318 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
1319 dispatch.of = function(thiz, argumentz) {
1320 return function(e1) {
1321 try {
1322 var e0 = e1.sourceEvent = d3.event;
1323 e1.target = target;
1324 d3.event = e1;
1325 dispatch[e1.type].apply(thiz, argumentz);
1326 } finally {
1327 d3.event = e0;
1328 }
1329 };
1330 };
1331 return dispatch;
1332 }
1333 d3.requote = function(s) {
1334 return s.replace(d3_requote_re, "\\$&");
1335 };
1336 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
1337 var d3_subclass = {}.__proto__ ? function(object, prototype) {
1338 object.__proto__ = prototype;
1339 } : function(object, prototype) {
1340 for (var property in prototype) object[property] = prototype[property];
1341 };
1342 function d3_selection(groups) {
1343 d3_subclass(groups, d3_selectionPrototype);
1344 return groups;
1345 }
1346 var d3_select = function(s, n) {
1347 return n.querySelector(s);
1348 }, d3_selectAll = function(s, n) {
1349 return n.querySelectorAll(s);
1350 }, d3_selectMatches = function(n, s) {
1351 var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
1352 d3_selectMatches = function(n, s) {
1353 return d3_selectMatcher.call(n, s);
1354 };
1355 return d3_selectMatches(n, s);
1356 };
1357 if (typeof Sizzle === "function") {
1358 d3_select = function(s, n) {
1359 return Sizzle(s, n)[0] || null;
1360 };
1361 d3_selectAll = Sizzle;
1362 d3_selectMatches = Sizzle.matchesSelector;
1363 }
1364 d3.selection = function() {
1365 return d3.select(d3_document.documentElement);
1366 };
1367 var d3_selectionPrototype = d3.selection.prototype = [];
1368 d3_selectionPrototype.select = function(selector) {
1369 var subgroups = [], subgroup, subnode, group, node;
1370 selector = d3_selection_selector(selector);
1371 for (var j = -1, m = this.length; ++j < m; ) {
1372 subgroups.push(subgroup = []);
1373 subgroup.parentNode = (group = this[j]).parentNode;
1374 for (var i = -1, n = group.length; ++i < n; ) {
1375 if (node = group[i]) {
1376 subgroup.push(subnode = selector.call(node, node.__data__, i, j));
1377 if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
1378 } else {
1379 subgroup.push(null);
1380 }
1381 }
1382 }
1383 return d3_selection(subgroups);
1384 };
1385 function d3_selection_selector(selector) {
1386 return typeof selector === "function" ? selector : function() {
1387 return d3_select(selector, this);
1388 };
1389 }
1390 d3_selectionPrototype.selectAll = function(selector) {
1391 var subgroups = [], subgroup, node;
1392 selector = d3_selection_selectorAll(selector);
1393 for (var j = -1, m = this.length; ++j < m; ) {
1394 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
1395 if (node = group[i]) {
1396 subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
1397 subgroup.parentNode = node;
1398 }
1399 }
1400 }
1401 return d3_selection(subgroups);
1402 };
1403 function d3_selection_selectorAll(selector) {
1404 return typeof selector === "function" ? selector : function() {
1405 return d3_selectAll(selector, this);
1406 };
1407 }
1408 var d3_nsXhtml = "http://www.w3.org/1999/xhtml";
1409 var d3_nsPrefix = {
1410 svg: "http://www.w3.org/2000/svg",
1411 xhtml: d3_nsXhtml,
1412 xlink: "http://www.w3.org/1999/xlink",
1413 xml: "http://www.w3.org/XML/1998/namespace",
1414 xmlns: "http://www.w3.org/2000/xmlns/"
1415 };
1416 d3.ns = {
1417 prefix: d3_nsPrefix,
1418 qualify: function(name) {
1419 var i = name.indexOf(":"), prefix = name;
1420 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
1421 return d3_nsPrefix.hasOwnProperty(prefix) ? {
1422 space: d3_nsPrefix[prefix],
1423 local: name
1424 } : name;
1425 }
1426 };
1427 d3_selectionPrototype.attr = function(name, value) {
1428 if (arguments.length < 2) {
1429 if (typeof name === "string") {
1430 var node = this.node();
1431 name = d3.ns.qualify(name);
1432 return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
1433 }
1434 for (value in name) this.each(d3_selection_attr(value, name[value]));
1435 return this;
1436 }
1437 return this.each(d3_selection_attr(name, value));
1438 };
1439 function d3_selection_attr(name, value) {
1440 name = d3.ns.qualify(name);
1441 function attrNull() {
1442 this.removeAttribute(name);
1443 }
1444 function attrNullNS() {
1445 this.removeAttributeNS(name.space, name.local);
1446 }
1447 function attrConstant() {
1448 this.setAttribute(name, value);
1449 }
1450 function attrConstantNS() {
1451 this.setAttributeNS(name.space, name.local, value);
1452 }
1453 function attrFunction() {
1454 var x = value.apply(this, arguments);
1455 if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
1456 }
1457 function attrFunctionNS() {
1458 var x = value.apply(this, arguments);
1459 if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
1460 }
1461 return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
1462 }
1463 function d3_collapse(s) {
1464 return s.trim().replace(/\s+/g, " ");
1465 }
1466 d3_selectionPrototype.classed = function(name, value) {
1467 if (arguments.length < 2) {
1468 if (typeof name === "string") {
1469 var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
1470 if (value = node.classList) {
1471 while (++i < n) if (!value.contains(name[i])) return false;
1472 } else {
1473 value = node.getAttribute("class");
1474 while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1475 }
1476 return true;
1477 }
1478 for (value in name) this.each(d3_selection_classed(value, name[value]));
1479 return this;
1480 }
1481 return this.each(d3_selection_classed(name, value));
1482 };
1483 function d3_selection_classedRe(name) {
1484 return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1485 }
1486 function d3_selection_classes(name) {
1487 return (name + "").trim().split(/^|\s+/);
1488 }
1489 function d3_selection_classed(name, value) {
1490 name = d3_selection_classes(name).map(d3_selection_classedName);
1491 var n = name.length;
1492 function classedConstant() {
1493 var i = -1;
1494 while (++i < n) name[i](this, value);
1495 }
1496 function classedFunction() {
1497 var i = -1, x = value.apply(this, arguments);
1498 while (++i < n) name[i](this, x);
1499 }
1500 return typeof value === "function" ? classedFunction : classedConstant;
1501 }
1502 function d3_selection_classedName(name) {
1503 var re = d3_selection_classedRe(name);
1504 return function(node, value) {
1505 if (c = node.classList) return value ? c.add(name) : c.remove(name);
1506 var c = node.getAttribute("class") || "";
1507 if (value) {
1508 re.lastIndex = 0;
1509 if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1510 } else {
1511 node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1512 }
1513 };
1514 }
1515 d3_selectionPrototype.style = function(name, value, priority) {
1516 var n = arguments.length;
1517 if (n < 3) {
1518 if (typeof name !== "string") {
1519 if (n < 2) value = "";
1520 for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
1521 return this;
1522 }
1523 if (n < 2) {
1524 var node = this.node();
1525 return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1526 }
1527 priority = "";
1528 }
1529 return this.each(d3_selection_style(name, value, priority));
1530 };
1531 function d3_selection_style(name, value, priority) {
1532 function styleNull() {
1533 this.style.removeProperty(name);
1534 }
1535 function styleConstant() {
1536 this.style.setProperty(name, value, priority);
1537 }
1538 function styleFunction() {
1539 var x = value.apply(this, arguments);
1540 if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
1541 }
1542 return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
1543 }
1544 d3_selectionPrototype.property = function(name, value) {
1545 if (arguments.length < 2) {
1546 if (typeof name === "string") return this.node()[name];
1547 for (value in name) this.each(d3_selection_property(value, name[value]));
1548 return this;
1549 }
1550 return this.each(d3_selection_property(name, value));
1551 };
1552 function d3_selection_property(name, value) {
1553 function propertyNull() {
1554 delete this[name];
1555 }
1556 function propertyConstant() {
1557 this[name] = value;
1558 }
1559 function propertyFunction() {
1560 var x = value.apply(this, arguments);
1561 if (x == null) delete this[name]; else this[name] = x;
1562 }
1563 return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
1564 }
1565 d3_selectionPrototype.text = function(value) {
1566 return arguments.length ? this.each(typeof value === "function" ? function() {
1567 var v = value.apply(this, arguments);
1568 this.textContent = v == null ? "" : v;
1569 } : value == null ? function() {
1570 this.textContent = "";
1571 } : function() {
1572 this.textContent = value;
1573 }) : this.node().textContent;
1574 };
1575 d3_selectionPrototype.html = function(value) {
1576 return arguments.length ? this.each(typeof value === "function" ? function() {
1577 var v = value.apply(this, arguments);
1578 this.innerHTML = v == null ? "" : v;
1579 } : value == null ? function() {
1580 this.innerHTML = "";
1581 } : function() {
1582 this.innerHTML = value;
1583 }) : this.node().innerHTML;
1584 };
1585 d3_selectionPrototype.append = function(name) {
1586 name = d3_selection_creator(name);
1587 return this.select(function() {
1588 return this.appendChild(name.apply(this, arguments));
1589 });
1590 };
1591 function d3_selection_creator(name) {
1592 function create() {
1593 var document = this.ownerDocument, namespace = this.namespaceURI;
1594 return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name);
1595 }
1596 function createNS() {
1597 return this.ownerDocument.createElementNS(name.space, name.local);
1598 }
1599 return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
1600 }
1601 d3_selectionPrototype.insert = function(name, before) {
1602 name = d3_selection_creator(name);
1603 before = d3_selection_selector(before);
1604 return this.select(function() {
1605 return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1606 });
1607 };
1608 d3_selectionPrototype.remove = function() {
1609 return this.each(d3_selectionRemove);
1610 };
1611 function d3_selectionRemove() {
1612 var parent = this.parentNode;
1613 if (parent) parent.removeChild(this);
1614 }
1615 d3_selectionPrototype.data = function(value, key) {
1616 var i = -1, n = this.length, group, node;
1617 if (!arguments.length) {
1618 value = new Array(n = (group = this[0]).length);
1619 while (++i < n) {
1620 if (node = group[i]) {
1621 value[i] = node.__data__;
1622 }
1623 }
1624 return value;
1625 }
1626 function bind(group, groupData) {
1627 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;
1628 if (key) {
1629 var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
1630 for (i = -1; ++i < n; ) {
1631 if (node = group[i]) {
1632 if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) {
1633 exitNodes[i] = node;
1634 } else {
1635 nodeByKeyValue.set(keyValue, node);
1636 }
1637 keyValues[i] = keyValue;
1638 }
1639 }
1640 for (i = -1; ++i < m; ) {
1641 if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1642 enterNodes[i] = d3_selection_dataNode(nodeData);
1643 } else if (node !== true) {
1644 updateNodes[i] = node;
1645 node.__data__ = nodeData;
1646 }
1647 nodeByKeyValue.set(keyValue, true);
1648 }
1649 for (i = -1; ++i < n; ) {
1650 if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) {
1651 exitNodes[i] = group[i];
1652 }
1653 }
1654 } else {
1655 for (i = -1; ++i < n0; ) {
1656 node = group[i];
1657 nodeData = groupData[i];
1658 if (node) {
1659 node.__data__ = nodeData;
1660 updateNodes[i] = node;
1661 } else {
1662 enterNodes[i] = d3_selection_dataNode(nodeData);
1663 }
1664 }
1665 for (;i < m; ++i) {
1666 enterNodes[i] = d3_selection_dataNode(groupData[i]);
1667 }
1668 for (;i < n; ++i) {
1669 exitNodes[i] = group[i];
1670 }
1671 }
1672 enterNodes.update = updateNodes;
1673 enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
1674 enter.push(enterNodes);
1675 update.push(updateNodes);
1676 exit.push(exitNodes);
1677 }
1678 var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
1679 if (typeof value === "function") {
1680 while (++i < n) {
1681 bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1682 }
1683 } else {
1684 while (++i < n) {
1685 bind(group = this[i], value);
1686 }
1687 }
1688 update.enter = function() {
1689 return enter;
1690 };
1691 update.exit = function() {
1692 return exit;
1693 };
1694 return update;
1695 };
1696 function d3_selection_dataNode(data) {
1697 return {
1698 __data__: data
1699 };
1700 }
1701 d3_selectionPrototype.datum = function(value) {
1702 return arguments.length ? this.property("__data__", value) : this.property("__data__");
1703 };
1704 d3_selectionPrototype.filter = function(filter) {
1705 var subgroups = [], subgroup, group, node;
1706 if (typeof filter !== "function") filter = d3_selection_filter(filter);
1707 for (var j = 0, m = this.length; j < m; j++) {
1708 subgroups.push(subgroup = []);
1709 subgroup.parentNode = (group = this[j]).parentNode;
1710 for (var i = 0, n = group.length; i < n; i++) {
1711 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1712 subgroup.push(node);
1713 }
1714 }
1715 }
1716 return d3_selection(subgroups);
1717 };
1718 function d3_selection_filter(selector) {
1719 return function() {
1720 return d3_selectMatches(this, selector);
1721 };
1722 }
1723 d3_selectionPrototype.order = function() {
1724 for (var j = -1, m = this.length; ++j < m; ) {
1725 for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
1726 if (node = group[i]) {
1727 if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1728 next = node;
1729 }
1730 }
1731 }
1732 return this;
1733 };
1734 d3_selectionPrototype.sort = function(comparator) {
1735 comparator = d3_selection_sortComparator.apply(this, arguments);
1736 for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
1737 return this.order();
1738 };
1739 function d3_selection_sortComparator(comparator) {
1740 if (!arguments.length) comparator = d3_ascending;
1741 return function(a, b) {
1742 return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1743 };
1744 }
1745 d3_selectionPrototype.each = function(callback) {
1746 return d3_selection_each(this, function(node, i, j) {
1747 callback.call(node, node.__data__, i, j);
1748 });
1749 };
1750 function d3_selection_each(groups, callback) {
1751 for (var j = 0, m = groups.length; j < m; j++) {
1752 for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1753 if (node = group[i]) callback(node, i, j);
1754 }
1755 }
1756 return groups;
1757 }
1758 d3_selectionPrototype.call = function(callback) {
1759 var args = d3_array(arguments);
1760 callback.apply(args[0] = this, args);
1761 return this;
1762 };
1763 d3_selectionPrototype.empty = function() {
1764 return !this.node();
1765 };
1766 d3_selectionPrototype.node = function() {
1767 for (var j = 0, m = this.length; j < m; j++) {
1768 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1769 var node = group[i];
1770 if (node) return node;
1771 }
1772 }
1773 return null;
1774 };
1775 d3_selectionPrototype.size = function() {
1776 var n = 0;
1777 d3_selection_each(this, function() {
1778 ++n;
1779 });
1780 return n;
1781 };
1782 function d3_selection_enter(selection) {
1783 d3_subclass(selection, d3_selection_enterPrototype);
1784 return selection;
1785 }
1786 var d3_selection_enterPrototype = [];
1787 d3.selection.enter = d3_selection_enter;
1788 d3.selection.enter.prototype = d3_selection_enterPrototype;
1789 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1790 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1791 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1792 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1793 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1794 d3_selection_enterPrototype.select = function(selector) {
1795 var subgroups = [], subgroup, subnode, upgroup, group, node;
1796 for (var j = -1, m = this.length; ++j < m; ) {
1797 upgroup = (group = this[j]).update;
1798 subgroups.push(subgroup = []);
1799 subgroup.parentNode = group.parentNode;
1800 for (var i = -1, n = group.length; ++i < n; ) {
1801 if (node = group[i]) {
1802 subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1803 subnode.__data__ = node.__data__;
1804 } else {
1805 subgroup.push(null);
1806 }
1807 }
1808 }
1809 return d3_selection(subgroups);
1810 };
1811 d3_selection_enterPrototype.insert = function(name, before) {
1812 if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1813 return d3_selectionPrototype.insert.call(this, name, before);
1814 };
1815 function d3_selection_enterInsertBefore(enter) {
1816 var i0, j0;
1817 return function(d, i, j) {
1818 var group = enter[j].update, n = group.length, node;
1819 if (j != j0) j0 = j, i0 = 0;
1820 if (i >= i0) i0 = i + 1;
1821 while (!(node = group[i0]) && ++i0 < n) ;
1822 return node;
1823 };
1824 }
1825 d3.select = function(node) {
1826 var group;
1827 if (typeof node === "string") {
1828 group = [ d3_select(node, d3_document) ];
1829 group.parentNode = d3_document.documentElement;
1830 } else {
1831 group = [ node ];
1832 group.parentNode = d3_documentElement(node);
1833 }
1834 return d3_selection([ group ]);
1835 };
1836 d3.selectAll = function(nodes) {
1837 var group;
1838 if (typeof nodes === "string") {
1839 group = d3_array(d3_selectAll(nodes, d3_document));
1840 group.parentNode = d3_document.documentElement;
1841 } else {
1842 group = d3_array(nodes);
1843 group.parentNode = null;
1844 }
1845 return d3_selection([ group ]);
1846 };
1847 d3_selectionPrototype.on = function(type, listener, capture) {
1848 var n = arguments.length;
1849 if (n < 3) {
1850 if (typeof type !== "string") {
1851 if (n < 2) listener = false;
1852 for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1853 return this;
1854 }
1855 if (n < 2) return (n = this.node()["__on" + type]) && n._;
1856 capture = false;
1857 }
1858 return this.each(d3_selection_on(type, listener, capture));
1859 };
1860 function d3_selection_on(type, listener, capture) {
1861 var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
1862 if (i > 0) type = type.slice(0, i);
1863 var filter = d3_selection_onFilters.get(type);
1864 if (filter) type = filter, wrap = d3_selection_onFilter;
1865 function onRemove() {
1866 var l = this[name];
1867 if (l) {
1868 this.removeEventListener(type, l, l.$);
1869 delete this[name];
1870 }
1871 }
1872 function onAdd() {
1873 var l = wrap(listener, d3_array(arguments));
1874 onRemove.call(this);
1875 this.addEventListener(type, this[name] = l, l.$ = capture);
1876 l._ = listener;
1877 }
1878 function removeAll() {
1879 var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
1880 for (var name in this) {
1881 if (match = name.match(re)) {
1882 var l = this[name];
1883 this.removeEventListener(match[1], l, l.$);
1884 delete this[name];
1885 }
1886 }
1887 }
1888 return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
1889 }
1890 var d3_selection_onFilters = d3.map({
1891 mouseenter: "mouseover",
1892 mouseleave: "mouseout"
1893 });
1894 if (d3_document) {
1895 d3_selection_onFilters.forEach(function(k) {
1896 if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1897 });
1898 }
1899 function d3_selection_onListener(listener, argumentz) {
1900 return function(e) {
1901 var o = d3.event;
1902 d3.event = e;
1903 argumentz[0] = this.__data__;
1904 try {
1905 listener.apply(this, argumentz);
1906 } finally {
1907 d3.event = o;
1908 }
1909 };
1910 }
1911 function d3_selection_onFilter(listener, argumentz) {
1912 var l = d3_selection_onListener(listener, argumentz);
1913 return function(e) {
1914 var target = this, related = e.relatedTarget;
1915 if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
1916 l.call(target, e);
1917 }
1918 };
1919 }
1920 var d3_event_dragSelect, d3_event_dragId = 0;
1921 function d3_event_dragSuppress(node) {
1922 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);
1923 if (d3_event_dragSelect == null) {
1924 d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect");
1925 }
1926 if (d3_event_dragSelect) {
1927 var style = d3_documentElement(node).style, select = style[d3_event_dragSelect];
1928 style[d3_event_dragSelect] = "none";
1929 }
1930 return function(suppressClick) {
1931 w.on(name, null);
1932 if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1933 if (suppressClick) {
1934 var off = function() {
1935 w.on(click, null);
1936 };
1937 w.on(click, function() {
1938 d3_eventPreventDefault();
1939 off();
1940 }, true);
1941 setTimeout(off, 0);
1942 }
1943 };
1944 }
1945 d3.mouse = function(container) {
1946 return d3_mousePoint(container, d3_eventSource());
1947 };
1948 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1949 function d3_mousePoint(container, e) {
1950 if (e.changedTouches) e = e.changedTouches[0];
1951 var svg = container.ownerSVGElement || container;
1952 if (svg.createSVGPoint) {
1953 var point = svg.createSVGPoint();
1954 if (d3_mouse_bug44083 < 0) {
1955 var window = d3_window(container);
1956 if (window.scrollX || window.scrollY) {
1957 svg = d3.select("body").append("svg").style({
1958 position: "absolute",
1959 top: 0,
1960 left: 0,
1961 margin: 0,
1962 padding: 0,
1963 border: "none"
1964 }, "important");
1965 var ctm = svg[0][0].getScreenCTM();
1966 d3_mouse_bug44083 = !(ctm.f || ctm.e);
1967 svg.remove();
1968 }
1969 }
1970 if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
1971 point.y = e.clientY;
1972 point = point.matrixTransform(container.getScreenCTM().inverse());
1973 return [ point.x, point.y ];
1974 }
1975 var rect = container.getBoundingClientRect();
1976 return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
1977 }
1978 d3.touch = function(container, touches, identifier) {
1979 if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
1980 if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
1981 if ((touch = touches[i]).identifier === identifier) {
1982 return d3_mousePoint(container, touch);
1983 }
1984 }
1985 };
1986 d3.behavior.drag = function() {
1987 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");
1988 function drag() {
1989 this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
1990 }
1991 function dragstart(id, position, subject, move, end) {
1992 return function() {
1993 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);
1994 if (origin) {
1995 dragOffset = origin.apply(that, arguments);
1996 dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
1997 } else {
1998 dragOffset = [ 0, 0 ];
1999 }
2000 dispatch({
2001 type: "dragstart"
2002 });
2003 function moved() {
2004 var position1 = position(parent, dragId), dx, dy;
2005 if (!position1) return;
2006 dx = position1[0] - position0[0];
2007 dy = position1[1] - position0[1];
2008 dragged |= dx | dy;
2009 position0 = position1;
2010 dispatch({
2011 type: "drag",
2012 x: position1[0] + dragOffset[0],
2013 y: position1[1] + dragOffset[1],
2014 dx: dx,
2015 dy: dy
2016 });
2017 }
2018 function ended() {
2019 if (!position(parent, dragId)) return;
2020 dragSubject.on(move + dragName, null).on(end + dragName, null);
2021 dragRestore(dragged);
2022 dispatch({
2023 type: "dragend"
2024 });
2025 }
2026 };
2027 }
2028 drag.origin = function(x) {
2029 if (!arguments.length) return origin;
2030 origin = x;
2031 return drag;
2032 };
2033 return d3.rebind(drag, event, "on");
2034 };
2035 function d3_behavior_dragTouchId() {
2036 return d3.event.changedTouches[0].identifier;
2037 }
2038 d3.touches = function(container, touches) {
2039 if (arguments.length < 2) touches = d3_eventSource().touches;
2040 return touches ? d3_array(touches).map(function(touch) {
2041 var point = d3_mousePoint(container, touch);
2042 point.identifier = touch.identifier;
2043 return point;
2044 }) : [];
2045 };
2046 var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
2047 function d3_sgn(x) {
2048 return x > 0 ? 1 : x < 0 ? -1 : 0;
2049 }
2050 function d3_cross2d(a, b, c) {
2051 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
2052 }
2053 function d3_acos(x) {
2054 return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
2055 }
2056 function d3_asin(x) {
2057 return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
2058 }
2059 function d3_sinh(x) {
2060 return ((x = Math.exp(x)) - 1 / x) / 2;
2061 }
2062 function d3_cosh(x) {
2063 return ((x = Math.exp(x)) + 1 / x) / 2;
2064 }
2065 function d3_tanh(x) {
2066 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
2067 }
2068 function d3_haversin(x) {
2069 return (x = Math.sin(x / 2)) * x;
2070 }
2071 var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
2072 d3.interpolateZoom = function(p0, p1) {
2073 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;
2074 if (d2 < ε2) {
2075 S = Math.log(w1 / w0) / ρ;
2076 i = function(t) {
2077 return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ];
2078 };
2079 } else {
2080 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);
2081 S = (r1 - r0) / ρ;
2082 i = function(t) {
2083 var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
2084 return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
2085 };
2086 }
2087 i.duration = S * 1e3;
2088 return i;
2089 };
2090 d3.behavior.zoom = function() {
2091 var view = {
2092 x: 0,
2093 y: 0,
2094 k: 1
2095 }, 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;
2096 if (!d3_behavior_zoomWheel) {
2097 d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
2098 return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
2099 }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
2100 return d3.event.wheelDelta;
2101 }, "mousewheel") : (d3_behavior_zoomDelta = function() {
2102 return -d3.event.detail;
2103 }, "MozMousePixelScroll");
2104 }
2105 function zoom(g) {
2106 g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
2107 }
2108 zoom.event = function(g) {
2109 g.each(function() {
2110 var dispatch = event.of(this, arguments), view1 = view;
2111 if (d3_transitionInheritId) {
2112 d3.select(this).transition().each("start.zoom", function() {
2113 view = this.__chart__ || {
2114 x: 0,
2115 y: 0,
2116 k: 1
2117 };
2118 zoomstarted(dispatch);
2119 }).tween("zoom:zoom", function() {
2120 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 ]);
2121 return function(t) {
2122 var l = i(t), k = dx / l[2];
2123 this.__chart__ = view = {
2124 x: cx - l[0] * k,
2125 y: cy - l[1] * k,
2126 k: k
2127 };
2128 zoomed(dispatch);
2129 };
2130 }).each("interrupt.zoom", function() {
2131 zoomended(dispatch);
2132 }).each("end.zoom", function() {
2133 zoomended(dispatch);
2134 });
2135 } else {
2136 this.__chart__ = view;
2137 zoomstarted(dispatch);
2138 zoomed(dispatch);
2139 zoomended(dispatch);
2140 }
2141 });
2142 };
2143 zoom.translate = function(_) {
2144 if (!arguments.length) return [ view.x, view.y ];
2145 view = {
2146 x: +_[0],
2147 y: +_[1],
2148 k: view.k
2149 };
2150 rescale();
2151 return zoom;
2152 };
2153 zoom.scale = function(_) {
2154 if (!arguments.length) return view.k;
2155 view = {
2156 x: view.x,
2157 y: view.y,
2158 k: null
2159 };
2160 scaleTo(+_);
2161 rescale();
2162 return zoom;
2163 };
2164 zoom.scaleExtent = function(_) {
2165 if (!arguments.length) return scaleExtent;
2166 scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
2167 return zoom;
2168 };
2169 zoom.center = function(_) {
2170 if (!arguments.length) return center;
2171 center = _ && [ +_[0], +_[1] ];
2172 return zoom;
2173 };
2174 zoom.size = function(_) {
2175 if (!arguments.length) return size;
2176 size = _ && [ +_[0], +_[1] ];
2177 return zoom;
2178 };
2179 zoom.duration = function(_) {
2180 if (!arguments.length) return duration;
2181 duration = +_;
2182 return zoom;
2183 };
2184 zoom.x = function(z) {
2185 if (!arguments.length) return x1;
2186 x1 = z;
2187 x0 = z.copy();
2188 view = {
2189 x: 0,
2190 y: 0,
2191 k: 1
2192 };
2193 return zoom;
2194 };
2195 zoom.y = function(z) {
2196 if (!arguments.length) return y1;
2197 y1 = z;
2198 y0 = z.copy();
2199 view = {
2200 x: 0,
2201 y: 0,
2202 k: 1
2203 };
2204 return zoom;
2205 };
2206 function location(p) {
2207 return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
2208 }
2209 function point(l) {
2210 return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
2211 }
2212 function scaleTo(s) {
2213 view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
2214 }
2215 function translateTo(p, l) {
2216 l = point(l);
2217 view.x += p[0] - l[0];
2218 view.y += p[1] - l[1];
2219 }
2220 function zoomTo(that, p, l, k) {
2221 that.__chart__ = {
2222 x: view.x,
2223 y: view.y,
2224 k: view.k
2225 };
2226 scaleTo(Math.pow(2, k));
2227 translateTo(center0 = p, l);
2228 that = d3.select(that);
2229 if (duration > 0) that = that.transition().duration(duration);
2230 that.call(zoom.event);
2231 }
2232 function rescale() {
2233 if (x1) x1.domain(x0.range().map(function(x) {
2234 return (x - view.x) / view.k;
2235 }).map(x0.invert));
2236 if (y1) y1.domain(y0.range().map(function(y) {
2237 return (y - view.y) / view.k;
2238 }).map(y0.invert));
2239 }
2240 function zoomstarted(dispatch) {
2241 if (!zooming++) dispatch({
2242 type: "zoomstart"
2243 });
2244 }
2245 function zoomed(dispatch) {
2246 rescale();
2247 dispatch({
2248 type: "zoom",
2249 scale: view.k,
2250 translate: [ view.x, view.y ]
2251 });
2252 }
2253 function zoomended(dispatch) {
2254 if (!--zooming) dispatch({
2255 type: "zoomend"
2256 }), center0 = null;
2257 }
2258 function mousedowned() {
2259 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);
2260 d3_selection_interrupt.call(that);
2261 zoomstarted(dispatch);
2262 function moved() {
2263 dragged = 1;
2264 translateTo(d3.mouse(that), location0);
2265 zoomed(dispatch);
2266 }
2267 function ended() {
2268 subject.on(mousemove, null).on(mouseup, null);
2269 dragRestore(dragged);
2270 zoomended(dispatch);
2271 }
2272 }
2273 function touchstarted() {
2274 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);
2275 started();
2276 zoomstarted(dispatch);
2277 subject.on(mousedown, null).on(touchstart, started);
2278 function relocate() {
2279 var touches = d3.touches(that);
2280 scale0 = view.k;
2281 touches.forEach(function(t) {
2282 if (t.identifier in locations0) locations0[t.identifier] = location(t);
2283 });
2284 return touches;
2285 }
2286 function started() {
2287 var target = d3.event.target;
2288 d3.select(target).on(touchmove, moved).on(touchend, ended);
2289 targets.push(target);
2290 var changed = d3.event.changedTouches;
2291 for (var i = 0, n = changed.length; i < n; ++i) {
2292 locations0[changed[i].identifier] = null;
2293 }
2294 var touches = relocate(), now = Date.now();
2295 if (touches.length === 1) {
2296 if (now - touchtime < 500) {
2297 var p = touches[0];
2298 zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2299 d3_eventPreventDefault();
2300 }
2301 touchtime = now;
2302 } else if (touches.length > 1) {
2303 var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
2304 distance0 = dx * dx + dy * dy;
2305 }
2306 }
2307 function moved() {
2308 var touches = d3.touches(that), p0, l0, p1, l1;
2309 d3_selection_interrupt.call(that);
2310 for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2311 p1 = touches[i];
2312 if (l1 = locations0[p1.identifier]) {
2313 if (l0) break;
2314 p0 = p1, l0 = l1;
2315 }
2316 }
2317 if (l1) {
2318 var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
2319 p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
2320 l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
2321 scaleTo(scale1 * scale0);
2322 }
2323 touchtime = null;
2324 translateTo(p0, l0);
2325 zoomed(dispatch);
2326 }
2327 function ended() {
2328 if (d3.event.touches.length) {
2329 var changed = d3.event.changedTouches;
2330 for (var i = 0, n = changed.length; i < n; ++i) {
2331 delete locations0[changed[i].identifier];
2332 }
2333 for (var identifier in locations0) {
2334 return void relocate();
2335 }
2336 }
2337 d3.selectAll(targets).on(zoomName, null);
2338 subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2339 dragRestore();
2340 zoomended(dispatch);
2341 }
2342 }
2343 function mousewheeled() {
2344 var dispatch = event.of(this, arguments);
2345 if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
2346 translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
2347 mousewheelTimer = setTimeout(function() {
2348 mousewheelTimer = null;
2349 zoomended(dispatch);
2350 }, 50);
2351 d3_eventPreventDefault();
2352 scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2353 translateTo(center0, translate0);
2354 zoomed(dispatch);
2355 }
2356 function dblclicked() {
2357 var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
2358 zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2359 }
2360 return d3.rebind(zoom, event, "on");
2361 };
2362 var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel;
2363 d3.color = d3_color;
2364 function d3_color() {}
2365 d3_color.prototype.toString = function() {
2366 return this.rgb() + "";
2367 };
2368 d3.hsl = d3_hsl;
2369 function d3_hsl(h, s, l) {
2370 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);
2371 }
2372 var d3_hslPrototype = d3_hsl.prototype = new d3_color();
2373 d3_hslPrototype.brighter = function(k) {
2374 k = Math.pow(.7, arguments.length ? k : 1);
2375 return new d3_hsl(this.h, this.s, this.l / k);
2376 };
2377 d3_hslPrototype.darker = function(k) {
2378 k = Math.pow(.7, arguments.length ? k : 1);
2379 return new d3_hsl(this.h, this.s, k * this.l);
2380 };
2381 d3_hslPrototype.rgb = function() {
2382 return d3_hsl_rgb(this.h, this.s, this.l);
2383 };
2384 function d3_hsl_rgb(h, s, l) {
2385 var m1, m2;
2386 h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
2387 s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
2388 l = l < 0 ? 0 : l > 1 ? 1 : l;
2389 m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
2390 m1 = 2 * l - m2;
2391 function v(h) {
2392 if (h > 360) h -= 360; else if (h < 0) h += 360;
2393 if (h < 60) return m1 + (m2 - m1) * h / 60;
2394 if (h < 180) return m2;
2395 if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
2396 return m1;
2397 }
2398 function vv(h) {
2399 return Math.round(v(h) * 255);
2400 }
2401 return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
2402 }
2403 d3.hcl = d3_hcl;
2404 function d3_hcl(h, c, l) {
2405 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);
2406 }
2407 var d3_hclPrototype = d3_hcl.prototype = new d3_color();
2408 d3_hclPrototype.brighter = function(k) {
2409 return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
2410 };
2411 d3_hclPrototype.darker = function(k) {
2412 return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
2413 };
2414 d3_hclPrototype.rgb = function() {
2415 return d3_hcl_lab(this.h, this.c, this.l).rgb();
2416 };
2417 function d3_hcl_lab(h, c, l) {
2418 if (isNaN(h)) h = 0;
2419 if (isNaN(c)) c = 0;
2420 return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
2421 }
2422 d3.lab = d3_lab;
2423 function d3_lab(l, a, b) {
2424 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);
2425 }
2426 var d3_lab_K = 18;
2427 var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
2428 var d3_labPrototype = d3_lab.prototype = new d3_color();
2429 d3_labPrototype.brighter = function(k) {
2430 return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
2431 };
2432 d3_labPrototype.darker = function(k) {
2433 return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
2434 };
2435 d3_labPrototype.rgb = function() {
2436 return d3_lab_rgb(this.l, this.a, this.b);
2437 };
2438 function d3_lab_rgb(l, a, b) {
2439 var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
2440 x = d3_lab_xyz(x) * d3_lab_X;
2441 y = d3_lab_xyz(y) * d3_lab_Y;
2442 z = d3_lab_xyz(z) * d3_lab_Z;
2443 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));
2444 }
2445 function d3_lab_hcl(l, a, b) {
2446 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);
2447 }
2448 function d3_lab_xyz(x) {
2449 return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
2450 }
2451 function d3_xyz_lab(x) {
2452 return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
2453 }
2454 function d3_xyz_rgb(r) {
2455 return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
2456 }
2457 d3.rgb = d3_rgb;
2458 function d3_rgb(r, g, b) {
2459 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);
2460 }
2461 function d3_rgbNumber(value) {
2462 return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
2463 }
2464 function d3_rgbString(value) {
2465 return d3_rgbNumber(value) + "";
2466 }
2467 var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
2468 d3_rgbPrototype.brighter = function(k) {
2469 k = Math.pow(.7, arguments.length ? k : 1);
2470 var r = this.r, g = this.g, b = this.b, i = 30;
2471 if (!r && !g && !b) return new d3_rgb(i, i, i);
2472 if (r && r < i) r = i;
2473 if (g && g < i) g = i;
2474 if (b && b < i) b = i;
2475 return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
2476 };
2477 d3_rgbPrototype.darker = function(k) {
2478 k = Math.pow(.7, arguments.length ? k : 1);
2479 return new d3_rgb(k * this.r, k * this.g, k * this.b);
2480 };
2481 d3_rgbPrototype.hsl = function() {
2482 return d3_rgb_hsl(this.r, this.g, this.b);
2483 };
2484 d3_rgbPrototype.toString = function() {
2485 return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
2486 };
2487 function d3_rgb_hex(v) {
2488 return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
2489 }
2490 function d3_rgb_parse(format, rgb, hsl) {
2491 var r = 0, g = 0, b = 0, m1, m2, color;
2492 m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase());
2493 if (m1) {
2494 m2 = m1[2].split(",");
2495 switch (m1[1]) {
2496 case "hsl":
2497 {
2498 return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
2499 }
2500
2501 case "rgb":
2502 {
2503 return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
2504 }
2505 }
2506 }
2507 if (color = d3_rgb_names.get(format)) {
2508 return rgb(color.r, color.g, color.b);
2509 }
2510 if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
2511 if (format.length === 4) {
2512 r = (color & 3840) >> 4;
2513 r = r >> 4 | r;
2514 g = color & 240;
2515 g = g >> 4 | g;
2516 b = color & 15;
2517 b = b << 4 | b;
2518 } else if (format.length === 7) {
2519 r = (color & 16711680) >> 16;
2520 g = (color & 65280) >> 8;
2521 b = color & 255;
2522 }
2523 }
2524 return rgb(r, g, b);
2525 }
2526 function d3_rgb_hsl(r, g, b) {
2527 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;
2528 if (d) {
2529 s = l < .5 ? d / (max + min) : d / (2 - max - min);
2530 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;
2531 h *= 60;
2532 } else {
2533 h = NaN;
2534 s = l > 0 && l < 1 ? 0 : h;
2535 }
2536 return new d3_hsl(h, s, l);
2537 }
2538 function d3_rgb_lab(r, g, b) {
2539 r = d3_rgb_xyz(r);
2540 g = d3_rgb_xyz(g);
2541 b = d3_rgb_xyz(b);
2542 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);
2543 return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
2544 }
2545 function d3_rgb_xyz(r) {
2546 return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
2547 }
2548 function d3_rgb_parseNumber(c) {
2549 var f = parseFloat(c);
2550 return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
2551 }
2552 var d3_rgb_names = d3.map({
2553 aliceblue: 15792383,
2554 antiquewhite: 16444375,
2555 aqua: 65535,
2556 aquamarine: 8388564,
2557 azure: 15794175,
2558 beige: 16119260,
2559 bisque: 16770244,
2560 black: 0,
2561 blanchedalmond: 16772045,
2562 blue: 255,
2563 blueviolet: 9055202,
2564 brown: 10824234,
2565 burlywood: 14596231,
2566 cadetblue: 6266528,
2567 chartreuse: 8388352,
2568 chocolate: 13789470,
2569 coral: 16744272,
2570 cornflowerblue: 6591981,
2571 cornsilk: 16775388,
2572 crimson: 14423100,
2573 cyan: 65535,
2574 darkblue: 139,
2575 darkcyan: 35723,
2576 darkgoldenrod: 12092939,
2577 darkgray: 11119017,
2578 darkgreen: 25600,
2579 darkgrey: 11119017,
2580 darkkhaki: 12433259,
2581 darkmagenta: 9109643,
2582 darkolivegreen: 5597999,
2583 darkorange: 16747520,
2584 darkorchid: 10040012,
2585 darkred: 9109504,
2586 darksalmon: 15308410,
2587 darkseagreen: 9419919,
2588 darkslateblue: 4734347,
2589 darkslategray: 3100495,
2590 darkslategrey: 3100495,
2591 darkturquoise: 52945,
2592 darkviolet: 9699539,
2593 deeppink: 16716947,
2594 deepskyblue: 49151,
2595 dimgray: 6908265,
2596 dimgrey: 6908265,
2597 dodgerblue: 2003199,
2598 firebrick: 11674146,
2599 floralwhite: 16775920,
2600 forestgreen: 2263842,
2601 fuchsia: 16711935,
2602 gainsboro: 14474460,
2603 ghostwhite: 16316671,
2604 gold: 16766720,
2605 goldenrod: 14329120,
2606 gray: 8421504,
2607 green: 32768,
2608 greenyellow: 11403055,
2609 grey: 8421504,
2610 honeydew: 15794160,
2611 hotpink: 16738740,
2612 indianred: 13458524,
2613 indigo: 4915330,
2614 ivory: 16777200,
2615 khaki: 15787660,
2616 lavender: 15132410,
2617 lavenderblush: 16773365,
2618 lawngreen: 8190976,
2619 lemonchiffon: 16775885,
2620 lightblue: 11393254,
2621 lightcoral: 15761536,
2622 lightcyan: 14745599,
2623 lightgoldenrodyellow: 16448210,
2624 lightgray: 13882323,
2625 lightgreen: 9498256,
2626 lightgrey: 13882323,
2627 lightpink: 16758465,
2628 lightsalmon: 16752762,
2629 lightseagreen: 2142890,
2630 lightskyblue: 8900346,
2631 lightslategray: 7833753,
2632 lightslategrey: 7833753,
2633 lightsteelblue: 11584734,
2634 lightyellow: 16777184,
2635 lime: 65280,
2636 limegreen: 3329330,
2637 linen: 16445670,
2638 magenta: 16711935,
2639 maroon: 8388608,
2640 mediumaquamarine: 6737322,
2641 mediumblue: 205,
2642 mediumorchid: 12211667,
2643 mediumpurple: 9662683,
2644 mediumseagreen: 3978097,
2645 mediumslateblue: 8087790,
2646 mediumspringgreen: 64154,
2647 mediumturquoise: 4772300,
2648 mediumvioletred: 13047173,
2649 midnightblue: 1644912,
2650 mintcream: 16121850,
2651 mistyrose: 16770273,
2652 moccasin: 16770229,
2653 navajowhite: 16768685,
2654 navy: 128,
2655 oldlace: 16643558,
2656 olive: 8421376,
2657 olivedrab: 7048739,
2658 orange: 16753920,
2659 orangered: 16729344,
2660 orchid: 14315734,
2661 palegoldenrod: 15657130,
2662 palegreen: 10025880,
2663 paleturquoise: 11529966,
2664 palevioletred: 14381203,
2665 papayawhip: 16773077,
2666 peachpuff: 16767673,
2667 peru: 13468991,
2668 pink: 16761035,
2669 plum: 14524637,
2670 powderblue: 11591910,
2671 purple: 8388736,
2672 rebeccapurple: 6697881,
2673 red: 16711680,
2674 rosybrown: 12357519,
2675 royalblue: 4286945,
2676 saddlebrown: 9127187,
2677 salmon: 16416882,
2678 sandybrown: 16032864,
2679 seagreen: 3050327,
2680 seashell: 16774638,
2681 sienna: 10506797,
2682 silver: 12632256,
2683 skyblue: 8900331,
2684 slateblue: 6970061,
2685 slategray: 7372944,
2686 slategrey: 7372944,
2687 snow: 16775930,
2688 springgreen: 65407,
2689 steelblue: 4620980,
2690 tan: 13808780,
2691 teal: 32896,
2692 thistle: 14204888,
2693 tomato: 16737095,
2694 turquoise: 4251856,
2695 violet: 15631086,
2696 wheat: 16113331,
2697 white: 16777215,
2698 whitesmoke: 16119285,
2699 yellow: 16776960,
2700 yellowgreen: 10145074
2701 });
2702 d3_rgb_names.forEach(function(key, value) {
2703 d3_rgb_names.set(key, d3_rgbNumber(value));
2704 });
2705 function d3_functor(v) {
2706 return typeof v === "function" ? v : function() {
2707 return v;
2708 };
2709 }
2710 d3.functor = d3_functor;
2711 d3.xhr = d3_xhrType(d3_identity);
2712 function d3_xhrType(response) {
2713 return function(url, mimeType, callback) {
2714 if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
2715 mimeType = null;
2716 return d3_xhr(url, mimeType, response, callback);
2717 };
2718 }
2719 function d3_xhr(url, mimeType, response, callback) {
2720 var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
2721 if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
2722 "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
2723 request.readyState > 3 && respond();
2724 };
2725 function respond() {
2726 var status = request.status, result;
2727 if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
2728 try {
2729 result = response.call(xhr, request);
2730 } catch (e) {
2731 dispatch.error.call(xhr, e);
2732 return;
2733 }
2734 dispatch.load.call(xhr, result);
2735 } else {
2736 dispatch.error.call(xhr, request);
2737 }
2738 }
2739 request.onprogress = function(event) {
2740 var o = d3.event;
2741 d3.event = event;
2742 try {
2743 dispatch.progress.call(xhr, request);
2744 } finally {
2745 d3.event = o;
2746 }
2747 };
2748 xhr.header = function(name, value) {
2749 name = (name + "").toLowerCase();
2750 if (arguments.length < 2) return headers[name];
2751 if (value == null) delete headers[name]; else headers[name] = value + "";
2752 return xhr;
2753 };
2754 xhr.mimeType = function(value) {
2755 if (!arguments.length) return mimeType;
2756 mimeType = value == null ? null : value + "";
2757 return xhr;
2758 };
2759 xhr.responseType = function(value) {
2760 if (!arguments.length) return responseType;
2761 responseType = value;
2762 return xhr;
2763 };
2764 xhr.response = function(value) {
2765 response = value;
2766 return xhr;
2767 };
2768 [ "get", "post" ].forEach(function(method) {
2769 xhr[method] = function() {
2770 return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
2771 };
2772 });
2773 xhr.send = function(method, data, callback) {
2774 if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
2775 request.open(method, url, true);
2776 if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
2777 if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
2778 if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
2779 if (responseType != null) request.responseType = responseType;
2780 if (callback != null) xhr.on("error", callback).on("load", function(request) {
2781 callback(null, request);
2782 });
2783 dispatch.beforesend.call(xhr, request);
2784 request.send(data == null ? null : data);
2785 return xhr;
2786 };
2787 xhr.abort = function() {
2788 request.abort();
2789 return xhr;
2790 };
2791 d3.rebind(xhr, dispatch, "on");
2792 return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
2793 }
2794 function d3_xhr_fixCallback(callback) {
2795 return callback.length === 1 ? function(error, request) {
2796 callback(error == null ? request : null);
2797 } : callback;
2798 }
2799 function d3_xhrHasResponse(request) {
2800 var type = request.responseType;
2801 return type && type !== "text" ? request.response : request.responseText;
2802 }
2803 d3.dsv = function(delimiter, mimeType) {
2804 var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
2805 function dsv(url, row, callback) {
2806 if (arguments.length < 3) callback = row, row = null;
2807 var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
2808 xhr.row = function(_) {
2809 return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
2810 };
2811 return xhr;
2812 }
2813 function response(request) {
2814 return dsv.parse(request.responseText);
2815 }
2816 function typedResponse(f) {
2817 return function(request) {
2818 return dsv.parse(request.responseText, f);
2819 };
2820 }
2821 dsv.parse = function(text, f) {
2822 var o;
2823 return dsv.parseRows(text, function(row, i) {
2824 if (o) return o(row, i - 1);
2825 var a = new Function("d", "return {" + row.map(function(name, i) {
2826 return JSON.stringify(name) + ": d[" + i + "]";
2827 }).join(",") + "}");
2828 o = f ? function(row, i) {
2829 return f(a(row), i);
2830 } : a;
2831 });
2832 };
2833 dsv.parseRows = function(text, f) {
2834 var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
2835 function token() {
2836 if (I >= N) return EOF;
2837 if (eol) return eol = false, EOL;
2838 var j = I;
2839 if (text.charCodeAt(j) === 34) {
2840 var i = j;
2841 while (i++ < N) {
2842 if (text.charCodeAt(i) === 34) {
2843 if (text.charCodeAt(i + 1) !== 34) break;
2844 ++i;
2845 }
2846 }
2847 I = i + 2;
2848 var c = text.charCodeAt(i + 1);
2849 if (c === 13) {
2850 eol = true;
2851 if (text.charCodeAt(i + 2) === 10) ++I;
2852 } else if (c === 10) {
2853 eol = true;
2854 }
2855 return text.slice(j + 1, i).replace(/""/g, '"');
2856 }
2857 while (I < N) {
2858 var c = text.charCodeAt(I++), k = 1;
2859 if (c === 10) eol = true; else if (c === 13) {
2860 eol = true;
2861 if (text.charCodeAt(I) === 10) ++I, ++k;
2862 } else if (c !== delimiterCode) continue;
2863 return text.slice(j, I - k);
2864 }
2865 return text.slice(j);
2866 }
2867 while ((t = token()) !== EOF) {
2868 var a = [];
2869 while (t !== EOL && t !== EOF) {
2870 a.push(t);
2871 t = token();
2872 }
2873 if (f && (a = f(a, n++)) == null) continue;
2874 rows.push(a);
2875 }
2876 return rows;
2877 };
2878 dsv.format = function(rows) {
2879 if (Array.isArray(rows[0])) return dsv.formatRows(rows);
2880 var fieldSet = new d3_Set(), fields = [];
2881 rows.forEach(function(row) {
2882 for (var field in row) {
2883 if (!fieldSet.has(field)) {
2884 fields.push(fieldSet.add(field));
2885 }
2886 }
2887 });
2888 return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
2889 return fields.map(function(field) {
2890 return formatValue(row[field]);
2891 }).join(delimiter);
2892 })).join("\n");
2893 };
2894 dsv.formatRows = function(rows) {
2895 return rows.map(formatRow).join("\n");
2896 };
2897 function formatRow(row) {
2898 return row.map(formatValue).join(delimiter);
2899 }
2900 function formatValue(text) {
2901 return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
2902 }
2903 return dsv;
2904 };
2905 d3.csv = d3.dsv(",", "text/csv");
2906 d3.tsv = d3.dsv(" ", "text/tab-separated-values");
2907 var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) {
2908 setTimeout(callback, 17);
2909 };
2910 d3.timer = function() {
2911 d3_timer.apply(this, arguments);
2912 };
2913 function d3_timer(callback, delay, then) {
2914 var n = arguments.length;
2915 if (n < 2) delay = 0;
2916 if (n < 3) then = Date.now();
2917 var time = then + delay, timer = {
2918 c: callback,
2919 t: time,
2920 n: null
2921 };
2922 if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
2923 d3_timer_queueTail = timer;
2924 if (!d3_timer_interval) {
2925 d3_timer_timeout = clearTimeout(d3_timer_timeout);
2926 d3_timer_interval = 1;
2927 d3_timer_frame(d3_timer_step);
2928 }
2929 return timer;
2930 }
2931 function d3_timer_step() {
2932 var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
2933 if (delay > 24) {
2934 if (isFinite(delay)) {
2935 clearTimeout(d3_timer_timeout);
2936 d3_timer_timeout = setTimeout(d3_timer_step, delay);
2937 }
2938 d3_timer_interval = 0;
2939 } else {
2940 d3_timer_interval = 1;
2941 d3_timer_frame(d3_timer_step);
2942 }
2943 }
2944 d3.timer.flush = function() {
2945 d3_timer_mark();
2946 d3_timer_sweep();
2947 };
2948 function d3_timer_mark() {
2949 var now = Date.now(), timer = d3_timer_queueHead;
2950 while (timer) {
2951 if (now >= timer.t && timer.c(now - timer.t)) timer.c = null;
2952 timer = timer.n;
2953 }
2954 return now;
2955 }
2956 function d3_timer_sweep() {
2957 var t0, t1 = d3_timer_queueHead, time = Infinity;
2958 while (t1) {
2959 if (t1.c) {
2960 if (t1.t < time) time = t1.t;
2961 t1 = (t0 = t1).n;
2962 } else {
2963 t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2964 }
2965 }
2966 d3_timer_queueTail = t0;
2967 return time;
2968 }
2969 function d3_format_precision(x, p) {
2970 return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
2971 }
2972 d3.round = function(x, n) {
2973 return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
2974 };
2975 var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
2976 d3.formatPrefix = function(value, precision) {
2977 var i = 0;
2978 if (value = +value) {
2979 if (value < 0) value *= -1;
2980 if (precision) value = d3.round(value, d3_format_precision(value, precision));
2981 i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
2982 i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
2983 }
2984 return d3_formatPrefixes[8 + i / 3];
2985 };
2986 function d3_formatPrefix(d, i) {
2987 var k = Math.pow(10, abs(8 - i) * 3);
2988 return {
2989 scale: i > 8 ? function(d) {
2990 return d / k;
2991 } : function(d) {
2992 return d * k;
2993 },
2994 symbol: d
2995 };
2996 }
2997 function d3_locale_numberFormat(locale) {
2998 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) {
2999 var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0;
3000 while (i > 0 && g > 0) {
3001 if (length + g + 1 > width) g = Math.max(1, width - length);
3002 t.push(value.substring(i -= g, i + g));
3003 if ((length += g + 1) > width) break;
3004 g = locale_grouping[j = (j + 1) % locale_grouping.length];
3005 }
3006 return t.reverse().join(locale_thousands);
3007 } : d3_identity;
3008 return function(specifier) {
3009 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;
3010 if (precision) precision = +precision.substring(1);
3011 if (zfill || fill === "0" && align === "=") {
3012 zfill = fill = "0";
3013 align = "=";
3014 }
3015 switch (type) {
3016 case "n":
3017 comma = true;
3018 type = "g";
3019 break;
3020
3021 case "%":
3022 scale = 100;
3023 suffix = "%";
3024 type = "f";
3025 break;
3026
3027 case "p":
3028 scale = 100;
3029 suffix = "%";
3030 type = "r";
3031 break;
3032
3033 case "b":
3034 case "o":
3035 case "x":
3036 case "X":
3037 if (symbol === "#") prefix = "0" + type.toLowerCase();
3038
3039 case "c":
3040 exponent = false;
3041
3042 case "d":
3043 integer = true;
3044 precision = 0;
3045 break;
3046
3047 case "s":
3048 scale = -1;
3049 type = "r";
3050 break;
3051 }
3052 if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
3053 if (type == "r" && !precision) type = "g";
3054 if (precision != null) {
3055 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));
3056 }
3057 type = d3_format_types.get(type) || d3_format_typeDefault;
3058 var zcomma = zfill && comma;
3059 return function(value) {
3060 var fullSuffix = suffix;
3061 if (integer && value % 1) return "";
3062 var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign;
3063 if (scale < 0) {
3064 var unit = d3.formatPrefix(value, precision);
3065 value = unit.scale(value);
3066 fullSuffix = unit.symbol + suffix;
3067 } else {
3068 value *= scale;
3069 }
3070 value = type(value, precision);
3071 var i = value.lastIndexOf("."), before, after;
3072 if (i < 0) {
3073 var j = exponent ? value.lastIndexOf("e") : -1;
3074 if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j);
3075 } else {
3076 before = value.substring(0, i);
3077 after = locale_decimal + value.substring(i + 1);
3078 }
3079 if (!zfill && comma) before = formatGroup(before, Infinity);
3080 var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
3081 if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity);
3082 negative += prefix;
3083 value = before + after;
3084 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;
3085 };
3086 };
3087 }
3088 var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
3089 var d3_format_types = d3.map({
3090 b: function(x) {
3091 return x.toString(2);
3092 },
3093 c: function(x) {
3094 return String.fromCharCode(x);
3095 },
3096 o: function(x) {
3097 return x.toString(8);
3098 },
3099 x: function(x) {
3100 return x.toString(16);
3101 },
3102 X: function(x) {
3103 return x.toString(16).toUpperCase();
3104 },
3105 g: function(x, p) {
3106 return x.toPrecision(p);
3107 },
3108 e: function(x, p) {
3109 return x.toExponential(p);
3110 },
3111 f: function(x, p) {
3112 return x.toFixed(p);
3113 },
3114 r: function(x, p) {
3115 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))));
3116 }
3117 });
3118 function d3_format_typeDefault(x) {
3119 return x + "";
3120 }
3121 var d3_time = d3.time = {}, d3_date = Date;
3122 function d3_date_utc() {
3123 this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
3124 }
3125 d3_date_utc.prototype = {
3126 getDate: function() {
3127 return this._.getUTCDate();
3128 },
3129 getDay: function() {
3130 return this._.getUTCDay();
3131 },
3132 getFullYear: function() {
3133 return this._.getUTCFullYear();
3134 },
3135 getHours: function() {
3136 return this._.getUTCHours();
3137 },
3138 getMilliseconds: function() {
3139 return this._.getUTCMilliseconds();
3140 },
3141 getMinutes: function() {
3142 return this._.getUTCMinutes();
3143 },
3144 getMonth: function() {
3145 return this._.getUTCMonth();
3146 },
3147 getSeconds: function() {
3148 return this._.getUTCSeconds();
3149 },
3150 getTime: function() {
3151 return this._.getTime();
3152 },
3153 getTimezoneOffset: function() {
3154 return 0;
3155 },
3156 valueOf: function() {
3157 return this._.valueOf();
3158 },
3159 setDate: function() {
3160 d3_time_prototype.setUTCDate.apply(this._, arguments);
3161 },
3162 setDay: function() {
3163 d3_time_prototype.setUTCDay.apply(this._, arguments);
3164 },
3165 setFullYear: function() {
3166 d3_time_prototype.setUTCFullYear.apply(this._, arguments);
3167 },
3168 setHours: function() {
3169 d3_time_prototype.setUTCHours.apply(this._, arguments);
3170 },
3171 setMilliseconds: function() {
3172 d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
3173 },
3174 setMinutes: function() {
3175 d3_time_prototype.setUTCMinutes.apply(this._, arguments);
3176 },
3177 setMonth: function() {
3178 d3_time_prototype.setUTCMonth.apply(this._, arguments);
3179 },
3180 setSeconds: function() {
3181 d3_time_prototype.setUTCSeconds.apply(this._, arguments);
3182 },
3183 setTime: function() {
3184 d3_time_prototype.setTime.apply(this._, arguments);
3185 }
3186 };
3187 var d3_time_prototype = Date.prototype;
3188 function d3_time_interval(local, step, number) {
3189 function round(date) {
3190 var d0 = local(date), d1 = offset(d0, 1);
3191 return date - d0 < d1 - date ? d0 : d1;
3192 }
3193 function ceil(date) {
3194 step(date = local(new d3_date(date - 1)), 1);
3195 return date;
3196 }
3197 function offset(date, k) {
3198 step(date = new d3_date(+date), k);
3199 return date;
3200 }
3201 function range(t0, t1, dt) {
3202 var time = ceil(t0), times = [];
3203 if (dt > 1) {
3204 while (time < t1) {
3205 if (!(number(time) % dt)) times.push(new Date(+time));
3206 step(time, 1);
3207 }
3208 } else {
3209 while (time < t1) times.push(new Date(+time)), step(time, 1);
3210 }
3211 return times;
3212 }
3213 function range_utc(t0, t1, dt) {
3214 try {
3215 d3_date = d3_date_utc;
3216 var utc = new d3_date_utc();
3217 utc._ = t0;
3218 return range(utc, t1, dt);
3219 } finally {
3220 d3_date = Date;
3221 }
3222 }
3223 local.floor = local;
3224 local.round = round;
3225 local.ceil = ceil;
3226 local.offset = offset;
3227 local.range = range;
3228 var utc = local.utc = d3_time_interval_utc(local);
3229 utc.floor = utc;
3230 utc.round = d3_time_interval_utc(round);
3231 utc.ceil = d3_time_interval_utc(ceil);
3232 utc.offset = d3_time_interval_utc(offset);
3233 utc.range = range_utc;
3234 return local;
3235 }
3236 function d3_time_interval_utc(method) {
3237 return function(date, k) {
3238 try {
3239 d3_date = d3_date_utc;
3240 var utc = new d3_date_utc();
3241 utc._ = date;
3242 return method(utc, k)._;
3243 } finally {
3244 d3_date = Date;
3245 }
3246 };
3247 }
3248 d3_time.year = d3_time_interval(function(date) {
3249 date = d3_time.day(date);
3250 date.setMonth(0, 1);
3251 return date;
3252 }, function(date, offset) {
3253 date.setFullYear(date.getFullYear() + offset);
3254 }, function(date) {
3255 return date.getFullYear();
3256 });
3257 d3_time.years = d3_time.year.range;
3258 d3_time.years.utc = d3_time.year.utc.range;
3259 d3_time.day = d3_time_interval(function(date) {
3260 var day = new d3_date(2e3, 0);
3261 day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
3262 return day;
3263 }, function(date, offset) {
3264 date.setDate(date.getDate() + offset);
3265 }, function(date) {
3266 return date.getDate() - 1;
3267 });
3268 d3_time.days = d3_time.day.range;
3269 d3_time.days.utc = d3_time.day.utc.range;
3270 d3_time.dayOfYear = function(date) {
3271 var year = d3_time.year(date);
3272 return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
3273 };
3274 [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
3275 i = 7 - i;
3276 var interval = d3_time[day] = d3_time_interval(function(date) {
3277 (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
3278 return date;
3279 }, function(date, offset) {
3280 date.setDate(date.getDate() + Math.floor(offset) * 7);
3281 }, function(date) {
3282 var day = d3_time.year(date).getDay();
3283 return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
3284 });
3285 d3_time[day + "s"] = interval.range;
3286 d3_time[day + "s"].utc = interval.utc.range;
3287 d3_time[day + "OfYear"] = function(date) {
3288 var day = d3_time.year(date).getDay();
3289 return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
3290 };
3291 });
3292 d3_time.week = d3_time.sunday;
3293 d3_time.weeks = d3_time.sunday.range;
3294 d3_time.weeks.utc = d3_time.sunday.utc.range;
3295 d3_time.weekOfYear = d3_time.sundayOfYear;
3296 function d3_locale_timeFormat(locale) {
3297 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;
3298 function d3_time_format(template) {
3299 var n = template.length;
3300 function format(date) {
3301 var string = [], i = -1, j = 0, c, p, f;
3302 while (++i < n) {
3303 if (template.charCodeAt(i) === 37) {
3304 string.push(template.slice(j, i));
3305 if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
3306 if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
3307 string.push(c);
3308 j = i + 1;
3309 }
3310 }
3311 string.push(template.slice(j, i));
3312 return string.join("");
3313 }
3314 format.parse = function(string) {
3315 var d = {
3316 y: 1900,
3317 m: 0,
3318 d: 1,
3319 H: 0,
3320 M: 0,
3321 S: 0,
3322 L: 0,
3323 Z: null
3324 }, i = d3_time_parse(d, template, string, 0);
3325 if (i != string.length) return null;
3326 if ("p" in d) d.H = d.H % 12 + d.p * 12;
3327 var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
3328 if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) {
3329 if (!("w" in d)) d.w = "W" in d ? 1 : 0;
3330 date.setFullYear(d.y, 0, 1);
3331 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);
3332 } else date.setFullYear(d.y, d.m, d.d);
3333 date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
3334 return localZ ? date._ : date;
3335 };
3336 format.toString = function() {
3337 return template;
3338 };
3339 return format;
3340 }
3341 function d3_time_parse(date, template, string, j) {
3342 var c, p, t, i = 0, n = template.length, m = string.length;
3343 while (i < n) {
3344 if (j >= m) return -1;
3345 c = template.charCodeAt(i++);
3346 if (c === 37) {
3347 t = template.charAt(i++);
3348 p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
3349 if (!p || (j = p(date, string, j)) < 0) return -1;
3350 } else if (c != string.charCodeAt(j++)) {
3351 return -1;
3352 }
3353 }
3354 return j;
3355 }
3356 d3_time_format.utc = function(template) {
3357 var local = d3_time_format(template);
3358 function format(date) {
3359 try {
3360 d3_date = d3_date_utc;
3361 var utc = new d3_date();
3362 utc._ = date;
3363 return local(utc);
3364 } finally {
3365 d3_date = Date;
3366 }
3367 }
3368 format.parse = function(string) {
3369 try {
3370 d3_date = d3_date_utc;
3371 var date = local.parse(string);
3372 return date && date._;
3373 } finally {
3374 d3_date = Date;
3375 }
3376 };
3377 format.toString = local.toString;
3378 return format;
3379 };
3380 d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
3381 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);
3382 locale_periods.forEach(function(p, i) {
3383 d3_time_periodLookup.set(p.toLowerCase(), i);
3384 });
3385 var d3_time_formats = {
3386 a: function(d) {
3387 return locale_shortDays[d.getDay()];
3388 },
3389 A: function(d) {
3390 return locale_days[d.getDay()];
3391 },
3392 b: function(d) {
3393 return locale_shortMonths[d.getMonth()];
3394 },
3395 B: function(d) {
3396 return locale_months[d.getMonth()];
3397 },
3398 c: d3_time_format(locale_dateTime),
3399 d: function(d, p) {
3400 return d3_time_formatPad(d.getDate(), p, 2);
3401 },
3402 e: function(d, p) {
3403 return d3_time_formatPad(d.getDate(), p, 2);
3404 },
3405 H: function(d, p) {
3406 return d3_time_formatPad(d.getHours(), p, 2);
3407 },
3408 I: function(d, p) {
3409 return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
3410 },
3411 j: function(d, p) {
3412 return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
3413 },
3414 L: function(d, p) {
3415 return d3_time_formatPad(d.getMilliseconds(), p, 3);
3416 },
3417 m: function(d, p) {
3418 return d3_time_formatPad(d.getMonth() + 1, p, 2);
3419 },
3420 M: function(d, p) {
3421 return d3_time_formatPad(d.getMinutes(), p, 2);
3422 },
3423 p: function(d) {
3424 return locale_periods[+(d.getHours() >= 12)];
3425 },
3426 S: function(d, p) {
3427 return d3_time_formatPad(d.getSeconds(), p, 2);
3428 },
3429 U: function(d, p) {
3430 return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
3431 },
3432 w: function(d) {
3433 return d.getDay();
3434 },
3435 W: function(d, p) {
3436 return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
3437 },
3438 x: d3_time_format(locale_date),
3439 X: d3_time_format(locale_time),
3440 y: function(d, p) {
3441 return d3_time_formatPad(d.getFullYear() % 100, p, 2);
3442 },
3443 Y: function(d, p) {
3444 return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
3445 },
3446 Z: d3_time_zone,
3447 "%": function() {
3448 return "%";
3449 }
3450 };
3451 var d3_time_parsers = {
3452 a: d3_time_parseWeekdayAbbrev,
3453 A: d3_time_parseWeekday,
3454 b: d3_time_parseMonthAbbrev,
3455 B: d3_time_parseMonth,
3456 c: d3_time_parseLocaleFull,
3457 d: d3_time_parseDay,
3458 e: d3_time_parseDay,
3459 H: d3_time_parseHour24,
3460 I: d3_time_parseHour24,
3461 j: d3_time_parseDayOfYear,
3462 L: d3_time_parseMilliseconds,
3463 m: d3_time_parseMonthNumber,
3464 M: d3_time_parseMinutes,
3465 p: d3_time_parseAmPm,
3466 S: d3_time_parseSeconds,
3467 U: d3_time_parseWeekNumberSunday,
3468 w: d3_time_parseWeekdayNumber,
3469 W: d3_time_parseWeekNumberMonday,
3470 x: d3_time_parseLocaleDate,
3471 X: d3_time_parseLocaleTime,
3472 y: d3_time_parseYear,
3473 Y: d3_time_parseFullYear,
3474 Z: d3_time_parseZone,
3475 "%": d3_time_parseLiteralPercent
3476 };
3477 function d3_time_parseWeekdayAbbrev(date, string, i) {
3478 d3_time_dayAbbrevRe.lastIndex = 0;
3479 var n = d3_time_dayAbbrevRe.exec(string.slice(i));
3480 return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
3481 }
3482 function d3_time_parseWeekday(date, string, i) {
3483 d3_time_dayRe.lastIndex = 0;
3484 var n = d3_time_dayRe.exec(string.slice(i));
3485 return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
3486 }
3487 function d3_time_parseMonthAbbrev(date, string, i) {
3488 d3_time_monthAbbrevRe.lastIndex = 0;
3489 var n = d3_time_monthAbbrevRe.exec(string.slice(i));
3490 return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
3491 }
3492 function d3_time_parseMonth(date, string, i) {
3493 d3_time_monthRe.lastIndex = 0;
3494 var n = d3_time_monthRe.exec(string.slice(i));
3495 return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
3496 }
3497 function d3_time_parseLocaleFull(date, string, i) {
3498 return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
3499 }
3500 function d3_time_parseLocaleDate(date, string, i) {
3501 return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
3502 }
3503 function d3_time_parseLocaleTime(date, string, i) {
3504 return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
3505 }
3506 function d3_time_parseAmPm(date, string, i) {
3507 var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
3508 return n == null ? -1 : (date.p = n, i);
3509 }
3510 return d3_time_format;
3511 }
3512 var d3_time_formatPads = {
3513 "-": "",
3514 _: " ",
3515 "0": "0"
3516 }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
3517 function d3_time_formatPad(value, fill, width) {
3518 var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
3519 return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
3520 }
3521 function d3_time_formatRe(names) {
3522 return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
3523 }
3524 function d3_time_formatLookup(names) {
3525 var map = new d3_Map(), i = -1, n = names.length;
3526 while (++i < n) map.set(names[i].toLowerCase(), i);
3527 return map;
3528 }
3529 function d3_time_parseWeekdayNumber(date, string, i) {
3530 d3_time_numberRe.lastIndex = 0;
3531 var n = d3_time_numberRe.exec(string.slice(i, i + 1));
3532 return n ? (date.w = +n[0], i + n[0].length) : -1;
3533 }
3534 function d3_time_parseWeekNumberSunday(date, string, i) {
3535 d3_time_numberRe.lastIndex = 0;
3536 var n = d3_time_numberRe.exec(string.slice(i));
3537 return n ? (date.U = +n[0], i + n[0].length) : -1;
3538 }
3539 function d3_time_parseWeekNumberMonday(date, string, i) {
3540 d3_time_numberRe.lastIndex = 0;
3541 var n = d3_time_numberRe.exec(string.slice(i));
3542 return n ? (date.W = +n[0], i + n[0].length) : -1;
3543 }
3544 function d3_time_parseFullYear(date, string, i) {
3545 d3_time_numberRe.lastIndex = 0;
3546 var n = d3_time_numberRe.exec(string.slice(i, i + 4));
3547 return n ? (date.y = +n[0], i + n[0].length) : -1;
3548 }
3549 function d3_time_parseYear(date, string, i) {
3550 d3_time_numberRe.lastIndex = 0;
3551 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3552 return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
3553 }
3554 function d3_time_parseZone(date, string, i) {
3555 return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string,
3556 i + 5) : -1;
3557 }
3558 function d3_time_expandYear(d) {
3559 return d + (d > 68 ? 1900 : 2e3);
3560 }
3561 function d3_time_parseMonthNumber(date, string, i) {
3562 d3_time_numberRe.lastIndex = 0;
3563 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3564 return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
3565 }
3566 function d3_time_parseDay(date, string, i) {
3567 d3_time_numberRe.lastIndex = 0;
3568 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3569 return n ? (date.d = +n[0], i + n[0].length) : -1;
3570 }
3571 function d3_time_parseDayOfYear(date, string, i) {
3572 d3_time_numberRe.lastIndex = 0;
3573 var n = d3_time_numberRe.exec(string.slice(i, i + 3));
3574 return n ? (date.j = +n[0], i + n[0].length) : -1;
3575 }
3576 function d3_time_parseHour24(date, string, i) {
3577 d3_time_numberRe.lastIndex = 0;
3578 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3579 return n ? (date.H = +n[0], i + n[0].length) : -1;
3580 }
3581 function d3_time_parseMinutes(date, string, i) {
3582 d3_time_numberRe.lastIndex = 0;
3583 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3584 return n ? (date.M = +n[0], i + n[0].length) : -1;
3585 }
3586 function d3_time_parseSeconds(date, string, i) {
3587 d3_time_numberRe.lastIndex = 0;
3588 var n = d3_time_numberRe.exec(string.slice(i, i + 2));
3589 return n ? (date.S = +n[0], i + n[0].length) : -1;
3590 }
3591 function d3_time_parseMilliseconds(date, string, i) {
3592 d3_time_numberRe.lastIndex = 0;
3593 var n = d3_time_numberRe.exec(string.slice(i, i + 3));
3594 return n ? (date.L = +n[0], i + n[0].length) : -1;
3595 }
3596 function d3_time_zone(d) {
3597 var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
3598 return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
3599 }
3600 function d3_time_parseLiteralPercent(date, string, i) {
3601 d3_time_percentRe.lastIndex = 0;
3602 var n = d3_time_percentRe.exec(string.slice(i, i + 1));
3603 return n ? i + n[0].length : -1;
3604 }
3605 function d3_time_formatMulti(formats) {
3606 var n = formats.length, i = -1;
3607 while (++i < n) formats[i][0] = this(formats[i][0]);
3608 return function(date) {
3609 var i = 0, f = formats[i];
3610 while (!f[1](date)) f = formats[++i];
3611 return f[0](date);
3612 };
3613 }
3614 d3.locale = function(locale) {
3615 return {
3616 numberFormat: d3_locale_numberFormat(locale),
3617 timeFormat: d3_locale_timeFormat(locale)
3618 };
3619 };
3620 var d3_locale_enUS = d3.locale({
3621 decimal: ".",
3622 thousands: ",",
3623 grouping: [ 3 ],
3624 currency: [ "$", "" ],
3625 dateTime: "%a %b %e %X %Y",
3626 date: "%m/%d/%Y",
3627 time: "%H:%M:%S",
3628 periods: [ "AM", "PM" ],
3629 days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
3630 shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
3631 months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
3632 shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
3633 });
3634 d3.format = d3_locale_enUS.numberFormat;
3635 d3.geo = {};
3636 function d3_adder() {}
3637 d3_adder.prototype = {
3638 s: 0,
3639 t: 0,
3640 add: function(y) {
3641 d3_adderSum(y, this.t, d3_adderTemp);
3642 d3_adderSum(d3_adderTemp.s, this.s, this);
3643 if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
3644 },
3645 reset: function() {
3646 this.s = this.t = 0;
3647 },
3648 valueOf: function() {
3649 return this.s;
3650 }
3651 };
3652 var d3_adderTemp = new d3_adder();
3653 function d3_adderSum(a, b, o) {
3654 var x = o.s = a + b, bv = x - a, av = x - bv;
3655 o.t = a - av + (b - bv);
3656 }
3657 d3.geo.stream = function(object, listener) {
3658 if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
3659 d3_geo_streamObjectType[object.type](object, listener);
3660 } else {
3661 d3_geo_streamGeometry(object, listener);
3662 }
3663 };
3664 function d3_geo_streamGeometry(geometry, listener) {
3665 if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
3666 d3_geo_streamGeometryType[geometry.type](geometry, listener);
3667 }
3668 }
3669 var d3_geo_streamObjectType = {
3670 Feature: function(feature, listener) {
3671 d3_geo_streamGeometry(feature.geometry, listener);
3672 },
3673 FeatureCollection: function(object, listener) {
3674 var features = object.features, i = -1, n = features.length;
3675 while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
3676 }
3677 };
3678 var d3_geo_streamGeometryType = {
3679 Sphere: function(object, listener) {
3680 listener.sphere();
3681 },
3682 Point: function(object, listener) {
3683 object = object.coordinates;
3684 listener.point(object[0], object[1], object[2]);
3685 },
3686 MultiPoint: function(object, listener) {
3687 var coordinates = object.coordinates, i = -1, n = coordinates.length;
3688 while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
3689 },
3690 LineString: function(object, listener) {
3691 d3_geo_streamLine(object.coordinates, listener, 0);
3692 },
3693 MultiLineString: function(object, listener) {
3694 var coordinates = object.coordinates, i = -1, n = coordinates.length;
3695 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
3696 },
3697 Polygon: function(object, listener) {
3698 d3_geo_streamPolygon(object.coordinates, listener);
3699 },
3700 MultiPolygon: function(object, listener) {
3701 var coordinates = object.coordinates, i = -1, n = coordinates.length;
3702 while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
3703 },
3704 GeometryCollection: function(object, listener) {
3705 var geometries = object.geometries, i = -1, n = geometries.length;
3706 while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
3707 }
3708 };
3709 function d3_geo_streamLine(coordinates, listener, closed) {
3710 var i = -1, n = coordinates.length - closed, coordinate;
3711 listener.lineStart();
3712 while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
3713 listener.lineEnd();
3714 }
3715 function d3_geo_streamPolygon(coordinates, listener) {
3716 var i = -1, n = coordinates.length;
3717 listener.polygonStart();
3718 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
3719 listener.polygonEnd();
3720 }
3721 d3.geo.area = function(object) {
3722 d3_geo_areaSum = 0;
3723 d3.geo.stream(object, d3_geo_area);
3724 return d3_geo_areaSum;
3725 };
3726 var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
3727 var d3_geo_area = {
3728 sphere: function() {
3729 d3_geo_areaSum += 4 * π;
3730 },
3731 point: d3_noop,
3732 lineStart: d3_noop,
3733 lineEnd: d3_noop,
3734 polygonStart: function() {
3735 d3_geo_areaRingSum.reset();
3736 d3_geo_area.lineStart = d3_geo_areaRingStart;
3737 },
3738 polygonEnd: function() {
3739 var area = 2 * d3_geo_areaRingSum;
3740 d3_geo_areaSum += area < 0 ? 4 * π + area : area;
3741 d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
3742 }
3743 };
3744 function d3_geo_areaRingStart() {
3745 var λ00, φ00, λ0, cosφ0, sinφ0;
3746 d3_geo_area.point = function(λ, φ) {
3747 d3_geo_area.point = nextPoint;
3748 λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4),
3749 sinφ0 = Math.sin(φ);
3750 };
3751 function nextPoint(λ, φ) {
3752 λ *= d3_radians;
3753 φ = φ * d3_radians / 2 + π / 4;
3754 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λ);
3755 d3_geo_areaRingSum.add(Math.atan2(v, u));
3756 λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3757 }
3758 d3_geo_area.lineEnd = function() {
3759 nextPoint(λ00, φ00);
3760 };
3761 }
3762 function d3_geo_cartesian(spherical) {
3763 var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
3764 return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
3765 }
3766 function d3_geo_cartesianDot(a, b) {
3767 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
3768 }
3769 function d3_geo_cartesianCross(a, b) {
3770 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] ];
3771 }
3772 function d3_geo_cartesianAdd(a, b) {
3773 a[0] += b[0];
3774 a[1] += b[1];
3775 a[2] += b[2];
3776 }
3777 function d3_geo_cartesianScale(vector, k) {
3778 return [ vector[0] * k, vector[1] * k, vector[2] * k ];
3779 }
3780 function d3_geo_cartesianNormalize(d) {
3781 var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
3782 d[0] /= l;
3783 d[1] /= l;
3784 d[2] /= l;
3785 }
3786 function d3_geo_spherical(cartesian) {
3787 return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
3788 }
3789 function d3_geo_sphericalEqual(a, b) {
3790 return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
3791 }
3792 d3.geo.bounds = function() {
3793 var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
3794 var bound = {
3795 point: point,
3796 lineStart: lineStart,
3797 lineEnd: lineEnd,
3798 polygonStart: function() {
3799 bound.point = ringPoint;
3800 bound.lineStart = ringStart;
3801 bound.lineEnd = ringEnd;
3802 dλSum = 0;
3803 d3_geo_area.polygonStart();
3804 },
3805 polygonEnd: function() {
3806 d3_geo_area.polygonEnd();
3807 bound.point = point;
3808 bound.lineStart = lineStart;
3809 bound.lineEnd = lineEnd;
3810 if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
3811 range[0] = λ0, range[1] = λ1;
3812 }
3813 };
3814 function point(λ, φ) {
3815 ranges.push(range = [ λ0 = λ, λ1 = λ ]);
3816 if (φ < φ0) φ0 = φ;
3817 if (φ > φ1) φ1 = φ;
3818 }
3819 function linePoint(λ, φ) {
3820 var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
3821 if (p0) {
3822 var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
3823 d3_geo_cartesianNormalize(inflection);
3824 inflection = d3_geo_spherical(inflection);
3825 var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
3826 if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3827 var φi = inflection[1] * d3_degrees;
3828 if (φi > φ1) φ1 = φi;
3829 } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3830 var φi = -inflection[1] * d3_degrees;
3831 if (φi < φ0) φ0 = φi;
3832 } else {
3833 if (φ < φ0) φ0 = φ;
3834 if (φ > φ1) φ1 = φ;
3835 }
3836 if (antimeridian) {
3837 if (λ < λ_) {
3838 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3839 } else {
3840 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3841 }
3842 } else {
3843 if (λ1 >= λ0) {
3844 if (λ < λ0) λ0 = λ;
3845 if (λ > λ1) λ1 = λ;
3846 } else {
3847 if (λ > λ_) {
3848 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3849 } else {
3850 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3851 }
3852 }
3853 }
3854 } else {
3855 point(λ, φ);
3856 }
3857 p0 = p, λ_ = λ;
3858 }
3859 function lineStart() {
3860 bound.point = linePoint;
3861 }
3862 function lineEnd() {
3863 range[0] = λ0, range[1] = λ1;
3864 bound.point = point;
3865 p0 = null;
3866 }
3867 function ringPoint(λ, φ) {
3868 if (p0) {
3869 var dλ = λ - λ_;
3870 dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3871 } else λ__ = λ, φ__ = φ;
3872 d3_geo_area.point(λ, φ);
3873 linePoint(λ, φ);
3874 }
3875 function ringStart() {
3876 d3_geo_area.lineStart();
3877 }
3878 function ringEnd() {
3879 ringPoint(λ__, φ__);
3880 d3_geo_area.lineEnd();
3881 if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3882 range[0] = λ0, range[1] = λ1;
3883 p0 = null;
3884 }
3885 function angle(λ0, λ1) {
3886 return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
3887 }
3888 function compareRanges(a, b) {
3889 return a[0] - b[0];
3890 }
3891 function withinRange(x, range) {
3892 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3893 }
3894 return function(feature) {
3895 φ1 = λ1 = -(λ0 = φ0 = Infinity);
3896 ranges = [];
3897 d3.geo.stream(feature, bound);
3898 var n = ranges.length;
3899 if (n) {
3900 ranges.sort(compareRanges);
3901 for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
3902 b = ranges[i];
3903 if (withinRange(b[0], a) || withinRange(b[1], a)) {
3904 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3905 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3906 } else {
3907 merged.push(a = b);
3908 }
3909 }
3910 var best = -Infinity, dλ;
3911 for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3912 b = merged[i];
3913 if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3914 }
3915 }
3916 ranges = range = null;
3917 return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
3918 };
3919 }();
3920 d3.geo.centroid = function(object) {
3921 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;
3922 d3.geo.stream(object, d3_geo_centroid);
3923 var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
3924 if (m < ε2) {
3925 x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3926 if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3927 m = x * x + y * y + z * z;
3928 if (m < ε2) return [ NaN, NaN ];
3929 }
3930 return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
3931 };
3932 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;
3933 var d3_geo_centroid = {
3934 sphere: d3_noop,
3935 point: d3_geo_centroidPoint,
3936 lineStart: d3_geo_centroidLineStart,
3937 lineEnd: d3_geo_centroidLineEnd,
3938 polygonStart: function() {
3939 d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3940 },
3941 polygonEnd: function() {
3942 d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3943 }
3944 };
3945 function d3_geo_centroidPoint(λ, φ) {
3946 λ *= d3_radians;
3947 var cosφ = Math.cos(φ *= d3_radians);
3948 d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3949 }
3950 function d3_geo_centroidPointXYZ(x, y, z) {
3951 ++d3_geo_centroidW0;
3952 d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3953 d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3954 d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3955 }
3956 function d3_geo_centroidLineStart() {
3957 var x0, y0, z0;
3958 d3_geo_centroid.point = function(λ, φ) {
3959 λ *= d3_radians;
3960 var cosφ = Math.cos(φ *= d3_radians);
3961 x0 = cosφ * Math.cos(λ);
3962 y0 = cosφ * Math.sin(λ);
3963 z0 = Math.sin(φ);
3964 d3_geo_centroid.point = nextPoint;
3965 d3_geo_centroidPointXYZ(x0, y0, z0);
3966 };
3967 function nextPoint(λ, φ) {
3968 λ *= d3_radians;
3969 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);
3970 d3_geo_centroidW1 += w;
3971 d3_geo_centroidX1 += w * (x0 + (x0 = x));
3972 d3_geo_centroidY1 += w * (y0 + (y0 = y));
3973 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3974 d3_geo_centroidPointXYZ(x0, y0, z0);
3975 }
3976 }
3977 function d3_geo_centroidLineEnd() {
3978 d3_geo_centroid.point = d3_geo_centroidPoint;
3979 }
3980 function d3_geo_centroidRingStart() {
3981 var λ00, φ00, x0, y0, z0;
3982 d3_geo_centroid.point = function(λ, φ) {
3983 λ00 = λ, φ00 = φ;
3984 d3_geo_centroid.point = nextPoint;
3985 λ *= d3_radians;
3986 var cosφ = Math.cos(φ *= d3_radians);
3987 x0 = cosφ * Math.cos(λ);
3988 y0 = cosφ * Math.sin(λ);
3989 z0 = Math.sin(φ);
3990 d3_geo_centroidPointXYZ(x0, y0, z0);
3991 };
3992 d3_geo_centroid.lineEnd = function() {
3993 nextPoint(λ00, φ00);
3994 d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3995 d3_geo_centroid.point = d3_geo_centroidPoint;
3996 };
3997 function nextPoint(λ, φ) {
3998 λ *= d3_radians;
3999 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);
4000 d3_geo_centroidX2 += v * cx;
4001 d3_geo_centroidY2 += v * cy;
4002 d3_geo_centroidZ2 += v * cz;
4003 d3_geo_centroidW1 += w;
4004 d3_geo_centroidX1 += w * (x0 + (x0 = x));
4005 d3_geo_centroidY1 += w * (y0 + (y0 = y));
4006 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4007 d3_geo_centroidPointXYZ(x0, y0, z0);
4008 }
4009 }
4010 function d3_geo_compose(a, b) {
4011 function compose(x, y) {
4012 return x = a(x, y), b(x[0], x[1]);
4013 }
4014 if (a.invert && b.invert) compose.invert = function(x, y) {
4015 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
4016 };
4017 return compose;
4018 }
4019 function d3_true() {
4020 return true;
4021 }
4022 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
4023 var subject = [], clip = [];
4024 segments.forEach(function(segment) {
4025 if ((n = segment.length - 1) <= 0) return;
4026 var n, p0 = segment[0], p1 = segment[n];
4027 if (d3_geo_sphericalEqual(p0, p1)) {
4028 listener.lineStart();
4029 for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
4030 listener.lineEnd();
4031 return;
4032 }
4033 var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
4034 a.o = b;
4035 subject.push(a);
4036 clip.push(b);
4037 a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
4038 b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
4039 a.o = b;
4040 subject.push(a);
4041 clip.push(b);
4042 });
4043 clip.sort(compare);
4044 d3_geo_clipPolygonLinkCircular(subject);
4045 d3_geo_clipPolygonLinkCircular(clip);
4046 if (!subject.length) return;
4047 for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
4048 clip[i].e = entry = !entry;
4049 }
4050 var start = subject[0], points, point;
4051 while (1) {
4052 var current = start, isSubject = true;
4053 while (current.v) if ((current = current.n) === start) return;
4054 points = current.z;
4055 listener.lineStart();
4056 do {
4057 current.v = current.o.v = true;
4058 if (current.e) {
4059 if (isSubject) {
4060 for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
4061 } else {
4062 interpolate(current.x, current.n.x, 1, listener);
4063 }
4064 current = current.n;
4065 } else {
4066 if (isSubject) {
4067 points = current.p.z;
4068 for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
4069 } else {
4070 interpolate(current.x, current.p.x, -1, listener);
4071 }
4072 current = current.p;
4073 }
4074 current = current.o;
4075 points = current.z;
4076 isSubject = !isSubject;
4077 } while (!current.v);
4078 listener.lineEnd();
4079 }
4080 }
4081 function d3_geo_clipPolygonLinkCircular(array) {
4082 if (!(n = array.length)) return;
4083 var n, i = 0, a = array[0], b;
4084 while (++i < n) {
4085 a.n = b = array[i];
4086 b.p = a;
4087 a = b;
4088 }
4089 a.n = b = array[0];
4090 b.p = a;
4091 }
4092 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
4093 this.x = point;
4094 this.z = points;
4095 this.o = other;
4096 this.e = entry;
4097 this.v = false;
4098 this.n = this.p = null;
4099 }
4100 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
4101 return function(rotate, listener) {
4102 var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
4103 var clip = {
4104 point: point,
4105 lineStart: lineStart,
4106 lineEnd: lineEnd,
4107 polygonStart: function() {
4108 clip.point = pointRing;
4109 clip.lineStart = ringStart;
4110 clip.lineEnd = ringEnd;
4111 segments = [];
4112 polygon = [];
4113 },
4114 polygonEnd: function() {
4115 clip.point = point;
4116 clip.lineStart = lineStart;
4117 clip.lineEnd = lineEnd;
4118 segments = d3.merge(segments);
4119 var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
4120 if (segments.length) {
4121 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
4122 d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
4123 } else if (clipStartInside) {
4124 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
4125 listener.lineStart();
4126 interpolate(null, null, 1, listener);
4127 listener.lineEnd();
4128 }
4129 if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
4130 segments = polygon = null;
4131 },
4132 sphere: function() {
4133 listener.polygonStart();
4134 listener.lineStart();
4135 interpolate(null, null, 1, listener);
4136 listener.lineEnd();
4137 listener.polygonEnd();
4138 }
4139 };
4140 function point(λ, φ) {
4141 var point = rotate(λ, φ);
4142 if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
4143 }
4144 function pointLine(λ, φ) {
4145 var point = rotate(λ, φ);
4146 line.point(point[0], point[1]);
4147 }
4148 function lineStart() {
4149 clip.point = pointLine;
4150 line.lineStart();
4151 }
4152 function lineEnd() {
4153 clip.point = point;
4154 line.lineEnd();
4155 }
4156 var segments;
4157 var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring;
4158 function pointRing(λ, φ) {
4159 ring.push([ λ, φ ]);
4160 var point = rotate(λ, φ);
4161 ringListener.point(point[0], point[1]);
4162 }
4163 function ringStart() {
4164 ringListener.lineStart();
4165 ring = [];
4166 }
4167 function ringEnd() {
4168 pointRing(ring[0][0], ring[0][1]);
4169 ringListener.lineEnd();
4170 var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
4171 ring.pop();
4172 polygon.push(ring);
4173 ring = null;
4174 if (!n) return;
4175 if (clean & 1) {
4176 segment = ringSegments[0];
4177 var n = segment.length - 1, i = -1, point;
4178 if (n > 0) {
4179 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
4180 listener.lineStart();
4181 while (++i < n) listener.point((point = segment[i])[0], point[1]);
4182 listener.lineEnd();
4183 }
4184 return;
4185 }
4186 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
4187 segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
4188 }
4189 return clip;
4190 };
4191 }
4192 function d3_geo_clipSegmentLength1(segment) {
4193 return segment.length > 1;
4194 }
4195 function d3_geo_clipBufferListener() {
4196 var lines = [], line;
4197 return {
4198 lineStart: function() {
4199 lines.push(line = []);
4200 },
4201 point: function(λ, φ) {
4202 line.push([ λ, φ ]);
4203 },
4204 lineEnd: d3_noop,
4205 buffer: function() {
4206 var buffer = lines;
4207 lines = [];
4208 line = null;
4209 return buffer;
4210 },
4211 rejoin: function() {
4212 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
4213 }
4214 };
4215 }
4216 function d3_geo_clipSort(a, b) {
4217 return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
4218 }
4219 var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
4220 function d3_geo_clipAntimeridianLine(listener) {
4221 var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
4222 return {
4223 lineStart: function() {
4224 listener.lineStart();
4225 clean = 1;
4226 },
4227 point: function(λ1, φ1) {
4228 var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
4229 if (abs(dλ - π) < ε) {
4230 listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
4231 listener.point(sλ0, φ0);
4232 listener.lineEnd();
4233 listener.lineStart();
4234 listener.point(sλ1, φ0);
4235 listener.point(λ1, φ0);
4236 clean = 0;
4237 } else if (sλ0 !== sλ1 && dλ >= π) {
4238 if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
4239 if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
4240 φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
4241 listener.point(sλ0, φ0);
4242 listener.lineEnd();
4243 listener.lineStart();
4244 listener.point(sλ1, φ0);
4245 clean = 0;
4246 }
4247 listener.point(λ0 = λ1, φ0 = φ1);
4248 sλ0 = sλ1;
4249 },
4250 lineEnd: function() {
4251 listener.lineEnd();
4252 λ0 = φ0 = NaN;
4253 },
4254 clean: function() {
4255 return 2 - clean;
4256 }
4257 };
4258 }
4259 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
4260 var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
4261 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;
4262 }
4263 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
4264 var φ;
4265 if (from == null) {
4266 φ = direction * halfπ;
4267 listener.point(-π, φ);
4268 listener.point(0, φ);
4269 listener.point(π, φ);
4270 listener.point(π, 0);
4271 listener.point(π, -φ);
4272 listener.point(0, -φ);
4273 listener.point(-π, -φ);
4274 listener.point(-π, 0);
4275 listener.point(-π, φ);
4276 } else if (abs(from[0] - to[0]) > ε) {
4277 var s = from[0] < to[0] ? π : -π;
4278 φ = direction * s / 2;
4279 listener.point(-s, φ);
4280 listener.point(0, φ);
4281 listener.point(s, φ);
4282 } else {
4283 listener.point(to[0], to[1]);
4284 }
4285 }
4286 function d3_geo_pointInPolygon(point, polygon) {
4287 var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
4288 d3_geo_areaRingSum.reset();
4289 for (var i = 0, n = polygon.length; i < n; ++i) {
4290 var ring = polygon[i], m = ring.length;
4291 if (!m) continue;
4292 var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
4293 while (true) {
4294 if (j === m) j = 0;
4295 point = ring[j];
4296 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φ;
4297 d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
4298 polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
4299 if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
4300 var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
4301 d3_geo_cartesianNormalize(arc);
4302 var intersection = d3_geo_cartesianCross(meridianNormal, arc);
4303 d3_geo_cartesianNormalize(intersection);
4304 var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
4305 if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
4306 winding += antimeridian ^ dλ >= 0 ? 1 : -1;
4307 }
4308 }
4309 if (!j++) break;
4310 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
4311 }
4312 }
4313 return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1;
4314 }
4315 function d3_geo_clipCircle(radius) {
4316 var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
4317 return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
4318 function visible(λ, φ) {
4319 return Math.cos(λ) * Math.cos(φ) > cr;
4320 }
4321 function clipLine(listener) {
4322 var point0, c0, v0, v00, clean;
4323 return {
4324 lineStart: function() {
4325 v00 = v0 = false;
4326 clean = 1;
4327 },
4328 point: function(λ, φ) {
4329 var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
4330 if (!point0 && (v00 = v0 = v)) listener.lineStart();
4331 if (v !== v0) {
4332 point2 = intersect(point0, point1);
4333 if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
4334 point1[0] += ε;
4335 point1[1] += ε;
4336 v = visible(point1[0], point1[1]);
4337 }
4338 }
4339 if (v !== v0) {
4340 clean = 0;
4341 if (v) {
4342 listener.lineStart();
4343 point2 = intersect(point1, point0);
4344 listener.point(point2[0], point2[1]);
4345 } else {
4346 point2 = intersect(point0, point1);
4347 listener.point(point2[0], point2[1]);
4348 listener.lineEnd();
4349 }
4350 point0 = point2;
4351 } else if (notHemisphere && point0 && smallRadius ^ v) {
4352 var t;
4353 if (!(c & c0) && (t = intersect(point1, point0, true))) {
4354 clean = 0;
4355 if (smallRadius) {
4356 listener.lineStart();
4357 listener.point(t[0][0], t[0][1]);
4358 listener.point(t[1][0], t[1][1]);
4359 listener.lineEnd();
4360 } else {
4361 listener.point(t[1][0], t[1][1]);
4362 listener.lineEnd();
4363 listener.lineStart();
4364 listener.point(t[0][0], t[0][1]);
4365 }
4366 }
4367 }
4368 if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
4369 listener.point(point1[0], point1[1]);
4370 }
4371 point0 = point1, v0 = v, c0 = c;
4372 },
4373 lineEnd: function() {
4374 if (v0) listener.lineEnd();
4375 point0 = null;
4376 },
4377 clean: function() {
4378 return clean | (v00 && v0) << 1;
4379 }
4380 };
4381 }
4382 function intersect(a, b, two) {
4383 var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
4384 var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
4385 if (!determinant) return !two && a;
4386 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);
4387 d3_geo_cartesianAdd(A, B);
4388 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);
4389 if (t2 < 0) return;
4390 var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
4391 d3_geo_cartesianAdd(q, A);
4392 q = d3_geo_spherical(q);
4393 if (!two) return q;
4394 var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
4395 if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
4396 var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
4397 if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
4398 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)) {
4399 var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
4400 d3_geo_cartesianAdd(q1, A);
4401 return [ q, d3_geo_spherical(q1) ];
4402 }
4403 }
4404 function code(λ, φ) {
4405 var r = smallRadius ? radius : π - radius, code = 0;
4406 if (λ < -r) code |= 1; else if (λ > r) code |= 2;
4407 if (φ < -r) code |= 4; else if (φ > r) code |= 8;
4408 return code;
4409 }
4410 }
4411 function d3_geom_clipLine(x0, y0, x1, y1) {
4412 return function(line) {
4413 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;
4414 r = x0 - ax;
4415 if (!dx && r > 0) return;
4416 r /= dx;
4417 if (dx < 0) {
4418 if (r < t0) return;
4419 if (r < t1) t1 = r;
4420 } else if (dx > 0) {
4421 if (r > t1) return;
4422 if (r > t0) t0 = r;
4423 }
4424 r = x1 - ax;
4425 if (!dx && r < 0) return;
4426 r /= dx;
4427 if (dx < 0) {
4428 if (r > t1) return;
4429 if (r > t0) t0 = r;
4430 } else if (dx > 0) {
4431 if (r < t0) return;
4432 if (r < t1) t1 = r;
4433 }
4434 r = y0 - ay;
4435 if (!dy && r > 0) return;
4436 r /= dy;
4437 if (dy < 0) {
4438 if (r < t0) return;
4439 if (r < t1) t1 = r;
4440 } else if (dy > 0) {
4441 if (r > t1) return;
4442 if (r > t0) t0 = r;
4443 }
4444 r = y1 - ay;
4445 if (!dy && r < 0) return;
4446 r /= dy;
4447 if (dy < 0) {
4448 if (r > t1) return;
4449 if (r > t0) t0 = r;
4450 } else if (dy > 0) {
4451 if (r < t0) return;
4452 if (r < t1) t1 = r;
4453 }
4454 if (t0 > 0) line.a = {
4455 x: ax + t0 * dx,
4456 y: ay + t0 * dy
4457 };
4458 if (t1 < 1) line.b = {
4459 x: ax + t1 * dx,
4460 y: ay + t1 * dy
4461 };
4462 return line;
4463 };
4464 }
4465 var d3_geo_clipExtentMAX = 1e9;
4466 d3.geo.clipExtent = function() {
4467 var x0, y0, x1, y1, stream, clip, clipExtent = {
4468 stream: function(output) {
4469 if (stream) stream.valid = false;
4470 stream = clip(output);
4471 stream.valid = true;
4472 return stream;
4473 },
4474 extent: function(_) {
4475 if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
4476 clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
4477 if (stream) stream.valid = false, stream = null;
4478 return clipExtent;
4479 }
4480 };
4481 return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
4482 };
4483 function d3_geo_clipExtent(x0, y0, x1, y1) {
4484 return function(listener) {
4485 var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
4486 var clip = {
4487 point: point,
4488 lineStart: lineStart,
4489 lineEnd: lineEnd,
4490 polygonStart: function() {
4491 listener = bufferListener;
4492 segments = [];
4493 polygon = [];
4494 clean = true;
4495 },
4496 polygonEnd: function() {
4497 listener = listener_;
4498 segments = d3.merge(segments);
4499 var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
4500 if (inside || visible) {
4501 listener.polygonStart();
4502 if (inside) {
4503 listener.lineStart();
4504 interpolate(null, null, 1, listener);
4505 listener.lineEnd();
4506 }
4507 if (visible) {
4508 d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
4509 }
4510 listener.polygonEnd();
4511 }
4512 segments = polygon = ring = null;
4513 }
4514 };
4515 function insidePolygon(p) {
4516 var wn = 0, n = polygon.length, y = p[1];
4517 for (var i = 0; i < n; ++i) {
4518 for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
4519 b = v[j];
4520 if (a[1] <= y) {
4521 if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
4522 } else {
4523 if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
4524 }
4525 a = b;
4526 }
4527 }
4528 return wn !== 0;
4529 }
4530 function interpolate(from, to, direction, listener) {
4531 var a = 0, a1 = 0;
4532 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
4533 do {
4534 listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
4535 } while ((a = (a + direction + 4) % 4) !== a1);
4536 } else {
4537 listener.point(to[0], to[1]);
4538 }
4539 }
4540 function pointVisible(x, y) {
4541 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
4542 }
4543 function point(x, y) {
4544 if (pointVisible(x, y)) listener.point(x, y);
4545 }
4546 var x__, y__, v__, x_, y_, v_, first, clean;
4547 function lineStart() {
4548 clip.point = linePoint;
4549 if (polygon) polygon.push(ring = []);
4550 first = true;
4551 v_ = false;
4552 x_ = y_ = NaN;
4553 }
4554 function lineEnd() {
4555 if (segments) {
4556 linePoint(x__, y__);
4557 if (v__ && v_) bufferListener.rejoin();
4558 segments.push(bufferListener.buffer());
4559 }
4560 clip.point = point;
4561 if (v_) listener.lineEnd();
4562 }
4563 function linePoint(x, y) {
4564 x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
4565 y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
4566 var v = pointVisible(x, y);
4567 if (polygon) ring.push([ x, y ]);
4568 if (first) {
4569 x__ = x, y__ = y, v__ = v;
4570 first = false;
4571 if (v) {
4572 listener.lineStart();
4573 listener.point(x, y);
4574 }
4575 } else {
4576 if (v && v_) listener.point(x, y); else {
4577 var l = {
4578 a: {
4579 x: x_,
4580 y: y_
4581 },
4582 b: {
4583 x: x,
4584 y: y
4585 }
4586 };
4587 if (clipLine(l)) {
4588 if (!v_) {
4589 listener.lineStart();
4590 listener.point(l.a.x, l.a.y);
4591 }
4592 listener.point(l.b.x, l.b.y);
4593 if (!v) listener.lineEnd();
4594 clean = false;
4595 } else if (v) {
4596 listener.lineStart();
4597 listener.point(x, y);
4598 clean = false;
4599 }
4600 }
4601 }
4602 x_ = x, y_ = y, v_ = v;
4603 }
4604 return clip;
4605 };
4606 function corner(p, direction) {
4607 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;
4608 }
4609 function compare(a, b) {
4610 return comparePoints(a.x, b.x);
4611 }
4612 function comparePoints(a, b) {
4613 var ca = corner(a, 1), cb = corner(b, 1);
4614 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];
4615 }
4616 }
4617 function d3_geo_conic(projectAt) {
4618 var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
4619 p.parallels = function(_) {
4620 if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
4621 return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
4622 };
4623 return p;
4624 }
4625 function d3_geo_conicEqualArea(φ0, φ1) {
4626 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;
4627 function forward(λ, φ) {
4628 var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
4629 return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
4630 }
4631 forward.invert = function(x, y) {
4632 var ρ0_y = ρ0 - y;
4633 return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
4634 };
4635 return forward;
4636 }
4637 (d3.geo.conicEqualArea = function() {
4638 return d3_geo_conic(d3_geo_conicEqualArea);
4639 }).raw = d3_geo_conicEqualArea;
4640 d3.geo.albers = function() {
4641 return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
4642 };
4643 d3.geo.albersUsa = function() {
4644 var lower48 = d3.geo.albers();
4645 var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
4646 var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
4647 var point, pointStream = {
4648 point: function(x, y) {
4649 point = [ x, y ];
4650 }
4651 }, lower48Point, alaskaPoint, hawaiiPoint;
4652 function albersUsa(coordinates) {
4653 var x = coordinates[0], y = coordinates[1];
4654 point = null;
4655 (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
4656 return point;
4657 }
4658 albersUsa.invert = function(coordinates) {
4659 var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
4660 return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
4661 };
4662 albersUsa.stream = function(stream) {
4663 var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
4664 return {
4665 point: function(x, y) {
4666 lower48Stream.point(x, y);
4667 alaskaStream.point(x, y);
4668 hawaiiStream.point(x, y);
4669 },
4670 sphere: function() {
4671 lower48Stream.sphere();
4672 alaskaStream.sphere();
4673 hawaiiStream.sphere();
4674 },
4675 lineStart: function() {
4676 lower48Stream.lineStart();
4677 alaskaStream.lineStart();
4678 hawaiiStream.lineStart();
4679 },
4680 lineEnd: function() {
4681 lower48Stream.lineEnd();
4682 alaskaStream.lineEnd();
4683 hawaiiStream.lineEnd();
4684 },
4685 polygonStart: function() {
4686 lower48Stream.polygonStart();
4687 alaskaStream.polygonStart();
4688 hawaiiStream.polygonStart();
4689 },
4690 polygonEnd: function() {
4691 lower48Stream.polygonEnd();
4692 alaskaStream.polygonEnd();
4693 hawaiiStream.polygonEnd();
4694 }
4695 };
4696 };
4697 albersUsa.precision = function(_) {
4698 if (!arguments.length) return lower48.precision();
4699 lower48.precision(_);
4700 alaska.precision(_);
4701 hawaii.precision(_);
4702 return albersUsa;
4703 };
4704 albersUsa.scale = function(_) {
4705 if (!arguments.length) return lower48.scale();
4706 lower48.scale(_);
4707 alaska.scale(_ * .35);
4708 hawaii.scale(_);
4709 return albersUsa.translate(lower48.translate());
4710 };
4711 albersUsa.translate = function(_) {
4712 if (!arguments.length) return lower48.translate();
4713 var k = lower48.scale(), x = +_[0], y = +_[1];
4714 lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
4715 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;
4716 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;
4717 return albersUsa;
4718 };
4719 return albersUsa.scale(1070);
4720 };
4721 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4722 point: d3_noop,
4723 lineStart: d3_noop,
4724 lineEnd: d3_noop,
4725 polygonStart: function() {
4726 d3_geo_pathAreaPolygon = 0;
4727 d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4728 },
4729 polygonEnd: function() {
4730 d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4731 d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4732 }
4733 };
4734 function d3_geo_pathAreaRingStart() {
4735 var x00, y00, x0, y0;
4736 d3_geo_pathArea.point = function(x, y) {
4737 d3_geo_pathArea.point = nextPoint;
4738 x00 = x0 = x, y00 = y0 = y;
4739 };
4740 function nextPoint(x, y) {
4741 d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4742 x0 = x, y0 = y;
4743 }
4744 d3_geo_pathArea.lineEnd = function() {
4745 nextPoint(x00, y00);
4746 };
4747 }
4748 var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
4749 var d3_geo_pathBounds = {
4750 point: d3_geo_pathBoundsPoint,
4751 lineStart: d3_noop,
4752 lineEnd: d3_noop,
4753 polygonStart: d3_noop,
4754 polygonEnd: d3_noop
4755 };
4756 function d3_geo_pathBoundsPoint(x, y) {
4757 if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4758 if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4759 if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4760 if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4761 }
4762 function d3_geo_pathBuffer() {
4763 var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
4764 var stream = {
4765 point: point,
4766 lineStart: function() {
4767 stream.point = pointLineStart;
4768 },
4769 lineEnd: lineEnd,
4770 polygonStart: function() {
4771 stream.lineEnd = lineEndPolygon;
4772 },
4773 polygonEnd: function() {
4774 stream.lineEnd = lineEnd;
4775 stream.point = point;
4776 },
4777 pointRadius: function(_) {
4778 pointCircle = d3_geo_pathBufferCircle(_);
4779 return stream;
4780 },
4781 result: function() {
4782 if (buffer.length) {
4783 var result = buffer.join("");
4784 buffer = [];
4785 return result;
4786 }
4787 }
4788 };
4789 function point(x, y) {
4790 buffer.push("M", x, ",", y, pointCircle);
4791 }
4792 function pointLineStart(x, y) {
4793 buffer.push("M", x, ",", y);
4794 stream.point = pointLine;
4795 }
4796 function pointLine(x, y) {
4797 buffer.push("L", x, ",", y);
4798 }
4799 function lineEnd() {
4800 stream.point = point;
4801 }
4802 function lineEndPolygon() {
4803 buffer.push("Z");
4804 }
4805 return stream;
4806 }
4807 function d3_geo_pathBufferCircle(radius) {
4808 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
4809 }
4810 var d3_geo_pathCentroid = {
4811 point: d3_geo_pathCentroidPoint,
4812 lineStart: d3_geo_pathCentroidLineStart,
4813 lineEnd: d3_geo_pathCentroidLineEnd,
4814 polygonStart: function() {
4815 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4816 },
4817 polygonEnd: function() {
4818 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4819 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4820 d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4821 }
4822 };
4823 function d3_geo_pathCentroidPoint(x, y) {
4824 d3_geo_centroidX0 += x;
4825 d3_geo_centroidY0 += y;
4826 ++d3_geo_centroidZ0;
4827 }
4828 function d3_geo_pathCentroidLineStart() {
4829 var x0, y0;
4830 d3_geo_pathCentroid.point = function(x, y) {
4831 d3_geo_pathCentroid.point = nextPoint;
4832 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4833 };
4834 function nextPoint(x, y) {
4835 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4836 d3_geo_centroidX1 += z * (x0 + x) / 2;
4837 d3_geo_centroidY1 += z * (y0 + y) / 2;
4838 d3_geo_centroidZ1 += z;
4839 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4840 }
4841 }
4842 function d3_geo_pathCentroidLineEnd() {
4843 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4844 }
4845 function d3_geo_pathCentroidRingStart() {
4846 var x00, y00, x0, y0;
4847 d3_geo_pathCentroid.point = function(x, y) {
4848 d3_geo_pathCentroid.point = nextPoint;
4849 d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4850 };
4851 function nextPoint(x, y) {
4852 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4853 d3_geo_centroidX1 += z * (x0 + x) / 2;
4854 d3_geo_centroidY1 += z * (y0 + y) / 2;
4855 d3_geo_centroidZ1 += z;
4856 z = y0 * x - x0 * y;
4857 d3_geo_centroidX2 += z * (x0 + x);
4858 d3_geo_centroidY2 += z * (y0 + y);
4859 d3_geo_centroidZ2 += z * 3;
4860 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4861 }
4862 d3_geo_pathCentroid.lineEnd = function() {
4863 nextPoint(x00, y00);
4864 };
4865 }
4866 function d3_geo_pathContext(context) {
4867 var pointRadius = 4.5;
4868 var stream = {
4869 point: point,
4870 lineStart: function() {
4871 stream.point = pointLineStart;
4872 },
4873 lineEnd: lineEnd,
4874 polygonStart: function() {
4875 stream.lineEnd = lineEndPolygon;
4876 },
4877 polygonEnd: function() {
4878 stream.lineEnd = lineEnd;
4879 stream.point = point;
4880 },
4881 pointRadius: function(_) {
4882 pointRadius = _;
4883 return stream;
4884 },
4885 result: d3_noop
4886 };
4887 function point(x, y) {
4888 context.moveTo(x + pointRadius, y);
4889 context.arc(x, y, pointRadius, 0, τ);
4890 }
4891 function pointLineStart(x, y) {
4892 context.moveTo(x, y);
4893 stream.point = pointLine;
4894 }
4895 function pointLine(x, y) {
4896 context.lineTo(x, y);
4897 }
4898 function lineEnd() {
4899 stream.point = point;
4900 }
4901 function lineEndPolygon() {
4902 context.closePath();
4903 }
4904 return stream;
4905 }
4906 function d3_geo_resample(project) {
4907 var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
4908 function resample(stream) {
4909 return (maxDepth ? resampleRecursive : resampleNone)(stream);
4910 }
4911 function resampleNone(stream) {
4912 return d3_geo_transformPoint(stream, function(x, y) {
4913 x = project(x, y);
4914 stream.point(x[0], x[1]);
4915 });
4916 }
4917 function resampleRecursive(stream) {
4918 var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
4919 var resample = {
4920 point: point,
4921 lineStart: lineStart,
4922 lineEnd: lineEnd,
4923 polygonStart: function() {
4924 stream.polygonStart();
4925 resample.lineStart = ringStart;
4926 },
4927 polygonEnd: function() {
4928 stream.polygonEnd();
4929 resample.lineStart = lineStart;
4930 }
4931 };
4932 function point(x, y) {
4933 x = project(x, y);
4934 stream.point(x[0], x[1]);
4935 }
4936 function lineStart() {
4937 x0 = NaN;
4938 resample.point = linePoint;
4939 stream.lineStart();
4940 }
4941 function linePoint(λ, φ) {
4942 var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
4943 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);
4944 stream.point(x0, y0);
4945 }
4946 function lineEnd() {
4947 resample.point = point;
4948 stream.lineEnd();
4949 }
4950 function ringStart() {
4951 lineStart();
4952 resample.point = ringPoint;
4953 resample.lineEnd = ringEnd;
4954 }
4955 function ringPoint(λ, φ) {
4956 linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4957 resample.point = linePoint;
4958 }
4959 function ringEnd() {
4960 resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4961 resample.lineEnd = lineEnd;
4962 lineEnd();
4963 }
4964 return resample;
4965 }
4966 function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4967 var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
4968 if (d2 > 4 * δ2 && depth--) {
4969 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;
4970 if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
4971 resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4972 stream.point(x2, y2);
4973 resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4974 }
4975 }
4976 }
4977 resample.precision = function(_) {
4978 if (!arguments.length) return Math.sqrt(δ2);
4979 maxDepth = (δ2 = _ * _) > 0 && 16;
4980 return resample;
4981 };
4982 return resample;
4983 }
4984 d3.geo.path = function() {
4985 var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
4986 function path(object) {
4987 if (object) {
4988 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4989 if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4990 d3.geo.stream(object, cacheStream);
4991 }
4992 return contextStream.result();
4993 }
4994 path.area = function(object) {
4995 d3_geo_pathAreaSum = 0;
4996 d3.geo.stream(object, projectStream(d3_geo_pathArea));
4997 return d3_geo_pathAreaSum;
4998 };
4999 path.centroid = function(object) {
5000 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;
5001 d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
5002 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 ];
5003 };
5004 path.bounds = function(object) {
5005 d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
5006 d3.geo.stream(object, projectStream(d3_geo_pathBounds));
5007 return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
5008 };
5009 path.projection = function(_) {
5010 if (!arguments.length) return projection;
5011 projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
5012 return reset();
5013 };
5014 path.context = function(_) {
5015 if (!arguments.length) return context;
5016 contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
5017 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
5018 return reset();
5019 };
5020 path.pointRadius = function(_) {
5021 if (!arguments.length) return pointRadius;
5022 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
5023 return path;
5024 };
5025 function reset() {
5026 cacheStream = null;
5027 return path;
5028 }
5029 return path.projection(d3.geo.albersUsa()).context(null);
5030 };
5031 function d3_geo_pathProjectStream(project) {
5032 var resample = d3_geo_resample(function(x, y) {
5033 return project([ x * d3_degrees, y * d3_degrees ]);
5034 });
5035 return function(stream) {
5036 return d3_geo_projectionRadians(resample(stream));
5037 };
5038 }
5039 d3.geo.transform = function(methods) {
5040 return {
5041 stream: function(stream) {
5042 var transform = new d3_geo_transform(stream);
5043 for (var k in methods) transform[k] = methods[k];
5044 return transform;
5045 }
5046 };
5047 };
5048 function d3_geo_transform(stream) {
5049 this.stream = stream;
5050 }
5051 d3_geo_transform.prototype = {
5052 point: function(x, y) {
5053 this.stream.point(x, y);
5054 },
5055 sphere: function() {
5056 this.stream.sphere();
5057 },
5058 lineStart: function() {
5059 this.stream.lineStart();
5060 },
5061 lineEnd: function() {
5062 this.stream.lineEnd();
5063 },
5064 polygonStart: function() {
5065 this.stream.polygonStart();
5066 },
5067 polygonEnd: function() {
5068 this.stream.polygonEnd();
5069 }
5070 };
5071 function d3_geo_transformPoint(stream, point) {
5072 return {
5073 point: point,
5074 sphere: function() {
5075 stream.sphere();
5076 },
5077 lineStart: function() {
5078 stream.lineStart();
5079 },
5080 lineEnd: function() {
5081 stream.lineEnd();
5082 },
5083 polygonStart: function() {
5084 stream.polygonStart();
5085 },
5086 polygonEnd: function() {
5087 stream.polygonEnd();
5088 }
5089 };
5090 }
5091 d3.geo.projection = d3_geo_projection;
5092 d3.geo.projectionMutator = d3_geo_projectionMutator;
5093 function d3_geo_projection(project) {
5094 return d3_geo_projectionMutator(function() {
5095 return project;
5096 })();
5097 }
5098 function d3_geo_projectionMutator(projectAt) {
5099 var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
5100 x = project(x, y);
5101 return [ x[0] * k + δx, δy - x[1] * k ];
5102 }), 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;
5103 function projection(point) {
5104 point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
5105 return [ point[0] * k + δx, δy - point[1] * k ];
5106 }
5107 function invert(point) {
5108 point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
5109 return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
5110 }
5111 projection.stream = function(output) {
5112 if (stream) stream.valid = false;
5113 stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
5114 stream.valid = true;
5115 return stream;
5116 };
5117 projection.clipAngle = function(_) {
5118 if (!arguments.length) return clipAngle;
5119 preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
5120 return invalidate();
5121 };
5122 projection.clipExtent = function(_) {
5123 if (!arguments.length) return clipExtent;
5124 clipExtent = _;
5125 postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
5126 return invalidate();
5127 };
5128 projection.scale = function(_) {
5129 if (!arguments.length) return k;
5130 k = +_;
5131 return reset();
5132 };
5133 projection.translate = function(_) {
5134 if (!arguments.length) return [ x, y ];
5135 x = +_[0];
5136 y = +_[1];
5137 return reset();
5138 };
5139 projection.center = function(_) {
5140 if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
5141 λ = _[0] % 360 * d3_radians;
5142 φ = _[1] % 360 * d3_radians;
5143 return reset();
5144 };
5145 projection.rotate = function(_) {
5146 if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
5147 δλ = _[0] % 360 * d3_radians;
5148 δφ = _[1] % 360 * d3_radians;
5149 δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
5150 return reset();
5151 };
5152 d3.rebind(projection, projectResample, "precision");
5153 function reset() {
5154 projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
5155 var center = project(λ, φ);
5156 δx = x - center[0] * k;
5157 δy = y + center[1] * k;
5158 return invalidate();
5159 }
5160 function invalidate() {
5161 if (stream) stream.valid = false, stream = null;
5162 return projection;
5163 }
5164 return function() {
5165 project = projectAt.apply(this, arguments);
5166 projection.invert = project.invert && invert;
5167 return reset();
5168 };
5169 }
5170 function d3_geo_projectionRadians(stream) {
5171 return d3_geo_transformPoint(stream, function(x, y) {
5172 stream.point(x * d3_radians, y * d3_radians);
5173 });
5174 }
5175 function d3_geo_equirectangular(λ, φ) {
5176 return [ λ, φ ];
5177 }
5178 (d3.geo.equirectangular = function() {
5179 return d3_geo_projection(d3_geo_equirectangular);
5180 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
5181 d3.geo.rotation = function(rotate) {
5182 rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
5183 function forward(coordinates) {
5184 coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
5185 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
5186 }
5187 forward.invert = function(coordinates) {
5188 coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
5189 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
5190 };
5191 return forward;
5192 };
5193 function d3_geo_identityRotation(λ, φ) {
5194 return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
5195 }
5196 d3_geo_identityRotation.invert = d3_geo_equirectangular;
5197 function d3_geo_rotation(δλ, δφ, δγ) {
5198 return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
5199 }
5200 function d3_geo_forwardRotationλ(δλ) {
5201 return function(λ, φ) {
5202 return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
5203 };
5204 }
5205 function d3_geo_rotationλ(δλ) {
5206 var rotation = d3_geo_forwardRotationλ(δλ);
5207 rotation.invert = d3_geo_forwardRotationλ(-δλ);
5208 return rotation;
5209 }
5210 function d3_geo_rotationφγ(δφ, δγ) {
5211 var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
5212 function rotation(λ, φ) {
5213 var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
5214 return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
5215 }
5216 rotation.invert = function(λ, φ) {
5217 var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
5218 return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
5219 };
5220 return rotation;
5221 }
5222 d3.geo.circle = function() {
5223 var origin = [ 0, 0 ], angle, precision = 6, interpolate;
5224 function circle() {
5225 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 = [];
5226 interpolate(null, null, 1, {
5227 point: function(x, y) {
5228 ring.push(x = rotate(x, y));
5229 x[0] *= d3_degrees, x[1] *= d3_degrees;
5230 }
5231 });
5232 return {
5233 type: "Polygon",
5234 coordinates: [ ring ]
5235 };
5236 }
5237 circle.origin = function(x) {
5238 if (!arguments.length) return origin;
5239 origin = x;
5240 return circle;
5241 };
5242 circle.angle = function(x) {
5243 if (!arguments.length) return angle;
5244 interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
5245 return circle;
5246 };
5247 circle.precision = function(_) {
5248 if (!arguments.length) return precision;
5249 interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
5250 return circle;
5251 };
5252 return circle.angle(90);
5253 };
5254 function d3_geo_circleInterpolate(radius, precision) {
5255 var cr = Math.cos(radius), sr = Math.sin(radius);
5256 return function(from, to, direction, listener) {
5257 var step = direction * precision;
5258 if (from != null) {
5259 from = d3_geo_circleAngle(cr, from);
5260 to = d3_geo_circleAngle(cr, to);
5261 if (direction > 0 ? from < to : from > to) from += direction * τ;
5262 } else {
5263 from = radius + direction * τ;
5264 to = radius - .5 * step;
5265 }
5266 for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
5267 listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
5268 }
5269 };
5270 }
5271 function d3_geo_circleAngle(cr, point) {
5272 var a = d3_geo_cartesian(point);
5273 a[0] -= cr;
5274 d3_geo_cartesianNormalize(a);
5275 var angle = d3_acos(-a[1]);
5276 return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
5277 }
5278 d3.geo.distance = function(a, b) {
5279 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;
5280 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Δλ);
5281 };
5282 d3.geo.graticule = function() {
5283 var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
5284 function graticule() {
5285 return {
5286 type: "MultiLineString",
5287 coordinates: lines()
5288 };
5289 }
5290 function lines() {
5291 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) {
5292 return abs(x % DX) > ε;
5293 }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
5294 return abs(y % DY) > ε;
5295 }).map(y));
5296 }
5297 graticule.lines = function() {
5298 return lines().map(function(coordinates) {
5299 return {
5300 type: "LineString",
5301 coordinates: coordinates
5302 };
5303 });
5304 };
5305 graticule.outline = function() {
5306 return {
5307 type: "Polygon",
5308 coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
5309 };
5310 };
5311 graticule.extent = function(_) {
5312 if (!arguments.length) return graticule.minorExtent();
5313 return graticule.majorExtent(_).minorExtent(_);
5314 };
5315 graticule.majorExtent = function(_) {
5316 if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
5317 X0 = +_[0][0], X1 = +_[1][0];
5318 Y0 = +_[0][1], Y1 = +_[1][1];
5319 if (X0 > X1) _ = X0, X0 = X1, X1 = _;
5320 if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
5321 return graticule.precision(precision);
5322 };
5323 graticule.minorExtent = function(_) {
5324 if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
5325 x0 = +_[0][0], x1 = +_[1][0];
5326 y0 = +_[0][1], y1 = +_[1][1];
5327 if (x0 > x1) _ = x0, x0 = x1, x1 = _;
5328 if (y0 > y1) _ = y0, y0 = y1, y1 = _;
5329 return graticule.precision(precision);
5330 };
5331 graticule.step = function(_) {
5332 if (!arguments.length) return graticule.minorStep();
5333 return graticule.majorStep(_).minorStep(_);
5334 };
5335 graticule.majorStep = function(_) {
5336 if (!arguments.length) return [ DX, DY ];
5337 DX = +_[0], DY = +_[1];
5338 return graticule;
5339 };
5340 graticule.minorStep = function(_) {
5341 if (!arguments.length) return [ dx, dy ];
5342 dx = +_[0], dy = +_[1];
5343 return graticule;
5344 };
5345 graticule.precision = function(_) {
5346 if (!arguments.length) return precision;
5347 precision = +_;
5348 x = d3_geo_graticuleX(y0, y1, 90);
5349 y = d3_geo_graticuleY(x0, x1, precision);
5350 X = d3_geo_graticuleX(Y0, Y1, 90);
5351 Y = d3_geo_graticuleY(X0, X1, precision);
5352 return graticule;
5353 };
5354 return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
5355 };
5356 function d3_geo_graticuleX(y0, y1, dy) {
5357 var y = d3.range(y0, y1 - ε, dy).concat(y1);
5358 return function(x) {
5359 return y.map(function(y) {
5360 return [ x, y ];
5361 });
5362 };
5363 }
5364 function d3_geo_graticuleY(x0, x1, dx) {
5365 var x = d3.range(x0, x1 - ε, dx).concat(x1);
5366 return function(y) {
5367 return x.map(function(x) {
5368 return [ x, y ];
5369 });
5370 };
5371 }
5372 function d3_source(d) {
5373 return d.source;
5374 }
5375 function d3_target(d) {
5376 return d.target;
5377 }
5378 d3.geo.greatArc = function() {
5379 var source = d3_source, source_, target = d3_target, target_;
5380 function greatArc() {
5381 return {
5382 type: "LineString",
5383 coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
5384 };
5385 }
5386 greatArc.distance = function() {
5387 return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
5388 };
5389 greatArc.source = function(_) {
5390 if (!arguments.length) return source;
5391 source = _, source_ = typeof _ === "function" ? null : _;
5392 return greatArc;
5393 };
5394 greatArc.target = function(_) {
5395 if (!arguments.length) return target;
5396 target = _, target_ = typeof _ === "function" ? null : _;
5397 return greatArc;
5398 };
5399 greatArc.precision = function() {
5400 return arguments.length ? greatArc : 0;
5401 };
5402 return greatArc;
5403 };
5404 d3.geo.interpolate = function(source, target) {
5405 return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
5406 };
5407 function d3_geo_interpolate(x0, y0, x1, y1) {
5408 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);
5409 var interpolate = d ? function(t) {
5410 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;
5411 return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
5412 } : function() {
5413 return [ x0 * d3_degrees, y0 * d3_degrees ];
5414 };
5415 interpolate.distance = d;
5416 return interpolate;
5417 }
5418 d3.geo.length = function(object) {
5419 d3_geo_lengthSum = 0;
5420 d3.geo.stream(object, d3_geo_length);
5421 return d3_geo_lengthSum;
5422 };
5423 var d3_geo_lengthSum;
5424 var d3_geo_length = {
5425 sphere: d3_noop,
5426 point: d3_noop,
5427 lineStart: d3_geo_lengthLineStart,
5428 lineEnd: d3_noop,
5429 polygonStart: d3_noop,
5430 polygonEnd: d3_noop
5431 };
5432 function d3_geo_lengthLineStart() {
5433 var λ0, sinφ0, cosφ0;
5434 d3_geo_length.point = function(λ, φ) {
5435 λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
5436 d3_geo_length.point = nextPoint;
5437 };
5438 d3_geo_length.lineEnd = function() {
5439 d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
5440 };
5441 function nextPoint(λ, φ) {
5442 var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
5443 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Δλ);
5444 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
5445 }
5446 }
5447 function d3_geo_azimuthal(scale, angle) {
5448 function azimuthal(λ, φ) {
5449 var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
5450 return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
5451 }
5452 azimuthal.invert = function(x, y) {
5453 var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
5454 return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
5455 };
5456 return azimuthal;
5457 }
5458 var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
5459 return Math.sqrt(2 / (1 + cosλcosφ));
5460 }, function(ρ) {
5461 return 2 * Math.asin(ρ / 2);
5462 });
5463 (d3.geo.azimuthalEqualArea = function() {
5464 return d3_geo_projection(d3_geo_azimuthalEqualArea);
5465 }).raw = d3_geo_azimuthalEqualArea;
5466 var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
5467 var c = Math.acos(cosλcosφ);
5468 return c && c / Math.sin(c);
5469 }, d3_identity);
5470 (d3.geo.azimuthalEquidistant = function() {
5471 return d3_geo_projection(d3_geo_azimuthalEquidistant);
5472 }).raw = d3_geo_azimuthalEquidistant;
5473 function d3_geo_conicConformal(φ0, φ1) {
5474 var cosφ0 = Math.cos(φ0), t = function(φ) {
5475 return Math.tan(π / 4 + φ / 2);
5476 }, 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;
5477 if (!n) return d3_geo_mercator;
5478 function forward(λ, φ) {
5479 if (F > 0) {
5480 if (φ < -halfπ + ε) φ = -halfπ + ε;
5481 } else {
5482 if (φ > halfπ - ε) φ = halfπ - ε;
5483 }
5484 var ρ = F / Math.pow(t(φ), n);
5485 return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
5486 }
5487 forward.invert = function(x, y) {
5488 var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
5489 return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
5490 };
5491 return forward;
5492 }
5493 (d3.geo.conicConformal = function() {
5494 return d3_geo_conic(d3_geo_conicConformal);
5495 }).raw = d3_geo_conicConformal;
5496 function d3_geo_conicEquidistant(φ0, φ1) {
5497 var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
5498 if (abs(n) < ε) return d3_geo_equirectangular;
5499 function forward(λ, φ) {
5500 var ρ = G - φ;
5501 return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
5502 }
5503 forward.invert = function(x, y) {
5504 var ρ0_y = G - y;
5505 return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
5506 };
5507 return forward;
5508 }
5509 (d3.geo.conicEquidistant = function() {
5510 return d3_geo_conic(d3_geo_conicEquidistant);
5511 }).raw = d3_geo_conicEquidistant;
5512 var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
5513 return 1 / cosλcosφ;
5514 }, Math.atan);
5515 (d3.geo.gnomonic = function() {
5516 return d3_geo_projection(d3_geo_gnomonic);
5517 }).raw = d3_geo_gnomonic;
5518 function d3_geo_mercator(λ, φ) {
5519 return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
5520 }
5521 d3_geo_mercator.invert = function(x, y) {
5522 return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
5523 };
5524 function d3_geo_mercatorProjection(project) {
5525 var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
5526 m.scale = function() {
5527 var v = scale.apply(m, arguments);
5528 return v === m ? clipAuto ? m.clipExtent(null) : m : v;
5529 };
5530 m.translate = function() {
5531 var v = translate.apply(m, arguments);
5532 return v === m ? clipAuto ? m.clipExtent(null) : m : v;
5533 };
5534 m.clipExtent = function(_) {
5535 var v = clipExtent.apply(m, arguments);
5536 if (v === m) {
5537 if (clipAuto = _ == null) {
5538 var k = π * scale(), t = translate();
5539 clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
5540 }
5541 } else if (clipAuto) {
5542 v = null;
5543 }
5544 return v;
5545 };
5546 return m.clipExtent(null);
5547 }
5548 (d3.geo.mercator = function() {
5549 return d3_geo_mercatorProjection(d3_geo_mercator);
5550 }).raw = d3_geo_mercator;
5551 var d3_geo_orthographic = d3_geo_azimuthal(function() {
5552 return 1;
5553 }, Math.asin);
5554 (d3.geo.orthographic = function() {
5555 return d3_geo_projection(d3_geo_orthographic);
5556 }).raw = d3_geo_orthographic;
5557 var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
5558 return 1 / (1 + cosλcosφ);
5559 }, function(ρ) {
5560 return 2 * Math.atan(ρ);
5561 });
5562 (d3.geo.stereographic = function() {
5563 return d3_geo_projection(d3_geo_stereographic);
5564 }).raw = d3_geo_stereographic;
5565 function d3_geo_transverseMercator(λ, φ) {
5566 return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
5567 }
5568 d3_geo_transverseMercator.invert = function(x, y) {
5569 return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
5570 };
5571 (d3.geo.transverseMercator = function() {
5572 var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
5573 projection.center = function(_) {
5574 return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]);
5575 };
5576 projection.rotate = function(_) {
5577 return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
5578 [ _[0], _[1], _[2] - 90 ]);
5579 };
5580 return rotate([ 0, 0, 90 ]);
5581 }).raw = d3_geo_transverseMercator;
5582 d3.geom = {};
5583 function d3_geom_pointX(d) {
5584 return d[0];
5585 }
5586 function d3_geom_pointY(d) {
5587 return d[1];
5588 }
5589 d3.geom.hull = function(vertices) {
5590 var x = d3_geom_pointX, y = d3_geom_pointY;
5591 if (arguments.length) return hull(vertices);
5592 function hull(data) {
5593 if (data.length < 3) return [];
5594 var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
5595 for (i = 0; i < n; i++) {
5596 points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
5597 }
5598 points.sort(d3_geom_hullOrder);
5599 for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
5600 var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
5601 var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
5602 for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
5603 for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
5604 return polygon;
5605 }
5606 hull.x = function(_) {
5607 return arguments.length ? (x = _, hull) : x;
5608 };
5609 hull.y = function(_) {
5610 return arguments.length ? (y = _, hull) : y;
5611 };
5612 return hull;
5613 };
5614 function d3_geom_hullUpper(points) {
5615 var n = points.length, hull = [ 0, 1 ], hs = 2;
5616 for (var i = 2; i < n; i++) {
5617 while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
5618 hull[hs++] = i;
5619 }
5620 return hull.slice(0, hs);
5621 }
5622 function d3_geom_hullOrder(a, b) {
5623 return a[0] - b[0] || a[1] - b[1];
5624 }
5625 d3.geom.polygon = function(coordinates) {
5626 d3_subclass(coordinates, d3_geom_polygonPrototype);
5627 return coordinates;
5628 };
5629 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
5630 d3_geom_polygonPrototype.area = function() {
5631 var i = -1, n = this.length, a, b = this[n - 1], area = 0;
5632 while (++i < n) {
5633 a = b;
5634 b = this[i];
5635 area += a[1] * b[0] - a[0] * b[1];
5636 }
5637 return area * .5;
5638 };
5639 d3_geom_polygonPrototype.centroid = function(k) {
5640 var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
5641 if (!arguments.length) k = -1 / (6 * this.area());
5642 while (++i < n) {
5643 a = b;
5644 b = this[i];
5645 c = a[0] * b[1] - b[0] * a[1];
5646 x += (a[0] + b[0]) * c;
5647 y += (a[1] + b[1]) * c;
5648 }
5649 return [ x * k, y * k ];
5650 };
5651 d3_geom_polygonPrototype.clip = function(subject) {
5652 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;
5653 while (++i < n) {
5654 input = subject.slice();
5655 subject.length = 0;
5656 b = this[i];
5657 c = input[(m = input.length - closed) - 1];
5658 j = -1;
5659 while (++j < m) {
5660 d = input[j];
5661 if (d3_geom_polygonInside(d, a, b)) {
5662 if (!d3_geom_polygonInside(c, a, b)) {
5663 subject.push(d3_geom_polygonIntersect(c, d, a, b));
5664 }
5665 subject.push(d);
5666 } else if (d3_geom_polygonInside(c, a, b)) {
5667 subject.push(d3_geom_polygonIntersect(c, d, a, b));
5668 }
5669 c = d;
5670 }
5671 if (closed) subject.push(subject[0]);
5672 a = b;
5673 }
5674 return subject;
5675 };
5676 function d3_geom_polygonInside(p, a, b) {
5677 return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
5678 }
5679 function d3_geom_polygonIntersect(c, d, a, b) {
5680 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);
5681 return [ x1 + ua * x21, y1 + ua * y21 ];
5682 }
5683 function d3_geom_polygonClosed(coordinates) {
5684 var a = coordinates[0], b = coordinates[coordinates.length - 1];
5685 return !(a[0] - b[0] || a[1] - b[1]);
5686 }
5687 var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
5688 function d3_geom_voronoiBeach() {
5689 d3_geom_voronoiRedBlackNode(this);
5690 this.edge = this.site = this.circle = null;
5691 }
5692 function d3_geom_voronoiCreateBeach(site) {
5693 var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
5694 beach.site = site;
5695 return beach;
5696 }
5697 function d3_geom_voronoiDetachBeach(beach) {
5698 d3_geom_voronoiDetachCircle(beach);
5699 d3_geom_voronoiBeaches.remove(beach);
5700 d3_geom_voronoiBeachPool.push(beach);
5701 d3_geom_voronoiRedBlackNode(beach);
5702 }
5703 function d3_geom_voronoiRemoveBeach(beach) {
5704 var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
5705 x: x,
5706 y: y
5707 }, previous = beach.P, next = beach.N, disappearing = [ beach ];
5708 d3_geom_voronoiDetachBeach(beach);
5709 var lArc = previous;
5710 while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
5711 previous = lArc.P;
5712 disappearing.unshift(lArc);
5713 d3_geom_voronoiDetachBeach(lArc);
5714 lArc = previous;
5715 }
5716 disappearing.unshift(lArc);
5717 d3_geom_voronoiDetachCircle(lArc);
5718 var rArc = next;
5719 while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
5720 next = rArc.N;
5721 disappearing.push(rArc);
5722 d3_geom_voronoiDetachBeach(rArc);
5723 rArc = next;
5724 }
5725 disappearing.push(rArc);
5726 d3_geom_voronoiDetachCircle(rArc);
5727 var nArcs = disappearing.length, iArc;
5728 for (iArc = 1; iArc < nArcs; ++iArc) {
5729 rArc = disappearing[iArc];
5730 lArc = disappearing[iArc - 1];
5731 d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
5732 }
5733 lArc = disappearing[0];
5734 rArc = disappearing[nArcs - 1];
5735 rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
5736 d3_geom_voronoiAttachCircle(lArc);
5737 d3_geom_voronoiAttachCircle(rArc);
5738 }
5739 function d3_geom_voronoiAddBeach(site) {
5740 var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
5741 while (node) {
5742 dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
5743 if (dxl > ε) node = node.L; else {
5744 dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
5745 if (dxr > ε) {
5746 if (!node.R) {
5747 lArc = node;
5748 break;
5749 }
5750 node = node.R;
5751 } else {
5752 if (dxl > -ε) {
5753 lArc = node.P;
5754 rArc = node;
5755 } else if (dxr > -ε) {
5756 lArc = node;
5757 rArc = node.N;
5758 } else {
5759 lArc = rArc = node;
5760 }
5761 break;
5762 }
5763 }
5764 }
5765 var newArc = d3_geom_voronoiCreateBeach(site);
5766 d3_geom_voronoiBeaches.insert(lArc, newArc);
5767 if (!lArc && !rArc) return;
5768 if (lArc === rArc) {
5769 d3_geom_voronoiDetachCircle(lArc);
5770 rArc = d3_geom_voronoiCreateBeach(lArc.site);
5771 d3_geom_voronoiBeaches.insert(newArc, rArc);
5772 newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
5773 d3_geom_voronoiAttachCircle(lArc);
5774 d3_geom_voronoiAttachCircle(rArc);
5775 return;
5776 }
5777 if (!rArc) {
5778 newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
5779 return;
5780 }
5781 d3_geom_voronoiDetachCircle(lArc);
5782 d3_geom_voronoiDetachCircle(rArc);
5783 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 = {
5784 x: (cy * hb - by * hc) / d + ax,
5785 y: (bx * hc - cx * hb) / d + ay
5786 };
5787 d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
5788 newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
5789 rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
5790 d3_geom_voronoiAttachCircle(lArc);
5791 d3_geom_voronoiAttachCircle(rArc);
5792 }
5793 function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
5794 var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
5795 if (!pby2) return rfocx;
5796 var lArc = arc.P;
5797 if (!lArc) return -Infinity;
5798 site = lArc.site;
5799 var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
5800 if (!plby2) return lfocx;
5801 var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
5802 if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
5803 return (rfocx + lfocx) / 2;
5804 }
5805 function d3_geom_voronoiRightBreakPoint(arc, directrix) {
5806 var rArc = arc.N;
5807 if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
5808 var site = arc.site;
5809 return site.y === directrix ? site.x : Infinity;
5810 }
5811 function d3_geom_voronoiCell(site) {
5812 this.site = site;
5813 this.edges = [];
5814 }
5815 d3_geom_voronoiCell.prototype.prepare = function() {
5816 var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
5817 while (iHalfEdge--) {
5818 edge = halfEdges[iHalfEdge].edge;
5819 if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
5820 }
5821 halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
5822 return halfEdges.length;
5823 };
5824 function d3_geom_voronoiCloseCells(extent) {
5825 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;
5826 while (iCell--) {
5827 cell = cells[iCell];
5828 if (!cell || !cell.prepare()) continue;
5829 halfEdges = cell.edges;
5830 nHalfEdges = halfEdges.length;
5831 iHalfEdge = 0;
5832 while (iHalfEdge < nHalfEdges) {
5833 end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
5834 start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
5835 if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
5836 halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
5837 x: x0,
5838 y: abs(x2 - x0) < ε ? y2 : y1
5839 } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
5840 x: abs(y2 - y1) < ε ? x2 : x1,
5841 y: y1
5842 } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
5843 x: x1,
5844 y: abs(x2 - x1) < ε ? y2 : y0
5845 } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
5846 x: abs(y2 - y0) < ε ? x2 : x0,
5847 y: y0
5848 } : null), cell.site, null));
5849 ++nHalfEdges;
5850 }
5851 }
5852 }
5853 }
5854 function d3_geom_voronoiHalfEdgeOrder(a, b) {
5855 return b.angle - a.angle;
5856 }
5857 function d3_geom_voronoiCircle() {
5858 d3_geom_voronoiRedBlackNode(this);
5859 this.x = this.y = this.arc = this.site = this.cy = null;
5860 }
5861 function d3_geom_voronoiAttachCircle(arc) {
5862 var lArc = arc.P, rArc = arc.N;
5863 if (!lArc || !rArc) return;
5864 var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
5865 if (lSite === rSite) return;
5866 var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
5867 var d = 2 * (ax * cy - ay * cx);
5868 if (d >= -ε2) return;
5869 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;
5870 var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
5871 circle.arc = arc;
5872 circle.site = cSite;
5873 circle.x = x + bx;
5874 circle.y = cy + Math.sqrt(x * x + y * y);
5875 circle.cy = cy;
5876 arc.circle = circle;
5877 var before = null, node = d3_geom_voronoiCircles._;
5878 while (node) {
5879 if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
5880 if (node.L) node = node.L; else {
5881 before = node.P;
5882 break;
5883 }
5884 } else {
5885 if (node.R) node = node.R; else {
5886 before = node;
5887 break;
5888 }
5889 }
5890 }
5891 d3_geom_voronoiCircles.insert(before, circle);
5892 if (!before) d3_geom_voronoiFirstCircle = circle;
5893 }
5894 function d3_geom_voronoiDetachCircle(arc) {
5895 var circle = arc.circle;
5896 if (circle) {
5897 if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
5898 d3_geom_voronoiCircles.remove(circle);
5899 d3_geom_voronoiCirclePool.push(circle);
5900 d3_geom_voronoiRedBlackNode(circle);
5901 arc.circle = null;
5902 }
5903 }
5904 function d3_geom_voronoiClipEdges(extent) {
5905 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;
5906 while (i--) {
5907 e = edges[i];
5908 if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
5909 e.a = e.b = null;
5910 edges.splice(i, 1);
5911 }
5912 }
5913 }
5914 function d3_geom_voronoiConnectEdge(edge, extent) {
5915 var vb = edge.b;
5916 if (vb) return true;
5917 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;
5918 if (ry === ly) {
5919 if (fx < x0 || fx >= x1) return;
5920 if (lx > rx) {
5921 if (!va) va = {
5922 x: fx,
5923 y: y0
5924 }; else if (va.y >= y1) return;
5925 vb = {
5926 x: fx,
5927 y: y1
5928 };
5929 } else {
5930 if (!va) va = {
5931 x: fx,
5932 y: y1
5933 }; else if (va.y < y0) return;
5934 vb = {
5935 x: fx,
5936 y: y0
5937 };
5938 }
5939 } else {
5940 fm = (lx - rx) / (ry - ly);
5941 fb = fy - fm * fx;
5942 if (fm < -1 || fm > 1) {
5943 if (lx > rx) {
5944 if (!va) va = {
5945 x: (y0 - fb) / fm,
5946 y: y0
5947 }; else if (va.y >= y1) return;
5948 vb = {
5949 x: (y1 - fb) / fm,
5950 y: y1
5951 };
5952 } else {
5953 if (!va) va = {
5954 x: (y1 - fb) / fm,
5955 y: y1
5956 }; else if (va.y < y0) return;
5957 vb = {
5958 x: (y0 - fb) / fm,
5959 y: y0
5960 };
5961 }
5962 } else {
5963 if (ly < ry) {
5964 if (!va) va = {
5965 x: x0,
5966 y: fm * x0 + fb
5967 }; else if (va.x >= x1) return;
5968 vb = {
5969 x: x1,
5970 y: fm * x1 + fb
5971 };
5972 } else {
5973 if (!va) va = {
5974 x: x1,
5975 y: fm * x1 + fb
5976 }; else if (va.x < x0) return;
5977 vb = {
5978 x: x0,
5979 y: fm * x0 + fb
5980 };
5981 }
5982 }
5983 }
5984 edge.a = va;
5985 edge.b = vb;
5986 return true;
5987 }
5988 function d3_geom_voronoiEdge(lSite, rSite) {
5989 this.l = lSite;
5990 this.r = rSite;
5991 this.a = this.b = null;
5992 }
5993 function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
5994 var edge = new d3_geom_voronoiEdge(lSite, rSite);
5995 d3_geom_voronoiEdges.push(edge);
5996 if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
5997 if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
5998 d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
5999 d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
6000 return edge;
6001 }
6002 function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
6003 var edge = new d3_geom_voronoiEdge(lSite, null);
6004 edge.a = va;
6005 edge.b = vb;
6006 d3_geom_voronoiEdges.push(edge);
6007 return edge;
6008 }
6009 function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
6010 if (!edge.a && !edge.b) {
6011 edge.a = vertex;
6012 edge.l = lSite;
6013 edge.r = rSite;
6014 } else if (edge.l === rSite) {
6015 edge.b = vertex;
6016 } else {
6017 edge.a = vertex;
6018 }
6019 }
6020 function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
6021 var va = edge.a, vb = edge.b;
6022 this.edge = edge;
6023 this.site = lSite;
6024 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);
6025 }
6026 d3_geom_voronoiHalfEdge.prototype = {
6027 start: function() {
6028 return this.edge.l === this.site ? this.edge.a : this.edge.b;
6029 },
6030 end: function() {
6031 return this.edge.l === this.site ? this.edge.b : this.edge.a;
6032 }
6033 };
6034 function d3_geom_voronoiRedBlackTree() {
6035 this._ = null;
6036 }
6037 function d3_geom_voronoiRedBlackNode(node) {
6038 node.U = node.C = node.L = node.R = node.P = node.N = null;
6039 }
6040 d3_geom_voronoiRedBlackTree.prototype = {
6041 insert: function(after, node) {
6042 var parent, grandpa, uncle;
6043 if (after) {
6044 node.P = after;
6045 node.N = after.N;
6046 if (after.N) after.N.P = node;
6047 after.N = node;
6048 if (after.R) {
6049 after = after.R;
6050 while (after.L) after = after.L;
6051 after.L = node;
6052 } else {
6053 after.R = node;
6054 }
6055 parent = after;
6056 } else if (this._) {
6057 after = d3_geom_voronoiRedBlackFirst(this._);
6058 node.P = null;
6059 node.N = after;
6060 after.P = after.L = node;
6061 parent = after;
6062 } else {
6063 node.P = node.N = null;
6064 this._ = node;
6065 parent = null;
6066 }
6067 node.L = node.R = null;
6068 node.U = parent;
6069 node.C = true;
6070 after = node;
6071 while (parent && parent.C) {
6072 grandpa = parent.U;
6073 if (parent === grandpa.L) {
6074 uncle = grandpa.R;
6075 if (uncle && uncle.C) {
6076 parent.C = uncle.C = false;
6077 grandpa.C = true;
6078 after = grandpa;
6079 } else {
6080 if (after === parent.R) {
6081 d3_geom_voronoiRedBlackRotateLeft(this, parent);
6082 after = parent;
6083 parent = after.U;
6084 }
6085 parent.C = false;
6086 grandpa.C = true;
6087 d3_geom_voronoiRedBlackRotateRight(this, grandpa);
6088 }
6089 } else {
6090 uncle = grandpa.L;
6091 if (uncle && uncle.C) {
6092 parent.C = uncle.C = false;
6093 grandpa.C = true;
6094 after = grandpa;
6095 } else {
6096 if (after === parent.L) {
6097 d3_geom_voronoiRedBlackRotateRight(this, parent);
6098 after = parent;
6099 parent = after.U;
6100 }
6101 parent.C = false;
6102 grandpa.C = true;
6103 d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
6104 }
6105 }
6106 parent = after.U;
6107 }
6108 this._.C = false;
6109 },
6110 remove: function(node) {
6111 if (node.N) node.N.P = node.P;
6112 if (node.P) node.P.N = node.N;
6113 node.N = node.P = null;
6114 var parent = node.U, sibling, left = node.L, right = node.R, next, red;
6115 if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
6116 if (parent) {
6117 if (parent.L === node) parent.L = next; else parent.R = next;
6118 } else {
6119 this._ = next;
6120 }
6121 if (left && right) {
6122 red = next.C;
6123 next.C = node.C;
6124 next.L = left;
6125 left.U = next;
6126 if (next !== right) {
6127 parent = next.U;
6128 next.U = node.U;
6129 node = next.R;
6130 parent.L = node;
6131 next.R = right;
6132 right.U = next;
6133 } else {
6134 next.U = parent;
6135 parent = next;
6136 node = next.R;
6137 }
6138 } else {
6139 red = node.C;
6140 node = next;
6141 }
6142 if (node) node.U = parent;
6143 if (red) return;
6144 if (node && node.C) {
6145 node.C = false;
6146 return;
6147 }
6148 do {
6149 if (node === this._) break;
6150 if (node === parent.L) {
6151 sibling = parent.R;
6152 if (sibling.C) {
6153 sibling.C = false;
6154 parent.C = true;
6155 d3_geom_voronoiRedBlackRotateLeft(this, parent);
6156 sibling = parent.R;
6157 }
6158 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
6159 if (!sibling.R || !sibling.R.C) {
6160 sibling.L.C = false;
6161 sibling.C = true;
6162 d3_geom_voronoiRedBlackRotateRight(this, sibling);
6163 sibling = parent.R;
6164 }
6165 sibling.C = parent.C;
6166 parent.C = sibling.R.C = false;
6167 d3_geom_voronoiRedBlackRotateLeft(this, parent);
6168 node = this._;
6169 break;
6170 }
6171 } else {
6172 sibling = parent.L;
6173 if (sibling.C) {
6174 sibling.C = false;
6175 parent.C = true;
6176 d3_geom_voronoiRedBlackRotateRight(this, parent);
6177 sibling = parent.L;
6178 }
6179 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
6180 if (!sibling.L || !sibling.L.C) {
6181 sibling.R.C = false;
6182 sibling.C = true;
6183 d3_geom_voronoiRedBlackRotateLeft(this, sibling);
6184 sibling = parent.L;
6185 }
6186 sibling.C = parent.C;
6187 parent.C = sibling.L.C = false;
6188 d3_geom_voronoiRedBlackRotateRight(this, parent);
6189 node = this._;
6190 break;
6191 }
6192 }
6193 sibling.C = true;
6194 node = parent;
6195 parent = parent.U;
6196 } while (!node.C);
6197 if (node) node.C = false;
6198 }
6199 };
6200 function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
6201 var p = node, q = node.R, parent = p.U;
6202 if (parent) {
6203 if (parent.L === p) parent.L = q; else parent.R = q;
6204 } else {
6205 tree._ = q;
6206 }
6207 q.U = parent;
6208 p.U = q;
6209 p.R = q.L;
6210 if (p.R) p.R.U = p;
6211 q.L = p;
6212 }
6213 function d3_geom_voronoiRedBlackRotateRight(tree, node) {
6214 var p = node, q = node.L, parent = p.U;
6215 if (parent) {
6216 if (parent.L === p) parent.L = q; else parent.R = q;
6217 } else {
6218 tree._ = q;
6219 }
6220 q.U = parent;
6221 p.U = q;
6222 p.L = q.R;
6223 if (p.L) p.L.U = p;
6224 q.R = p;
6225 }
6226 function d3_geom_voronoiRedBlackFirst(node) {
6227 while (node.L) node = node.L;
6228 return node;
6229 }
6230 function d3_geom_voronoi(sites, bbox) {
6231 var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
6232 d3_geom_voronoiEdges = [];
6233 d3_geom_voronoiCells = new Array(sites.length);
6234 d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
6235 d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
6236 while (true) {
6237 circle = d3_geom_voronoiFirstCircle;
6238 if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
6239 if (site.x !== x0 || site.y !== y0) {
6240 d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
6241 d3_geom_voronoiAddBeach(site);
6242 x0 = site.x, y0 = site.y;
6243 }
6244 site = sites.pop();
6245 } else if (circle) {
6246 d3_geom_voronoiRemoveBeach(circle.arc);
6247 } else {
6248 break;
6249 }
6250 }
6251 if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
6252 var diagram = {
6253 cells: d3_geom_voronoiCells,
6254 edges: d3_geom_voronoiEdges
6255 };
6256 d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
6257 return diagram;
6258 }
6259 function d3_geom_voronoiVertexOrder(a, b) {
6260 return b.y - a.y || b.x - a.x;
6261 }
6262 d3.geom.voronoi = function(points) {
6263 var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
6264 if (points) return voronoi(points);
6265 function voronoi(data) {
6266 var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
6267 d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
6268 var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
6269 var s = e.start();
6270 return [ s.x, s.y ];
6271 }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
6272 polygon.point = data[i];
6273 });
6274 return polygons;
6275 }
6276 function sites(data) {
6277 return data.map(function(d, i) {
6278 return {
6279 x: Math.round(fx(d, i) / ε) * ε,
6280 y: Math.round(fy(d, i) / ε) * ε,
6281 i: i
6282 };
6283 });
6284 }
6285 voronoi.links = function(data) {
6286 return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
6287 return edge.l && edge.r;
6288 }).map(function(edge) {
6289 return {
6290 source: data[edge.l.i],
6291 target: data[edge.r.i]
6292 };
6293 });
6294 };
6295 voronoi.triangles = function(data) {
6296 var triangles = [];
6297 d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
6298 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;
6299 while (++j < m) {
6300 e0 = e1;
6301 s0 = s1;
6302 e1 = edges[j].edge;
6303 s1 = e1.l === site ? e1.r : e1.l;
6304 if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
6305 triangles.push([ data[i], data[s0.i], data[s1.i] ]);
6306 }
6307 }
6308 });
6309 return triangles;
6310 };
6311 voronoi.x = function(_) {
6312 return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
6313 };
6314 voronoi.y = function(_) {
6315 return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
6316 };
6317 voronoi.clipExtent = function(_) {
6318 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
6319 clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
6320 return voronoi;
6321 };
6322 voronoi.size = function(_) {
6323 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
6324 return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
6325 };
6326 return voronoi;
6327 };
6328 var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
6329 function d3_geom_voronoiTriangleArea(a, b, c) {
6330 return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
6331 }
6332 d3.geom.delaunay = function(vertices) {
6333 return d3.geom.voronoi().triangles(vertices);
6334 };
6335 d3.geom.quadtree = function(points, x1, y1, x2, y2) {
6336 var x = d3_geom_pointX, y = d3_geom_pointY, compat;
6337 if (compat = arguments.length) {
6338 x = d3_geom_quadtreeCompatX;
6339 y = d3_geom_quadtreeCompatY;
6340 if (compat === 3) {
6341 y2 = y1;
6342 x2 = x1;
6343 y1 = x1 = 0;
6344 }
6345 return quadtree(points);
6346 }
6347 function quadtree(data) {
6348 var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
6349 if (x1 != null) {
6350 x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
6351 } else {
6352 x2_ = y2_ = -(x1_ = y1_ = Infinity);
6353 xs = [], ys = [];
6354 n = data.length;
6355 if (compat) for (i = 0; i < n; ++i) {
6356 d = data[i];
6357 if (d.x < x1_) x1_ = d.x;
6358 if (d.y < y1_) y1_ = d.y;
6359 if (d.x > x2_) x2_ = d.x;
6360 if (d.y > y2_) y2_ = d.y;
6361 xs.push(d.x);
6362 ys.push(d.y);
6363 } else for (i = 0; i < n; ++i) {
6364 var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
6365 if (x_ < x1_) x1_ = x_;
6366 if (y_ < y1_) y1_ = y_;
6367 if (x_ > x2_) x2_ = x_;
6368 if (y_ > y2_) y2_ = y_;
6369 xs.push(x_);
6370 ys.push(y_);
6371 }
6372 }
6373 var dx = x2_ - x1_, dy = y2_ - y1_;
6374 if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
6375 function insert(n, d, x, y, x1, y1, x2, y2) {
6376 if (isNaN(x) || isNaN(y)) return;
6377 if (n.leaf) {
6378 var nx = n.x, ny = n.y;
6379 if (nx != null) {
6380 if (abs(nx - x) + abs(ny - y) < .01) {
6381 insertChild(n, d, x, y, x1, y1, x2, y2);
6382 } else {
6383 var nPoint = n.point;
6384 n.x = n.y = n.point = null;
6385 insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
6386 insertChild(n, d, x, y, x1, y1, x2, y2);
6387 }
6388 } else {
6389 n.x = x, n.y = y, n.point = d;
6390 }
6391 } else {
6392 insertChild(n, d, x, y, x1, y1, x2, y2);
6393 }
6394 }
6395 function insertChild(n, d, x, y, x1, y1, x2, y2) {
6396 var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
6397 n.leaf = false;
6398 n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
6399 if (right) x1 = xm; else x2 = xm;
6400 if (below) y1 = ym; else y2 = ym;
6401 insert(n, d, x, y, x1, y1, x2, y2);
6402 }
6403 var root = d3_geom_quadtreeNode();
6404 root.add = function(d) {
6405 insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
6406 };
6407 root.visit = function(f) {
6408 d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
6409 };
6410 root.find = function(point) {
6411 return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
6412 };
6413 i = -1;
6414 if (x1 == null) {
6415 while (++i < n) {
6416 insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
6417 }
6418 --i;
6419 } else data.forEach(root.add);
6420 xs = ys = data = d = null;
6421 return root;
6422 }
6423 quadtree.x = function(_) {
6424 return arguments.length ? (x = _, quadtree) : x;
6425 };
6426 quadtree.y = function(_) {
6427 return arguments.length ? (y = _, quadtree) : y;
6428 };
6429 quadtree.extent = function(_) {
6430 if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
6431 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
6432 y2 = +_[1][1];
6433 return quadtree;
6434 };
6435 quadtree.size = function(_) {
6436 if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
6437 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
6438 return quadtree;
6439 };
6440 return quadtree;
6441 };
6442 function d3_geom_quadtreeCompatX(d) {
6443 return d.x;
6444 }
6445 function d3_geom_quadtreeCompatY(d) {
6446 return d.y;
6447 }
6448 function d3_geom_quadtreeNode() {
6449 return {
6450 leaf: true,
6451 nodes: [],
6452 point: null,
6453 x: null,
6454 y: null
6455 };
6456 }
6457 function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
6458 if (!f(node, x1, y1, x2, y2)) {
6459 var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
6460 if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
6461 if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
6462 if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
6463 if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
6464 }
6465 }
6466 function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
6467 var minDistance2 = Infinity, closestPoint;
6468 (function find(node, x1, y1, x2, y2) {
6469 if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
6470 if (point = node.point) {
6471 var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy;
6472 if (distance2 < minDistance2) {
6473 var distance = Math.sqrt(minDistance2 = distance2);
6474 x0 = x - distance, y0 = y - distance;
6475 x3 = x + distance, y3 = y + distance;
6476 closestPoint = point;
6477 }
6478 }
6479 var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
6480 for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
6481 if (node = children[i & 3]) switch (i & 3) {
6482 case 0:
6483 find(node, x1, y1, xm, ym);
6484 break;
6485
6486 case 1:
6487 find(node, xm, y1, x2, ym);
6488 break;
6489
6490 case 2:
6491 find(node, x1, ym, xm, y2);
6492 break;
6493
6494 case 3:
6495 find(node, xm, ym, x2, y2);
6496 break;
6497 }
6498 }
6499 })(root, x0, y0, x3, y3);
6500 return closestPoint;
6501 }
6502 d3.interpolateRgb = d3_interpolateRgb;
6503 function d3_interpolateRgb(a, b) {
6504 a = d3.rgb(a);
6505 b = d3.rgb(b);
6506 var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
6507 return function(t) {
6508 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));
6509 };
6510 }
6511 d3.interpolateObject = d3_interpolateObject;
6512 function d3_interpolateObject(a, b) {
6513 var i = {}, c = {}, k;
6514 for (k in a) {
6515 if (k in b) {
6516 i[k] = d3_interpolate(a[k], b[k]);
6517 } else {
6518 c[k] = a[k];
6519 }
6520 }
6521 for (k in b) {
6522 if (!(k in a)) {
6523 c[k] = b[k];
6524 }
6525 }
6526 return function(t) {
6527 for (k in i) c[k] = i[k](t);
6528 return c;
6529 };
6530 }
6531 d3.interpolateNumber = d3_interpolateNumber;
6532 function d3_interpolateNumber(a, b) {
6533 a = +a, b = +b;
6534 return function(t) {
6535 return a * (1 - t) + b * t;
6536 };
6537 }
6538 d3.interpolateString = d3_interpolateString;
6539 function d3_interpolateString(a, b) {
6540 var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
6541 a = a + "", b = b + "";
6542 while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
6543 if ((bs = bm.index) > bi) {
6544 bs = b.slice(bi, bs);
6545 if (s[i]) s[i] += bs; else s[++i] = bs;
6546 }
6547 if ((am = am[0]) === (bm = bm[0])) {
6548 if (s[i]) s[i] += bm; else s[++i] = bm;
6549 } else {
6550 s[++i] = null;
6551 q.push({
6552 i: i,
6553 x: d3_interpolateNumber(am, bm)
6554 });
6555 }
6556 bi = d3_interpolate_numberB.lastIndex;
6557 }
6558 if (bi < b.length) {
6559 bs = b.slice(bi);
6560 if (s[i]) s[i] += bs; else s[++i] = bs;
6561 }
6562 return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
6563 return b(t) + "";
6564 }) : function() {
6565 return b;
6566 } : (b = q.length, function(t) {
6567 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
6568 return s.join("");
6569 });
6570 }
6571 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
6572 d3.interpolate = d3_interpolate;
6573 function d3_interpolate(a, b) {
6574 var i = d3.interpolators.length, f;
6575 while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
6576 return f;
6577 }
6578 d3.interpolators = [ function(a, b) {
6579 var t = typeof b;
6580 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);
6581 } ];
6582 d3.interpolateArray = d3_interpolateArray;
6583 function d3_interpolateArray(a, b) {
6584 var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
6585 for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
6586 for (;i < na; ++i) c[i] = a[i];
6587 for (;i < nb; ++i) c[i] = b[i];
6588 return function(t) {
6589 for (i = 0; i < n0; ++i) c[i] = x[i](t);
6590 return c;
6591 };
6592 }
6593 var d3_ease_default = function() {
6594 return d3_identity;
6595 };
6596 var d3_ease = d3.map({
6597 linear: d3_ease_default,
6598 poly: d3_ease_poly,
6599 quad: function() {
6600 return d3_ease_quad;
6601 },
6602 cubic: function() {
6603 return d3_ease_cubic;
6604 },
6605 sin: function() {
6606 return d3_ease_sin;
6607 },
6608 exp: function() {
6609 return d3_ease_exp;
6610 },
6611 circle: function() {
6612 return d3_ease_circle;
6613 },
6614 elastic: d3_ease_elastic,
6615 back: d3_ease_back,
6616 bounce: function() {
6617 return d3_ease_bounce;
6618 }
6619 });
6620 var d3_ease_mode = d3.map({
6621 "in": d3_identity,
6622 out: d3_ease_reverse,
6623 "in-out": d3_ease_reflect,
6624 "out-in": function(f) {
6625 return d3_ease_reflect(d3_ease_reverse(f));
6626 }
6627 });
6628 d3.ease = function(name) {
6629 var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
6630 t = d3_ease.get(t) || d3_ease_default;
6631 m = d3_ease_mode.get(m) || d3_identity;
6632 return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
6633 };
6634 function d3_ease_clamp(f) {
6635 return function(t) {
6636 return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
6637 };
6638 }
6639 function d3_ease_reverse(f) {
6640 return function(t) {
6641 return 1 - f(1 - t);
6642 };
6643 }
6644 function d3_ease_reflect(f) {
6645 return function(t) {
6646 return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
6647 };
6648 }
6649 function d3_ease_quad(t) {
6650 return t * t;
6651 }
6652 function d3_ease_cubic(t) {
6653 return t * t * t;
6654 }
6655 function d3_ease_cubicInOut(t) {
6656 if (t <= 0) return 0;
6657 if (t >= 1) return 1;
6658 var t2 = t * t, t3 = t2 * t;
6659 return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
6660 }
6661 function d3_ease_poly(e) {
6662 return function(t) {
6663 return Math.pow(t, e);
6664 };
6665 }
6666 function d3_ease_sin(t) {
6667 return 1 - Math.cos(t * halfπ);
6668 }
6669 function d3_ease_exp(t) {
6670 return Math.pow(2, 10 * (t - 1));
6671 }
6672 function d3_ease_circle(t) {
6673 return 1 - Math.sqrt(1 - t * t);
6674 }
6675 function d3_ease_elastic(a, p) {
6676 var s;
6677 if (arguments.length < 2) p = .45;
6678 if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
6679 return function(t) {
6680 return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
6681 };
6682 }
6683 function d3_ease_back(s) {
6684 if (!s) s = 1.70158;
6685 return function(t) {
6686 return t * t * ((s + 1) * t - s);
6687 };
6688 }
6689 function d3_ease_bounce(t) {
6690 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;
6691 }
6692 d3.interpolateHcl = d3_interpolateHcl;
6693 function d3_interpolateHcl(a, b) {
6694 a = d3.hcl(a);
6695 b = d3.hcl(b);
6696 var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
6697 if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
6698 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
6699 return function(t) {
6700 return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
6701 };
6702 }
6703 d3.interpolateHsl = d3_interpolateHsl;
6704 function d3_interpolateHsl(a, b) {
6705 a = d3.hsl(a);
6706 b = d3.hsl(b);
6707 var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
6708 if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
6709 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
6710 return function(t) {
6711 return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
6712 };
6713 }
6714 d3.interpolateLab = d3_interpolateLab;
6715 function d3_interpolateLab(a, b) {
6716 a = d3.lab(a);
6717 b = d3.lab(b);
6718 var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
6719 return function(t) {
6720 return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
6721 };
6722 }
6723 d3.interpolateRound = d3_interpolateRound;
6724 function d3_interpolateRound(a, b) {
6725 b -= a;
6726 return function(t) {
6727 return Math.round(a + b * t);
6728 };
6729 }
6730 d3.transform = function(string) {
6731 var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
6732 return (d3.transform = function(string) {
6733 if (string != null) {
6734 g.setAttribute("transform", string);
6735 var t = g.transform.baseVal.consolidate();
6736 }
6737 return new d3_transform(t ? t.matrix : d3_transformIdentity);
6738 })(string);
6739 };
6740 function d3_transform(m) {
6741 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;
6742 if (r0[0] * r1[1] < r1[0] * r0[1]) {
6743 r0[0] *= -1;
6744 r0[1] *= -1;
6745 kx *= -1;
6746 kz *= -1;
6747 }
6748 this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
6749 this.translate = [ m.e, m.f ];
6750 this.scale = [ kx, ky ];
6751 this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
6752 }
6753 d3_transform.prototype.toString = function() {
6754 return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
6755 };
6756 function d3_transformDot(a, b) {
6757 return a[0] * b[0] + a[1] * b[1];
6758 }
6759 function d3_transformNormalize(a) {
6760 var k = Math.sqrt(d3_transformDot(a, a));
6761 if (k) {
6762 a[0] /= k;
6763 a[1] /= k;
6764 }
6765 return k;
6766 }
6767 function d3_transformCombine(a, b, k) {
6768 a[0] += k * b[0];
6769 a[1] += k * b[1];
6770 return a;
6771 }
6772 var d3_transformIdentity = {
6773 a: 1,
6774 b: 0,
6775 c: 0,
6776 d: 1,
6777 e: 0,
6778 f: 0
6779 };
6780 d3.interpolateTransform = d3_interpolateTransform;
6781 function d3_interpolateTransformPop(s) {
6782 return s.length ? s.pop() + "," : "";
6783 }
6784 function d3_interpolateTranslate(ta, tb, s, q) {
6785 if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
6786 var i = s.push("translate(", null, ",", null, ")");
6787 q.push({
6788 i: i - 4,
6789 x: d3_interpolateNumber(ta[0], tb[0])
6790 }, {
6791 i: i - 2,
6792 x: d3_interpolateNumber(ta[1], tb[1])
6793 });
6794 } else if (tb[0] || tb[1]) {
6795 s.push("translate(" + tb + ")");
6796 }
6797 }
6798 function d3_interpolateRotate(ra, rb, s, q) {
6799 if (ra !== rb) {
6800 if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
6801 q.push({
6802 i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2,
6803 x: d3_interpolateNumber(ra, rb)
6804 });
6805 } else if (rb) {
6806 s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")");
6807 }
6808 }
6809 function d3_interpolateSkew(wa, wb, s, q) {
6810 if (wa !== wb) {
6811 q.push({
6812 i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2,
6813 x: d3_interpolateNumber(wa, wb)
6814 });
6815 } else if (wb) {
6816 s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")");
6817 }
6818 }
6819 function d3_interpolateScale(ka, kb, s, q) {
6820 if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
6821 var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")");
6822 q.push({
6823 i: i - 4,
6824 x: d3_interpolateNumber(ka[0], kb[0])
6825 }, {
6826 i: i - 2,
6827 x: d3_interpolateNumber(ka[1], kb[1])
6828 });
6829 } else if (kb[0] !== 1 || kb[1] !== 1) {
6830 s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")");
6831 }
6832 }
6833 function d3_interpolateTransform(a, b) {
6834 var s = [], q = [];
6835 a = d3.transform(a), b = d3.transform(b);
6836 d3_interpolateTranslate(a.translate, b.translate, s, q);
6837 d3_interpolateRotate(a.rotate, b.rotate, s, q);
6838 d3_interpolateSkew(a.skew, b.skew, s, q);
6839 d3_interpolateScale(a.scale, b.scale, s, q);
6840 a = b = null;
6841 return function(t) {
6842 var i = -1, n = q.length, o;
6843 while (++i < n) s[(o = q[i]).i] = o.x(t);
6844 return s.join("");
6845 };
6846 }
6847 function d3_uninterpolateNumber(a, b) {
6848 b = (b -= a = +a) || 1 / b;
6849 return function(x) {
6850 return (x - a) / b;
6851 };
6852 }
6853 function d3_uninterpolateClamp(a, b) {
6854 b = (b -= a = +a) || 1 / b;
6855 return function(x) {
6856 return Math.max(0, Math.min(1, (x - a) / b));
6857 };
6858 }
6859 d3.layout = {};
6860 d3.layout.bundle = function() {
6861 return function(links) {
6862 var paths = [], i = -1, n = links.length;
6863 while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
6864 return paths;
6865 };
6866 };
6867 function d3_layout_bundlePath(link) {
6868 var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
6869 while (start !== lca) {
6870 start = start.parent;
6871 points.push(start);
6872 }
6873 var k = points.length;
6874 while (end !== lca) {
6875 points.splice(k, 0, end);
6876 end = end.parent;
6877 }
6878 return points;
6879 }
6880 function d3_layout_bundleAncestors(node) {
6881 var ancestors = [], parent = node.parent;
6882 while (parent != null) {
6883 ancestors.push(node);
6884 node = parent;
6885 parent = parent.parent;
6886 }
6887 ancestors.push(node);
6888 return ancestors;
6889 }
6890 function d3_layout_bundleLeastCommonAncestor(a, b) {
6891 if (a === b) return a;
6892 var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
6893 while (aNode === bNode) {
6894 sharedNode = aNode;
6895 aNode = aNodes.pop();
6896 bNode = bNodes.pop();
6897 }
6898 return sharedNode;
6899 }
6900 d3.layout.chord = function() {
6901 var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
6902 function relayout() {
6903 var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
6904 chords = [];
6905 groups = [];
6906 k = 0, i = -1;
6907 while (++i < n) {
6908 x = 0, j = -1;
6909 while (++j < n) {
6910 x += matrix[i][j];
6911 }
6912 groupSums.push(x);
6913 subgroupIndex.push(d3.range(n));
6914 k += x;
6915 }
6916 if (sortGroups) {
6917 groupIndex.sort(function(a, b) {
6918 return sortGroups(groupSums[a], groupSums[b]);
6919 });
6920 }
6921 if (sortSubgroups) {
6922 subgroupIndex.forEach(function(d, i) {
6923 d.sort(function(a, b) {
6924 return sortSubgroups(matrix[i][a], matrix[i][b]);
6925 });
6926 });
6927 }
6928 k = (τ - padding * n) / k;
6929 x = 0, i = -1;
6930 while (++i < n) {
6931 x0 = x, j = -1;
6932 while (++j < n) {
6933 var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
6934 subgroups[di + "-" + dj] = {
6935 index: di,
6936 subindex: dj,
6937 startAngle: a0,
6938 endAngle: a1,
6939 value: v
6940 };
6941 }
6942 groups[di] = {
6943 index: di,
6944 startAngle: x0,
6945 endAngle: x,
6946 value: groupSums[di]
6947 };
6948 x += padding;
6949 }
6950 i = -1;
6951 while (++i < n) {
6952 j = i - 1;
6953 while (++j < n) {
6954 var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
6955 if (source.value || target.value) {
6956 chords.push(source.value < target.value ? {
6957 source: target,
6958 target: source
6959 } : {
6960 source: source,
6961 target: target
6962 });
6963 }
6964 }
6965 }
6966 if (sortChords) resort();
6967 }
6968 function resort() {
6969 chords.sort(function(a, b) {
6970 return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
6971 });
6972 }
6973 chord.matrix = function(x) {
6974 if (!arguments.length) return matrix;
6975 n = (matrix = x) && matrix.length;
6976 chords = groups = null;
6977 return chord;
6978 };
6979 chord.padding = function(x) {
6980 if (!arguments.length) return padding;
6981 padding = x;
6982 chords = groups = null;
6983 return chord;
6984 };
6985 chord.sortGroups = function(x) {
6986 if (!arguments.length) return sortGroups;
6987 sortGroups = x;
6988 chords = groups = null;
6989 return chord;
6990 };
6991 chord.sortSubgroups = function(x) {
6992 if (!arguments.length) return sortSubgroups;
6993 sortSubgroups = x;
6994 chords = null;
6995 return chord;
6996 };
6997 chord.sortChords = function(x) {
6998 if (!arguments.length) return sortChords;
6999 sortChords = x;
7000 if (chords) resort();
7001 return chord;
7002 };
7003 chord.chords = function() {
7004 if (!chords) relayout();
7005 return chords;
7006 };
7007 chord.groups = function() {
7008 if (!groups) relayout();
7009 return groups;
7010 };
7011 return chord;
7012 };
7013 d3.layout.force = function() {
7014 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;
7015 function repulse(node) {
7016 return function(quad, x1, _, x2) {
7017 if (quad.point !== node) {
7018 var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
7019 if (dw * dw / theta2 < dn) {
7020 if (dn < chargeDistance2) {
7021 var k = quad.charge / dn;
7022 node.px -= dx * k;
7023 node.py -= dy * k;
7024 }
7025 return true;
7026 }
7027 if (quad.point && dn && dn < chargeDistance2) {
7028 var k = quad.pointCharge / dn;
7029 node.px -= dx * k;
7030 node.py -= dy * k;
7031 }
7032 }
7033 return !quad.charge;
7034 };
7035 }
7036 force.tick = function() {
7037 if ((alpha *= .99) < .005) {
7038 timer = null;
7039 event.end({
7040 type: "end",
7041 alpha: alpha = 0
7042 });
7043 return true;
7044 }
7045 var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
7046 for (i = 0; i < m; ++i) {
7047 o = links[i];
7048 s = o.source;
7049 t = o.target;
7050 x = t.x - s.x;
7051 y = t.y - s.y;
7052 if (l = x * x + y * y) {
7053 l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
7054 x *= l;
7055 y *= l;
7056 t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5);
7057 t.y -= y * k;
7058 s.x += x * (k = 1 - k);
7059 s.y += y * k;
7060 }
7061 }
7062 if (k = alpha * gravity) {
7063 x = size[0] / 2;
7064 y = size[1] / 2;
7065 i = -1;
7066 if (k) while (++i < n) {
7067 o = nodes[i];
7068 o.x += (x - o.x) * k;
7069 o.y += (y - o.y) * k;
7070 }
7071 }
7072 if (charge) {
7073 d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
7074 i = -1;
7075 while (++i < n) {
7076 if (!(o = nodes[i]).fixed) {
7077 q.visit(repulse(o));
7078 }
7079 }
7080 }
7081 i = -1;
7082 while (++i < n) {
7083 o = nodes[i];
7084 if (o.fixed) {
7085 o.x = o.px;
7086 o.y = o.py;
7087 } else {
7088 o.x -= (o.px - (o.px = o.x)) * friction;
7089 o.y -= (o.py - (o.py = o.y)) * friction;
7090 }
7091 }
7092 event.tick({
7093 type: "tick",
7094 alpha: alpha
7095 });
7096 };
7097 force.nodes = function(x) {
7098 if (!arguments.length) return nodes;
7099 nodes = x;
7100 return force;
7101 };
7102 force.links = function(x) {
7103 if (!arguments.length) return links;
7104 links = x;
7105 return force;
7106 };
7107 force.size = function(x) {
7108 if (!arguments.length) return size;
7109 size = x;
7110 return force;
7111 };
7112 force.linkDistance = function(x) {
7113 if (!arguments.length) return linkDistance;
7114 linkDistance = typeof x === "function" ? x : +x;
7115 return force;
7116 };
7117 force.distance = force.linkDistance;
7118 force.linkStrength = function(x) {
7119 if (!arguments.length) return linkStrength;
7120 linkStrength = typeof x === "function" ? x : +x;
7121 return force;
7122 };
7123 force.friction = function(x) {
7124 if (!arguments.length) return friction;
7125 friction = +x;
7126 return force;
7127 };
7128 force.charge = function(x) {
7129 if (!arguments.length) return charge;
7130 charge = typeof x === "function" ? x : +x;
7131 return force;
7132 };
7133 force.chargeDistance = function(x) {
7134 if (!arguments.length) return Math.sqrt(chargeDistance2);
7135 chargeDistance2 = x * x;
7136 return force;
7137 };
7138 force.gravity = function(x) {
7139 if (!arguments.length) return gravity;
7140 gravity = +x;
7141 return force;
7142 };
7143 force.theta = function(x) {
7144 if (!arguments.length) return Math.sqrt(theta2);
7145 theta2 = x * x;
7146 return force;
7147 };
7148 force.alpha = function(x) {
7149 if (!arguments.length) return alpha;
7150 x = +x;
7151 if (alpha) {
7152 if (x > 0) {
7153 alpha = x;
7154 } else {
7155 timer.c = null, timer.t = NaN, timer = null;
7156 event.end({
7157 type: "end",
7158 alpha: alpha = 0
7159 });
7160 }
7161 } else if (x > 0) {
7162 event.start({
7163 type: "start",
7164 alpha: alpha = x
7165 });
7166 timer = d3_timer(force.tick);
7167 }
7168 return force;
7169 };
7170 force.start = function() {
7171 var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
7172 for (i = 0; i < n; ++i) {
7173 (o = nodes[i]).index = i;
7174 o.weight = 0;
7175 }
7176 for (i = 0; i < m; ++i) {
7177 o = links[i];
7178 if (typeof o.source == "number") o.source = nodes[o.source];
7179 if (typeof o.target == "number") o.target = nodes[o.target];
7180 ++o.source.weight;
7181 ++o.target.weight;
7182 }
7183 for (i = 0; i < n; ++i) {
7184 o = nodes[i];
7185 if (isNaN(o.x)) o.x = position("x", w);
7186 if (isNaN(o.y)) o.y = position("y", h);
7187 if (isNaN(o.px)) o.px = o.x;
7188 if (isNaN(o.py)) o.py = o.y;
7189 }
7190 distances = [];
7191 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;
7192 strengths = [];
7193 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;
7194 charges = [];
7195 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;
7196 function position(dimension, size) {
7197 if (!neighbors) {
7198 neighbors = new Array(n);
7199 for (j = 0; j < n; ++j) {
7200 neighbors[j] = [];
7201 }
7202 for (j = 0; j < m; ++j) {
7203 var o = links[j];
7204 neighbors[o.source.index].push(o.target);
7205 neighbors[o.target.index].push(o.source);
7206 }
7207 }
7208 var candidates = neighbors[i], j = -1, l = candidates.length, x;
7209 while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x;
7210 return Math.random() * size;
7211 }
7212 return force.resume();
7213 };
7214 force.resume = function() {
7215 return force.alpha(.1);
7216 };
7217 force.stop = function() {
7218 return force.alpha(0);
7219 };
7220 force.drag = function() {
7221 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);
7222 if (!arguments.length) return drag;
7223 this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
7224 };
7225 function dragmove(d) {
7226 d.px = d3.event.x, d.py = d3.event.y;
7227 force.resume();
7228 }
7229 return d3.rebind(force, event, "on");
7230 };
7231 function d3_layout_forceDragstart(d) {
7232 d.fixed |= 2;
7233 }
7234 function d3_layout_forceDragend(d) {
7235 d.fixed &= ~6;
7236 }
7237 function d3_layout_forceMouseover(d) {
7238 d.fixed |= 4;
7239 d.px = d.x, d.py = d.y;
7240 }
7241 function d3_layout_forceMouseout(d) {
7242 d.fixed &= ~4;
7243 }
7244 function d3_layout_forceAccumulate(quad, alpha, charges) {
7245 var cx = 0, cy = 0;
7246 quad.charge = 0;
7247 if (!quad.leaf) {
7248 var nodes = quad.nodes, n = nodes.length, i = -1, c;
7249 while (++i < n) {
7250 c = nodes[i];
7251 if (c == null) continue;
7252 d3_layout_forceAccumulate(c, alpha, charges);
7253 quad.charge += c.charge;
7254 cx += c.charge * c.cx;
7255 cy += c.charge * c.cy;
7256 }
7257 }
7258 if (quad.point) {
7259 if (!quad.leaf) {
7260 quad.point.x += Math.random() - .5;
7261 quad.point.y += Math.random() - .5;
7262 }
7263 var k = alpha * charges[quad.point.index];
7264 quad.charge += quad.pointCharge = k;
7265 cx += k * quad.point.x;
7266 cy += k * quad.point.y;
7267 }
7268 quad.cx = cx / quad.charge;
7269 quad.cy = cy / quad.charge;
7270 }
7271 var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
7272 d3.layout.hierarchy = function() {
7273 var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
7274 function hierarchy(root) {
7275 var stack = [ root ], nodes = [], node;
7276 root.depth = 0;
7277 while ((node = stack.pop()) != null) {
7278 nodes.push(node);
7279 if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
7280 var n, childs, child;
7281 while (--n >= 0) {
7282 stack.push(child = childs[n]);
7283 child.parent = node;
7284 child.depth = node.depth + 1;
7285 }
7286 if (value) node.value = 0;
7287 node.children = childs;
7288 } else {
7289 if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
7290 delete node.children;
7291 }
7292 }
7293 d3_layout_hierarchyVisitAfter(root, function(node) {
7294 var childs, parent;
7295 if (sort && (childs = node.children)) childs.sort(sort);
7296 if (value && (parent = node.parent)) parent.value += node.value;
7297 });
7298 return nodes;
7299 }
7300 hierarchy.sort = function(x) {
7301 if (!arguments.length) return sort;
7302 sort = x;
7303 return hierarchy;
7304 };
7305 hierarchy.children = function(x) {
7306 if (!arguments.length) return children;
7307 children = x;
7308 return hierarchy;
7309 };
7310 hierarchy.value = function(x) {
7311 if (!arguments.length) return value;
7312 value = x;
7313 return hierarchy;
7314 };
7315 hierarchy.revalue = function(root) {
7316 if (value) {
7317 d3_layout_hierarchyVisitBefore(root, function(node) {
7318 if (node.children) node.value = 0;
7319 });
7320 d3_layout_hierarchyVisitAfter(root, function(node) {
7321 var parent;
7322 if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
7323 if (parent = node.parent) parent.value += node.value;
7324 });
7325 }
7326 return root;
7327 };
7328 return hierarchy;
7329 };
7330 function d3_layout_hierarchyRebind(object, hierarchy) {
7331 d3.rebind(object, hierarchy, "sort", "children", "value");
7332 object.nodes = object;
7333 object.links = d3_layout_hierarchyLinks;
7334 return object;
7335 }
7336 function d3_layout_hierarchyVisitBefore(node, callback) {
7337 var nodes = [ node ];
7338 while ((node = nodes.pop()) != null) {
7339 callback(node);
7340 if ((children = node.children) && (n = children.length)) {
7341 var n, children;
7342 while (--n >= 0) nodes.push(children[n]);
7343 }
7344 }
7345 }
7346 function d3_layout_hierarchyVisitAfter(node, callback) {
7347 var nodes = [ node ], nodes2 = [];
7348 while ((node = nodes.pop()) != null) {
7349 nodes2.push(node);
7350 if ((children = node.children) && (n = children.length)) {
7351 var i = -1, n, children;
7352 while (++i < n) nodes.push(children[i]);
7353 }
7354 }
7355 while ((node = nodes2.pop()) != null) {
7356 callback(node);
7357 }
7358 }
7359 function d3_layout_hierarchyChildren(d) {
7360 return d.children;
7361 }
7362 function d3_layout_hierarchyValue(d) {
7363 return d.value;
7364 }
7365 function d3_layout_hierarchySort(a, b) {
7366 return b.value - a.value;
7367 }
7368 function d3_layout_hierarchyLinks(nodes) {
7369 return d3.merge(nodes.map(function(parent) {
7370 return (parent.children || []).map(function(child) {
7371 return {
7372 source: parent,
7373 target: child
7374 };
7375 });
7376 }));
7377 }
7378 d3.layout.partition = function() {
7379 var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
7380 function position(node, x, dx, dy) {
7381 var children = node.children;
7382 node.x = x;
7383 node.y = node.depth * dy;
7384 node.dx = dx;
7385 node.dy = dy;
7386 if (children && (n = children.length)) {
7387 var i = -1, n, c, d;
7388 dx = node.value ? dx / node.value : 0;
7389 while (++i < n) {
7390 position(c = children[i], x, d = c.value * dx, dy);
7391 x += d;
7392 }
7393 }
7394 }
7395 function depth(node) {
7396 var children = node.children, d = 0;
7397 if (children && (n = children.length)) {
7398 var i = -1, n;
7399 while (++i < n) d = Math.max(d, depth(children[i]));
7400 }
7401 return 1 + d;
7402 }
7403 function partition(d, i) {
7404 var nodes = hierarchy.call(this, d, i);
7405 position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
7406 return nodes;
7407 }
7408 partition.size = function(x) {
7409 if (!arguments.length) return size;
7410 size = x;
7411 return partition;
7412 };
7413 return d3_layout_hierarchyRebind(partition, hierarchy);
7414 };
7415 d3.layout.pie = function() {
7416 var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
7417 function pie(data) {
7418 var n = data.length, values = data.map(function(d, i) {
7419 return +value.call(pie, d, i);
7420 }), 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;
7421 if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
7422 return values[j] - values[i];
7423 } : function(i, j) {
7424 return sort(data[i], data[j]);
7425 });
7426 index.forEach(function(i) {
7427 arcs[i] = {
7428 data: data[i],
7429 value: v = values[i],
7430 startAngle: a,
7431 endAngle: a += v * k + pa,
7432 padAngle: p
7433 };
7434 });
7435 return arcs;
7436 }
7437 pie.value = function(_) {
7438 if (!arguments.length) return value;
7439 value = _;
7440 return pie;
7441 };
7442 pie.sort = function(_) {
7443 if (!arguments.length) return sort;
7444 sort = _;
7445 return pie;
7446 };
7447 pie.startAngle = function(_) {
7448 if (!arguments.length) return startAngle;
7449 startAngle = _;
7450 return pie;
7451 };
7452 pie.endAngle = function(_) {
7453 if (!arguments.length) return endAngle;
7454 endAngle = _;
7455 return pie;
7456 };
7457 pie.padAngle = function(_) {
7458 if (!arguments.length) return padAngle;
7459 padAngle = _;
7460 return pie;
7461 };
7462 return pie;
7463 };
7464 var d3_layout_pieSortByValue = {};
7465 d3.layout.stack = function() {
7466 var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
7467 function stack(data, index) {
7468 if (!(n = data.length)) return data;
7469 var series = data.map(function(d, i) {
7470 return values.call(stack, d, i);
7471 });
7472 var points = series.map(function(d) {
7473 return d.map(function(v, i) {
7474 return [ x.call(stack, v, i), y.call(stack, v, i) ];
7475 });
7476 });
7477 var orders = order.call(stack, points, index);
7478 series = d3.permute(series, orders);
7479 points = d3.permute(points, orders);
7480 var offsets = offset.call(stack, points, index);
7481 var m = series[0].length, n, i, j, o;
7482 for (j = 0; j < m; ++j) {
7483 out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
7484 for (i = 1; i < n; ++i) {
7485 out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
7486 }
7487 }
7488 return data;
7489 }
7490 stack.values = function(x) {
7491 if (!arguments.length) return values;
7492 values = x;
7493 return stack;
7494 };
7495 stack.order = function(x) {
7496 if (!arguments.length) return order;
7497 order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
7498 return stack;
7499 };
7500 stack.offset = function(x) {
7501 if (!arguments.length) return offset;
7502 offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
7503 return stack;
7504 };
7505 stack.x = function(z) {
7506 if (!arguments.length) return x;
7507 x = z;
7508 return stack;
7509 };
7510 stack.y = function(z) {
7511 if (!arguments.length) return y;
7512 y = z;
7513 return stack;
7514 };
7515 stack.out = function(z) {
7516 if (!arguments.length) return out;
7517 out = z;
7518 return stack;
7519 };
7520 return stack;
7521 };
7522 function d3_layout_stackX(d) {
7523 return d.x;
7524 }
7525 function d3_layout_stackY(d) {
7526 return d.y;
7527 }
7528 function d3_layout_stackOut(d, y0, y) {
7529 d.y0 = y0;
7530 d.y = y;
7531 }
7532 var d3_layout_stackOrders = d3.map({
7533 "inside-out": function(data) {
7534 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) {
7535 return max[a] - max[b];
7536 }), top = 0, bottom = 0, tops = [], bottoms = [];
7537 for (i = 0; i < n; ++i) {
7538 j = index[i];
7539 if (top < bottom) {
7540 top += sums[j];
7541 tops.push(j);
7542 } else {
7543 bottom += sums[j];
7544 bottoms.push(j);
7545 }
7546 }
7547 return bottoms.reverse().concat(tops);
7548 },
7549 reverse: function(data) {
7550 return d3.range(data.length).reverse();
7551 },
7552 "default": d3_layout_stackOrderDefault
7553 });
7554 var d3_layout_stackOffsets = d3.map({
7555 silhouette: function(data) {
7556 var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
7557 for (j = 0; j < m; ++j) {
7558 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
7559 if (o > max) max = o;
7560 sums.push(o);
7561 }
7562 for (j = 0; j < m; ++j) {
7563 y0[j] = (max - sums[j]) / 2;
7564 }
7565 return y0;
7566 },
7567 wiggle: function(data) {
7568 var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
7569 y0[0] = o = o0 = 0;
7570 for (j = 1; j < m; ++j) {
7571 for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
7572 for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
7573 for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
7574 s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
7575 }
7576 s2 += s3 * data[i][j][1];
7577 }
7578 y0[j] = o -= s1 ? s2 / s1 * dx : 0;
7579 if (o < o0) o0 = o;
7580 }
7581 for (j = 0; j < m; ++j) y0[j] -= o0;
7582 return y0;
7583 },
7584 expand: function(data) {
7585 var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
7586 for (j = 0; j < m; ++j) {
7587 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
7588 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;
7589 }
7590 for (j = 0; j < m; ++j) y0[j] = 0;
7591 return y0;
7592 },
7593 zero: d3_layout_stackOffsetZero
7594 });
7595 function d3_layout_stackOrderDefault(data) {
7596 return d3.range(data.length);
7597 }
7598 function d3_layout_stackOffsetZero(data) {
7599 var j = -1, m = data[0].length, y0 = [];
7600 while (++j < m) y0[j] = 0;
7601 return y0;
7602 }
7603 function d3_layout_stackMaxIndex(array) {
7604 var i = 1, j = 0, v = array[0][1], k, n = array.length;
7605 for (;i < n; ++i) {
7606 if ((k = array[i][1]) > v) {
7607 j = i;
7608 v = k;
7609 }
7610 }
7611 return j;
7612 }
7613 function d3_layout_stackReduceSum(d) {
7614 return d.reduce(d3_layout_stackSum, 0);
7615 }
7616 function d3_layout_stackSum(p, d) {
7617 return p + d[1];
7618 }
7619 d3.layout.histogram = function() {
7620 var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
7621 function histogram(data, i) {
7622 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;
7623 while (++i < m) {
7624 bin = bins[i] = [];
7625 bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
7626 bin.y = 0;
7627 }
7628 if (m > 0) {
7629 i = -1;
7630 while (++i < n) {
7631 x = values[i];
7632 if (x >= range[0] && x <= range[1]) {
7633 bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
7634 bin.y += k;
7635 bin.push(data[i]);
7636 }
7637 }
7638 }
7639 return bins;
7640 }
7641 histogram.value = function(x) {
7642 if (!arguments.length) return valuer;
7643 valuer = x;
7644 return histogram;
7645 };
7646 histogram.range = function(x) {
7647 if (!arguments.length) return ranger;
7648 ranger = d3_functor(x);
7649 return histogram;
7650 };
7651 histogram.bins = function(x) {
7652 if (!arguments.length) return binner;
7653 binner = typeof x === "number" ? function(range) {
7654 return d3_layout_histogramBinFixed(range, x);
7655 } : d3_functor(x);
7656 return histogram;
7657 };
7658 histogram.frequency = function(x) {
7659 if (!arguments.length) return frequency;
7660 frequency = !!x;
7661 return histogram;
7662 };
7663 return histogram;
7664 };
7665 function d3_layout_histogramBinSturges(range, values) {
7666 return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
7667 }
7668 function d3_layout_histogramBinFixed(range, n) {
7669 var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
7670 while (++x <= n) f[x] = m * x + b;
7671 return f;
7672 }
7673 function d3_layout_histogramRange(values) {
7674 return [ d3.min(values), d3.max(values) ];
7675 }
7676 d3.layout.pack = function() {
7677 var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
7678 function pack(d, i) {
7679 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() {
7680 return radius;
7681 };
7682 root.x = root.y = 0;
7683 d3_layout_hierarchyVisitAfter(root, function(d) {
7684 d.r = +r(d.value);
7685 });
7686 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
7687 if (padding) {
7688 var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
7689 d3_layout_hierarchyVisitAfter(root, function(d) {
7690 d.r += dr;
7691 });
7692 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
7693 d3_layout_hierarchyVisitAfter(root, function(d) {
7694 d.r -= dr;
7695 });
7696 }
7697 d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
7698 return nodes;
7699 }
7700 pack.size = function(_) {
7701 if (!arguments.length) return size;
7702 size = _;
7703 return pack;
7704 };
7705 pack.radius = function(_) {
7706 if (!arguments.length) return radius;
7707 radius = _ == null || typeof _ === "function" ? _ : +_;
7708 return pack;
7709 };
7710 pack.padding = function(_) {
7711 if (!arguments.length) return padding;
7712 padding = +_;
7713 return pack;
7714 };
7715 return d3_layout_hierarchyRebind(pack, hierarchy);
7716 };
7717 function d3_layout_packSort(a, b) {
7718 return a.value - b.value;
7719 }
7720 function d3_layout_packInsert(a, b) {
7721 var c = a._pack_next;
7722 a._pack_next = b;
7723 b._pack_prev = a;
7724 b._pack_next = c;
7725 c._pack_prev = b;
7726 }
7727 function d3_layout_packSplice(a, b) {
7728 a._pack_next = b;
7729 b._pack_prev = a;
7730 }
7731 function d3_layout_packIntersects(a, b) {
7732 var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
7733 return .999 * dr * dr > dx * dx + dy * dy;
7734 }
7735 function d3_layout_packSiblings(node) {
7736 if (!(nodes = node.children) || !(n = nodes.length)) return;
7737 var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
7738 function bound(node) {
7739 xMin = Math.min(node.x - node.r, xMin);
7740 xMax = Math.max(node.x + node.r, xMax);
7741 yMin = Math.min(node.y - node.r, yMin);
7742 yMax = Math.max(node.y + node.r, yMax);
7743 }
7744 nodes.forEach(d3_layout_packLink);
7745 a = nodes[0];
7746 a.x = -a.r;
7747 a.y = 0;
7748 bound(a);
7749 if (n > 1) {
7750 b = nodes[1];
7751 b.x = b.r;
7752 b.y = 0;
7753 bound(b);
7754 if (n > 2) {
7755 c = nodes[2];
7756 d3_layout_packPlace(a, b, c);
7757 bound(c);
7758 d3_layout_packInsert(a, c);
7759 a._pack_prev = c;
7760 d3_layout_packInsert(c, b);
7761 b = a._pack_next;
7762 for (i = 3; i < n; i++) {
7763 d3_layout_packPlace(a, b, c = nodes[i]);
7764 var isect = 0, s1 = 1, s2 = 1;
7765 for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
7766 if (d3_layout_packIntersects(j, c)) {
7767 isect = 1;
7768 break;
7769 }
7770 }
7771 if (isect == 1) {
7772 for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
7773 if (d3_layout_packIntersects(k, c)) {
7774 break;
7775 }
7776 }
7777 }
7778 if (isect) {
7779 if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
7780 i--;
7781 } else {
7782 d3_layout_packInsert(a, c);
7783 b = c;
7784 bound(c);
7785 }
7786 }
7787 }
7788 }
7789 var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
7790 for (i = 0; i < n; i++) {
7791 c = nodes[i];
7792 c.x -= cx;
7793 c.y -= cy;
7794 cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
7795 }
7796 node.r = cr;
7797 nodes.forEach(d3_layout_packUnlink);
7798 }
7799 function d3_layout_packLink(node) {
7800 node._pack_next = node._pack_prev = node;
7801 }
7802 function d3_layout_packUnlink(node) {
7803 delete node._pack_next;
7804 delete node._pack_prev;
7805 }
7806 function d3_layout_packTransform(node, x, y, k) {
7807 var children = node.children;
7808 node.x = x += k * node.x;
7809 node.y = y += k * node.y;
7810 node.r *= k;
7811 if (children) {
7812 var i = -1, n = children.length;
7813 while (++i < n) d3_layout_packTransform(children[i], x, y, k);
7814 }
7815 }
7816 function d3_layout_packPlace(a, b, c) {
7817 var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
7818 if (db && (dx || dy)) {
7819 var da = b.r + c.r, dc = dx * dx + dy * dy;
7820 da *= da;
7821 db *= db;
7822 var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
7823 c.x = a.x + x * dx + y * dy;
7824 c.y = a.y + x * dy - y * dx;
7825 } else {
7826 c.x = a.x + db;
7827 c.y = a.y;
7828 }
7829 }
7830 d3.layout.tree = function() {
7831 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
7832 function tree(d, i) {
7833 var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
7834 d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
7835 d3_layout_hierarchyVisitBefore(root1, secondWalk);
7836 if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
7837 var left = root0, right = root0, bottom = root0;
7838 d3_layout_hierarchyVisitBefore(root0, function(node) {
7839 if (node.x < left.x) left = node;
7840 if (node.x > right.x) right = node;
7841 if (node.depth > bottom.depth) bottom = node;
7842 });
7843 var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
7844 d3_layout_hierarchyVisitBefore(root0, function(node) {
7845 node.x = (node.x + tx) * kx;
7846 node.y = node.depth * ky;
7847 });
7848 }
7849 return nodes;
7850 }
7851 function wrapTree(root0) {
7852 var root1 = {
7853 A: null,
7854 children: [ root0 ]
7855 }, queue = [ root1 ], node1;
7856 while ((node1 = queue.pop()) != null) {
7857 for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
7858 queue.push((children[i] = child = {
7859 _: children[i],
7860 parent: node1,
7861 children: (child = children[i].children) && child.slice() || [],
7862 A: null,
7863 a: null,
7864 z: 0,
7865 m: 0,
7866 c: 0,
7867 s: 0,
7868 t: null,
7869 i: i
7870 }).a = child);
7871 }
7872 }
7873 return root1.children[0];
7874 }
7875 function firstWalk(v) {
7876 var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
7877 if (children.length) {
7878 d3_layout_treeShift(v);
7879 var midpoint = (children[0].z + children[children.length - 1].z) / 2;
7880 if (w) {
7881 v.z = w.z + separation(v._, w._);
7882 v.m = v.z - midpoint;
7883 } else {
7884 v.z = midpoint;
7885 }
7886 } else if (w) {
7887 v.z = w.z + separation(v._, w._);
7888 }
7889 v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
7890 }
7891 function secondWalk(v) {
7892 v._.x = v.z + v.parent.m;
7893 v.m += v.parent.m;
7894 }
7895 function apportion(v, w, ancestor) {
7896 if (w) {
7897 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;
7898 while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
7899 vom = d3_layout_treeLeft(vom);
7900 vop = d3_layout_treeRight(vop);
7901 vop.a = v;
7902 shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
7903 if (shift > 0) {
7904 d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
7905 sip += shift;
7906 sop += shift;
7907 }
7908 sim += vim.m;
7909 sip += vip.m;
7910 som += vom.m;
7911 sop += vop.m;
7912 }
7913 if (vim && !d3_layout_treeRight(vop)) {
7914 vop.t = vim;
7915 vop.m += sim - sop;
7916 }
7917 if (vip && !d3_layout_treeLeft(vom)) {
7918 vom.t = vip;
7919 vom.m += sip - som;
7920 ancestor = v;
7921 }
7922 }
7923 return ancestor;
7924 }
7925 function sizeNode(node) {
7926 node.x *= size[0];
7927 node.y = node.depth * size[1];
7928 }
7929 tree.separation = function(x) {
7930 if (!arguments.length) return separation;
7931 separation = x;
7932 return tree;
7933 };
7934 tree.size = function(x) {
7935 if (!arguments.length) return nodeSize ? null : size;
7936 nodeSize = (size = x) == null ? sizeNode : null;
7937 return tree;
7938 };
7939 tree.nodeSize = function(x) {
7940 if (!arguments.length) return nodeSize ? size : null;
7941 nodeSize = (size = x) == null ? null : sizeNode;
7942 return tree;
7943 };
7944 return d3_layout_hierarchyRebind(tree, hierarchy);
7945 };
7946 function d3_layout_treeSeparation(a, b) {
7947 return a.parent == b.parent ? 1 : 2;
7948 }
7949 function d3_layout_treeLeft(v) {
7950 var children = v.children;
7951 return children.length ? children[0] : v.t;
7952 }
7953 function d3_layout_treeRight(v) {
7954 var children = v.children, n;
7955 return (n = children.length) ? children[n - 1] : v.t;
7956 }
7957 function d3_layout_treeMove(wm, wp, shift) {
7958 var change = shift / (wp.i - wm.i);
7959 wp.c -= change;
7960 wp.s += shift;
7961 wm.c += change;
7962 wp.z += shift;
7963 wp.m += shift;
7964 }
7965 function d3_layout_treeShift(v) {
7966 var shift = 0, change = 0, children = v.children, i = children.length, w;
7967 while (--i >= 0) {
7968 w = children[i];
7969 w.z += shift;
7970 w.m += shift;
7971 shift += w.s + (change += w.c);
7972 }
7973 }
7974 function d3_layout_treeAncestor(vim, v, ancestor) {
7975 return vim.a.parent === v.parent ? vim.a : ancestor;
7976 }
7977 d3.layout.cluster = function() {
7978 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
7979 function cluster(d, i) {
7980 var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
7981 d3_layout_hierarchyVisitAfter(root, function(node) {
7982 var children = node.children;
7983 if (children && children.length) {
7984 node.x = d3_layout_clusterX(children);
7985 node.y = d3_layout_clusterY(children);
7986 } else {
7987 node.x = previousNode ? x += separation(node, previousNode) : 0;
7988 node.y = 0;
7989 previousNode = node;
7990 }
7991 });
7992 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;
7993 d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
7994 node.x = (node.x - root.x) * size[0];
7995 node.y = (root.y - node.y) * size[1];
7996 } : function(node) {
7997 node.x = (node.x - x0) / (x1 - x0) * size[0];
7998 node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
7999 });
8000 return nodes;
8001 }
8002 cluster.separation = function(x) {
8003 if (!arguments.length) return separation;
8004 separation = x;
8005 return cluster;
8006 };
8007 cluster.size = function(x) {
8008 if (!arguments.length) return nodeSize ? null : size;
8009 nodeSize = (size = x) == null;
8010 return cluster;
8011 };
8012 cluster.nodeSize = function(x) {
8013 if (!arguments.length) return nodeSize ? size : null;
8014 nodeSize = (size = x) != null;
8015 return cluster;
8016 };
8017 return d3_layout_hierarchyRebind(cluster, hierarchy);
8018 };
8019 function d3_layout_clusterY(children) {
8020 return 1 + d3.max(children, function(child) {
8021 return child.y;
8022 });
8023 }
8024 function d3_layout_clusterX(children) {
8025 return children.reduce(function(x, child) {
8026 return x + child.x;
8027 }, 0) / children.length;
8028 }
8029 function d3_layout_clusterLeft(node) {
8030 var children = node.children;
8031 return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
8032 }
8033 function d3_layout_clusterRight(node) {
8034 var children = node.children, n;
8035 return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
8036 }
8037 d3.layout.treemap = function() {
8038 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));
8039 function scale(children, k) {
8040 var i = -1, n = children.length, child, area;
8041 while (++i < n) {
8042 area = (child = children[i]).value * (k < 0 ? 0 : k);
8043 child.area = isNaN(area) || area <= 0 ? 0 : area;
8044 }
8045 }
8046 function squarify(node) {
8047 var children = node.children;
8048 if (children && children.length) {
8049 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;
8050 scale(remaining, rect.dx * rect.dy / node.value);
8051 row.area = 0;
8052 while ((n = remaining.length) > 0) {
8053 row.push(child = remaining[n - 1]);
8054 row.area += child.area;
8055 if (mode !== "squarify" || (score = worst(row, u)) <= best) {
8056 remaining.pop();
8057 best = score;
8058 } else {
8059 row.area -= row.pop().area;
8060 position(row, u, rect, false);
8061 u = Math.min(rect.dx, rect.dy);
8062 row.length = row.area = 0;
8063 best = Infinity;
8064 }
8065 }
8066 if (row.length) {
8067 position(row, u, rect, true);
8068 row.length = row.area = 0;
8069 }
8070 children.forEach(squarify);
8071 }
8072 }
8073 function stickify(node) {
8074 var children = node.children;
8075 if (children && children.length) {
8076 var rect = pad(node), remaining = children.slice(), child, row = [];
8077 scale(remaining, rect.dx * rect.dy / node.value);
8078 row.area = 0;
8079 while (child = remaining.pop()) {
8080 row.push(child);
8081 row.area += child.area;
8082 if (child.z != null) {
8083 position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
8084 row.length = row.area = 0;
8085 }
8086 }
8087 children.forEach(stickify);
8088 }
8089 }
8090 function worst(row, u) {
8091 var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
8092 while (++i < n) {
8093 if (!(r = row[i].area)) continue;
8094 if (r < rmin) rmin = r;
8095 if (r > rmax) rmax = r;
8096 }
8097 s *= s;
8098 u *= u;
8099 return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
8100 }
8101 function position(row, u, rect, flush) {
8102 var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
8103 if (u == rect.dx) {
8104 if (flush || v > rect.dy) v = rect.dy;
8105 while (++i < n) {
8106 o = row[i];
8107 o.x = x;
8108 o.y = y;
8109 o.dy = v;
8110 x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
8111 }
8112 o.z = true;
8113 o.dx += rect.x + rect.dx - x;
8114 rect.y += v;
8115 rect.dy -= v;
8116 } else {
8117 if (flush || v > rect.dx) v = rect.dx;
8118 while (++i < n) {
8119 o = row[i];
8120 o.x = x;
8121 o.y = y;
8122 o.dx = v;
8123 y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
8124 }
8125 o.z = false;
8126 o.dy += rect.y + rect.dy - y;
8127 rect.x += v;
8128 rect.dx -= v;
8129 }
8130 }
8131 function treemap(d) {
8132 var nodes = stickies || hierarchy(d), root = nodes[0];
8133 root.x = root.y = 0;
8134 if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0;
8135 if (stickies) hierarchy.revalue(root);
8136 scale([ root ], root.dx * root.dy / root.value);
8137 (stickies ? stickify : squarify)(root);
8138 if (sticky) stickies = nodes;
8139 return nodes;
8140 }
8141 treemap.size = function(x) {
8142 if (!arguments.length) return size;
8143 size = x;
8144 return treemap;
8145 };
8146 treemap.padding = function(x) {
8147 if (!arguments.length) return padding;
8148 function padFunction(node) {
8149 var p = x.call(treemap, node, node.depth);
8150 return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
8151 }
8152 function padConstant(node) {
8153 return d3_layout_treemapPad(node, x);
8154 }
8155 var type;
8156 pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
8157 padConstant) : padConstant;
8158 return treemap;
8159 };
8160 treemap.round = function(x) {
8161 if (!arguments.length) return round != Number;
8162 round = x ? Math.round : Number;
8163 return treemap;
8164 };
8165 treemap.sticky = function(x) {
8166 if (!arguments.length) return sticky;
8167 sticky = x;
8168 stickies = null;
8169 return treemap;
8170 };
8171 treemap.ratio = function(x) {
8172 if (!arguments.length) return ratio;
8173 ratio = x;
8174 return treemap;
8175 };
8176 treemap.mode = function(x) {
8177 if (!arguments.length) return mode;
8178 mode = x + "";
8179 return treemap;
8180 };
8181 return d3_layout_hierarchyRebind(treemap, hierarchy);
8182 };
8183 function d3_layout_treemapPadNull(node) {
8184 return {
8185 x: node.x,
8186 y: node.y,
8187 dx: node.dx,
8188 dy: node.dy
8189 };
8190 }
8191 function d3_layout_treemapPad(node, padding) {
8192 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];
8193 if (dx < 0) {
8194 x += dx / 2;
8195 dx = 0;
8196 }
8197 if (dy < 0) {
8198 y += dy / 2;
8199 dy = 0;
8200 }
8201 return {
8202 x: x,
8203 y: y,
8204 dx: dx,
8205 dy: dy
8206 };
8207 }
8208 d3.random = {
8209 normal: function(µ, σ) {
8210 var n = arguments.length;
8211 if (n < 2) σ = 1;
8212 if (n < 1) µ = 0;
8213 return function() {
8214 var x, y, r;
8215 do {
8216 x = Math.random() * 2 - 1;
8217 y = Math.random() * 2 - 1;
8218 r = x * x + y * y;
8219 } while (!r || r > 1);
8220 return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
8221 };
8222 },
8223 logNormal: function() {
8224 var random = d3.random.normal.apply(d3, arguments);
8225 return function() {
8226 return Math.exp(random());
8227 };
8228 },
8229 bates: function(m) {
8230 var random = d3.random.irwinHall(m);
8231 return function() {
8232 return random() / m;
8233 };
8234 },
8235 irwinHall: function(m) {
8236 return function() {
8237 for (var s = 0, j = 0; j < m; j++) s += Math.random();
8238 return s;
8239 };
8240 }
8241 };
8242 d3.scale = {};
8243 function d3_scaleExtent(domain) {
8244 var start = domain[0], stop = domain[domain.length - 1];
8245 return start < stop ? [ start, stop ] : [ stop, start ];
8246 }
8247 function d3_scaleRange(scale) {
8248 return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
8249 }
8250 function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
8251 var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
8252 return function(x) {
8253 return i(u(x));
8254 };
8255 }
8256 function d3_scale_nice(domain, nice) {
8257 var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
8258 if (x1 < x0) {
8259 dx = i0, i0 = i1, i1 = dx;
8260 dx = x0, x0 = x1, x1 = dx;
8261 }
8262 domain[i0] = nice.floor(x0);
8263 domain[i1] = nice.ceil(x1);
8264 return domain;
8265 }
8266 function d3_scale_niceStep(step) {
8267 return step ? {
8268 floor: function(x) {
8269 return Math.floor(x / step) * step;
8270 },
8271 ceil: function(x) {
8272 return Math.ceil(x / step) * step;
8273 }
8274 } : d3_scale_niceIdentity;
8275 }
8276 var d3_scale_niceIdentity = {
8277 floor: d3_identity,
8278 ceil: d3_identity
8279 };
8280 function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
8281 var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
8282 if (domain[k] < domain[0]) {
8283 domain = domain.slice().reverse();
8284 range = range.slice().reverse();
8285 }
8286 while (++j <= k) {
8287 u.push(uninterpolate(domain[j - 1], domain[j]));
8288 i.push(interpolate(range[j - 1], range[j]));
8289 }
8290 return function(x) {
8291 var j = d3.bisect(domain, x, 1, k) - 1;
8292 return i[j](u[j](x));
8293 };
8294 }
8295 d3.scale.linear = function() {
8296 return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
8297 };
8298 function d3_scale_linear(domain, range, interpolate, clamp) {
8299 var output, input;
8300 function rescale() {
8301 var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
8302 output = linear(domain, range, uninterpolate, interpolate);
8303 input = linear(range, domain, uninterpolate, d3_interpolate);
8304 return scale;
8305 }
8306 function scale(x) {
8307 return output(x);
8308 }
8309 scale.invert = function(y) {
8310 return input(y);
8311 };
8312 scale.domain = function(x) {
8313 if (!arguments.length) return domain;
8314 domain = x.map(Number);
8315 return rescale();
8316 };
8317 scale.range = function(x) {
8318 if (!arguments.length) return range;
8319 range = x;
8320 return rescale();
8321 };
8322 scale.rangeRound = function(x) {
8323 return scale.range(x).interpolate(d3_interpolateRound);
8324 };
8325 scale.clamp = function(x) {
8326 if (!arguments.length) return clamp;
8327 clamp = x;
8328 return rescale();
8329 };
8330 scale.interpolate = function(x) {
8331 if (!arguments.length) return interpolate;
8332 interpolate = x;
8333 return rescale();
8334 };
8335 scale.ticks = function(m) {
8336 return d3_scale_linearTicks(domain, m);
8337 };
8338 scale.tickFormat = function(m, format) {
8339 return d3_scale_linearTickFormat(domain, m, format);
8340 };
8341 scale.nice = function(m) {
8342 d3_scale_linearNice(domain, m);
8343 return rescale();
8344 };
8345 scale.copy = function() {
8346 return d3_scale_linear(domain, range, interpolate, clamp);
8347 };
8348 return rescale();
8349 }
8350 function d3_scale_linearRebind(scale, linear) {
8351 return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
8352 }
8353 function d3_scale_linearNice(domain, m) {
8354 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
8355 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
8356 return domain;
8357 }
8358 function d3_scale_linearTickRange(domain, m) {
8359 if (m == null) m = 10;
8360 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;
8361 if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
8362 extent[0] = Math.ceil(extent[0] / step) * step;
8363 extent[1] = Math.floor(extent[1] / step) * step + step * .5;
8364 extent[2] = step;
8365 return extent;
8366 }
8367 function d3_scale_linearTicks(domain, m) {
8368 return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
8369 }
8370 function d3_scale_linearTickFormat(domain, m, format) {
8371 var range = d3_scale_linearTickRange(domain, m);
8372 if (format) {
8373 var match = d3_format_re.exec(format);
8374 match.shift();
8375 if (match[8] === "s") {
8376 var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
8377 if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
8378 match[8] = "f";
8379 format = d3.format(match.join(""));
8380 return function(d) {
8381 return format(prefix.scale(d)) + prefix.symbol;
8382 };
8383 }
8384 if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
8385 format = match.join("");
8386 } else {
8387 format = ",." + d3_scale_linearPrecision(range[2]) + "f";
8388 }
8389 return d3.format(format);
8390 }
8391 var d3_scale_linearFormatSignificant = {
8392 s: 1,
8393 g: 1,
8394 p: 1,
8395 r: 1,
8396 e: 1
8397 };
8398 function d3_scale_linearPrecision(value) {
8399 return -Math.floor(Math.log(value) / Math.LN10 + .01);
8400 }
8401 function d3_scale_linearFormatPrecision(type, range) {
8402 var p = d3_scale_linearPrecision(range[2]);
8403 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;
8404 }
8405 d3.scale.log = function() {
8406 return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
8407 };
8408 function d3_scale_log(linear, base, positive, domain) {
8409 function log(x) {
8410 return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
8411 }
8412 function pow(x) {
8413 return positive ? Math.pow(base, x) : -Math.pow(base, -x);
8414 }
8415 function scale(x) {
8416 return linear(log(x));
8417 }
8418 scale.invert = function(x) {
8419 return pow(linear.invert(x));
8420 };
8421 scale.domain = function(x) {
8422 if (!arguments.length) return domain;
8423 positive = x[0] >= 0;
8424 linear.domain((domain = x.map(Number)).map(log));
8425 return scale;
8426 };
8427 scale.base = function(_) {
8428 if (!arguments.length) return base;
8429 base = +_;
8430 linear.domain(domain.map(log));
8431 return scale;
8432 };
8433 scale.nice = function() {
8434 var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
8435 linear.domain(niced);
8436 domain = niced.map(pow);
8437 return scale;
8438 };
8439 scale.ticks = function() {
8440 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;
8441 if (isFinite(j - i)) {
8442 if (positive) {
8443 for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
8444 ticks.push(pow(i));
8445 } else {
8446 ticks.push(pow(i));
8447 for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
8448 }
8449 for (i = 0; ticks[i] < u; i++) {}
8450 for (j = ticks.length; ticks[j - 1] > v; j--) {}
8451 ticks = ticks.slice(i, j);
8452 }
8453 return ticks;
8454 };
8455 scale.tickFormat = function(n, format) {
8456 if (!arguments.length) return d3_scale_logFormat;
8457 if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
8458 var k = Math.max(1, base * n / scale.ticks().length);
8459 return function(d) {
8460 var i = d / pow(Math.round(log(d)));
8461 if (i * base < base - .5) i *= base;
8462 return i <= k ? format(d) : "";
8463 };
8464 };
8465 scale.copy = function() {
8466 return d3_scale_log(linear.copy(), base, positive, domain);
8467 };
8468 return d3_scale_linearRebind(scale, linear);
8469 }
8470 var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
8471 floor: function(x) {
8472 return -Math.ceil(-x);
8473 },
8474 ceil: function(x) {
8475 return -Math.floor(-x);
8476 }
8477 };
8478 d3.scale.pow = function() {
8479 return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
8480 };
8481 function d3_scale_pow(linear, exponent, domain) {
8482 var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
8483 function scale(x) {
8484 return linear(powp(x));
8485 }
8486 scale.invert = function(x) {
8487 return powb(linear.invert(x));
8488 };
8489 scale.domain = function(x) {
8490 if (!arguments.length) return domain;
8491 linear.domain((domain = x.map(Number)).map(powp));
8492 return scale;
8493 };
8494 scale.ticks = function(m) {
8495 return d3_scale_linearTicks(domain, m);
8496 };
8497 scale.tickFormat = function(m, format) {
8498 return d3_scale_linearTickFormat(domain, m, format);
8499 };
8500 scale.nice = function(m) {
8501 return scale.domain(d3_scale_linearNice(domain, m));
8502 };
8503 scale.exponent = function(x) {
8504 if (!arguments.length) return exponent;
8505 powp = d3_scale_powPow(exponent = x);
8506 powb = d3_scale_powPow(1 / exponent);
8507 linear.domain(domain.map(powp));
8508 return scale;
8509 };
8510 scale.copy = function() {
8511 return d3_scale_pow(linear.copy(), exponent, domain);
8512 };
8513 return d3_scale_linearRebind(scale, linear);
8514 }
8515 function d3_scale_powPow(e) {
8516 return function(x) {
8517 return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
8518 };
8519 }
8520 d3.scale.sqrt = function() {
8521 return d3.scale.pow().exponent(.5);
8522 };
8523 d3.scale.ordinal = function() {
8524 return d3_scale_ordinal([], {
8525 t: "range",
8526 a: [ [] ]
8527 });
8528 };
8529 function d3_scale_ordinal(domain, ranger) {
8530 var index, range, rangeBand;
8531 function scale(x) {
8532 return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
8533 }
8534 function steps(start, step) {
8535 return d3.range(domain.length).map(function(i) {
8536 return start + step * i;
8537 });
8538 }
8539 scale.domain = function(x) {
8540 if (!arguments.length) return domain;
8541 domain = [];
8542 index = new d3_Map();
8543 var i = -1, n = x.length, xi;
8544 while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
8545 return scale[ranger.t].apply(scale, ranger.a);
8546 };
8547 scale.range = function(x) {
8548 if (!arguments.length) return range;
8549 range = x;
8550 rangeBand = 0;
8551 ranger = {
8552 t: "range",
8553 a: arguments
8554 };
8555 return scale;
8556 };
8557 scale.rangePoints = function(x, padding) {
8558 if (arguments.length < 2) padding = 0;
8559 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2,
8560 0) : (stop - start) / (domain.length - 1 + padding);
8561 range = steps(start + step * padding / 2, step);
8562 rangeBand = 0;
8563 ranger = {
8564 t: "rangePoints",
8565 a: arguments
8566 };
8567 return scale;
8568 };
8569 scale.rangeRoundPoints = function(x, padding) {
8570 if (arguments.length < 2) padding = 0;
8571 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2),
8572 0) : (stop - start) / (domain.length - 1 + padding) | 0;
8573 range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
8574 rangeBand = 0;
8575 ranger = {
8576 t: "rangeRoundPoints",
8577 a: arguments
8578 };
8579 return scale;
8580 };
8581 scale.rangeBands = function(x, padding, outerPadding) {
8582 if (arguments.length < 2) padding = 0;
8583 if (arguments.length < 3) outerPadding = padding;
8584 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
8585 range = steps(start + step * outerPadding, step);
8586 if (reverse) range.reverse();
8587 rangeBand = step * (1 - padding);
8588 ranger = {
8589 t: "rangeBands",
8590 a: arguments
8591 };
8592 return scale;
8593 };
8594 scale.rangeRoundBands = function(x, padding, outerPadding) {
8595 if (arguments.length < 2) padding = 0;
8596 if (arguments.length < 3) outerPadding = padding;
8597 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
8598 range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
8599 if (reverse) range.reverse();
8600 rangeBand = Math.round(step * (1 - padding));
8601 ranger = {
8602 t: "rangeRoundBands",
8603 a: arguments
8604 };
8605 return scale;
8606 };
8607 scale.rangeBand = function() {
8608 return rangeBand;
8609 };
8610 scale.rangeExtent = function() {
8611 return d3_scaleExtent(ranger.a[0]);
8612 };
8613 scale.copy = function() {
8614 return d3_scale_ordinal(domain, ranger);
8615 };
8616 return scale.domain(domain);
8617 }
8618 d3.scale.category10 = function() {
8619 return d3.scale.ordinal().range(d3_category10);
8620 };
8621 d3.scale.category20 = function() {
8622 return d3.scale.ordinal().range(d3_category20);
8623 };
8624 d3.scale.category20b = function() {
8625 return d3.scale.ordinal().range(d3_category20b);
8626 };
8627 d3.scale.category20c = function() {
8628 return d3.scale.ordinal().range(d3_category20c);
8629 };
8630 var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
8631 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);
8632 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);
8633 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);
8634 d3.scale.quantile = function() {
8635 return d3_scale_quantile([], []);
8636 };
8637 function d3_scale_quantile(domain, range) {
8638 var thresholds;
8639 function rescale() {
8640 var k = 0, q = range.length;
8641 thresholds = [];
8642 while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
8643 return scale;
8644 }
8645 function scale(x) {
8646 if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
8647 }
8648 scale.domain = function(x) {
8649 if (!arguments.length) return domain;
8650 domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
8651 return rescale();
8652 };
8653 scale.range = function(x) {
8654 if (!arguments.length) return range;
8655 range = x;
8656 return rescale();
8657 };
8658 scale.quantiles = function() {
8659 return thresholds;
8660 };
8661 scale.invertExtent = function(y) {
8662 y = range.indexOf(y);
8663 return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
8664 };
8665 scale.copy = function() {
8666 return d3_scale_quantile(domain, range);
8667 };
8668 return rescale();
8669 }
8670 d3.scale.quantize = function() {
8671 return d3_scale_quantize(0, 1, [ 0, 1 ]);
8672 };
8673 function d3_scale_quantize(x0, x1, range) {
8674 var kx, i;
8675 function scale(x) {
8676 return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
8677 }
8678 function rescale() {
8679 kx = range.length / (x1 - x0);
8680 i = range.length - 1;
8681 return scale;
8682 }
8683 scale.domain = function(x) {
8684 if (!arguments.length) return [ x0, x1 ];
8685 x0 = +x[0];
8686 x1 = +x[x.length - 1];
8687 return rescale();
8688 };
8689 scale.range = function(x) {
8690 if (!arguments.length) return range;
8691 range = x;
8692 return rescale();
8693 };
8694 scale.invertExtent = function(y) {
8695 y = range.indexOf(y);
8696 y = y < 0 ? NaN : y / kx + x0;
8697 return [ y, y + 1 / kx ];
8698 };
8699 scale.copy = function() {
8700 return d3_scale_quantize(x0, x1, range);
8701 };
8702 return rescale();
8703 }
8704 d3.scale.threshold = function() {
8705 return d3_scale_threshold([ .5 ], [ 0, 1 ]);
8706 };
8707 function d3_scale_threshold(domain, range) {
8708 function scale(x) {
8709 if (x <= x) return range[d3.bisect(domain, x)];
8710 }
8711 scale.domain = function(_) {
8712 if (!arguments.length) return domain;
8713 domain = _;
8714 return scale;
8715 };
8716 scale.range = function(_) {
8717 if (!arguments.length) return range;
8718 range = _;
8719 return scale;
8720 };
8721 scale.invertExtent = function(y) {
8722 y = range.indexOf(y);
8723 return [ domain[y - 1], domain[y] ];
8724 };
8725 scale.copy = function() {
8726 return d3_scale_threshold(domain, range);
8727 };
8728 return scale;
8729 }
8730 d3.scale.identity = function() {
8731 return d3_scale_identity([ 0, 1 ]);
8732 };
8733 function d3_scale_identity(domain) {
8734 function identity(x) {
8735 return +x;
8736 }
8737 identity.invert = identity;
8738 identity.domain = identity.range = function(x) {
8739 if (!arguments.length) return domain;
8740 domain = x.map(identity);
8741 return identity;
8742 };
8743 identity.ticks = function(m) {
8744 return d3_scale_linearTicks(domain, m);
8745 };
8746 identity.tickFormat = function(m, format) {
8747 return d3_scale_linearTickFormat(domain, m, format);
8748 };
8749 identity.copy = function() {
8750 return d3_scale_identity(domain);
8751 };
8752 return identity;
8753 }
8754 d3.svg = {};
8755 function d3_zero() {
8756 return 0;
8757 }
8758 d3.svg.arc = function() {
8759 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;
8760 function arc() {
8761 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;
8762 if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
8763 if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
8764 var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
8765 if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
8766 rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
8767 if (!cw) p1 *= -1;
8768 if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
8769 if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
8770 }
8771 if (r1) {
8772 x0 = r1 * Math.cos(a0 + p1);
8773 y0 = r1 * Math.sin(a0 + p1);
8774 x1 = r1 * Math.cos(a1 - p1);
8775 y1 = r1 * Math.sin(a1 - p1);
8776 var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
8777 if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
8778 var h1 = (a0 + a1) / 2;
8779 x0 = r1 * Math.cos(h1);
8780 y0 = r1 * Math.sin(h1);
8781 x1 = y1 = null;
8782 }
8783 } else {
8784 x0 = y0 = 0;
8785 }
8786 if (r0) {
8787 x2 = r0 * Math.cos(a1 - p0);
8788 y2 = r0 * Math.sin(a1 - p0);
8789 x3 = r0 * Math.cos(a0 + p0);
8790 y3 = r0 * Math.sin(a0 + p0);
8791 var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
8792 if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
8793 var h0 = (a0 + a1) / 2;
8794 x2 = r0 * Math.cos(h0);
8795 y2 = r0 * Math.sin(h0);
8796 x3 = y3 = null;
8797 }
8798 } else {
8799 x2 = y2 = 0;
8800 }
8801 if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
8802 cr = r0 < r1 ^ cw ? 0 : 1;
8803 var rc1 = rc, rc0 = rc;
8804 if (da < π) {
8805 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]);
8806 rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
8807 rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
8808 }
8809 if (x1 != null) {
8810 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);
8811 if (rc === rc1) {
8812 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]);
8813 } else {
8814 path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
8815 }
8816 } else {
8817 path.push("M", x0, ",", y0);
8818 }
8819 if (x3 != null) {
8820 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);
8821 if (rc === rc0) {
8822 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]);
8823 } else {
8824 path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
8825 }
8826 } else {
8827 path.push("L", x2, ",", y2);
8828 }
8829 } else {
8830 path.push("M", x0, ",", y0);
8831 if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
8832 path.push("L", x2, ",", y2);
8833 if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
8834 }
8835 path.push("Z");
8836 return path.join("");
8837 }
8838 function circleSegment(r1, cw) {
8839 return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
8840 }
8841 arc.innerRadius = function(v) {
8842 if (!arguments.length) return innerRadius;
8843 innerRadius = d3_functor(v);
8844 return arc;
8845 };
8846 arc.outerRadius = function(v) {
8847 if (!arguments.length) return outerRadius;
8848 outerRadius = d3_functor(v);
8849 return arc;
8850 };
8851 arc.cornerRadius = function(v) {
8852 if (!arguments.length) return cornerRadius;
8853 cornerRadius = d3_functor(v);
8854 return arc;
8855 };
8856 arc.padRadius = function(v) {
8857 if (!arguments.length) return padRadius;
8858 padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
8859 return arc;
8860 };
8861 arc.startAngle = function(v) {
8862 if (!arguments.length) return startAngle;
8863 startAngle = d3_functor(v);
8864 return arc;
8865 };
8866 arc.endAngle = function(v) {
8867 if (!arguments.length) return endAngle;
8868 endAngle = d3_functor(v);
8869 return arc;
8870 };
8871 arc.padAngle = function(v) {
8872 if (!arguments.length) return padAngle;
8873 padAngle = d3_functor(v);
8874 return arc;
8875 };
8876 arc.centroid = function() {
8877 var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
8878 return [ Math.cos(a) * r, Math.sin(a) * r ];
8879 };
8880 return arc;
8881 };
8882 var d3_svg_arcAuto = "auto";
8883 function d3_svg_arcInnerRadius(d) {
8884 return d.innerRadius;
8885 }
8886 function d3_svg_arcOuterRadius(d) {
8887 return d.outerRadius;
8888 }
8889 function d3_svg_arcStartAngle(d) {
8890 return d.startAngle;
8891 }
8892 function d3_svg_arcEndAngle(d) {
8893 return d.endAngle;
8894 }
8895 function d3_svg_arcPadAngle(d) {
8896 return d && d.padAngle;
8897 }
8898 function d3_svg_arcSweep(x0, y0, x1, y1) {
8899 return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
8900 }
8901 function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
8902 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;
8903 if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
8904 return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
8905 }
8906 function d3_svg_line(projection) {
8907 var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
8908 function line(data) {
8909 var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
8910 function segment() {
8911 segments.push("M", interpolate(projection(points), tension));
8912 }
8913 while (++i < n) {
8914 if (defined.call(this, d = data[i], i)) {
8915 points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
8916 } else if (points.length) {
8917 segment();
8918 points = [];
8919 }
8920 }
8921 if (points.length) segment();
8922 return segments.length ? segments.join("") : null;
8923 }
8924 line.x = function(_) {
8925 if (!arguments.length) return x;
8926 x = _;
8927 return line;
8928 };
8929 line.y = function(_) {
8930 if (!arguments.length) return y;
8931 y = _;
8932 return line;
8933 };
8934 line.defined = function(_) {
8935 if (!arguments.length) return defined;
8936 defined = _;
8937 return line;
8938 };
8939 line.interpolate = function(_) {
8940 if (!arguments.length) return interpolateKey;
8941 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
8942 return line;
8943 };
8944 line.tension = function(_) {
8945 if (!arguments.length) return tension;
8946 tension = _;
8947 return line;
8948 };
8949 return line;
8950 }
8951 d3.svg.line = function() {
8952 return d3_svg_line(d3_identity);
8953 };
8954 var d3_svg_lineInterpolators = d3.map({
8955 linear: d3_svg_lineLinear,
8956 "linear-closed": d3_svg_lineLinearClosed,
8957 step: d3_svg_lineStep,
8958 "step-before": d3_svg_lineStepBefore,
8959 "step-after": d3_svg_lineStepAfter,
8960 basis: d3_svg_lineBasis,
8961 "basis-open": d3_svg_lineBasisOpen,
8962 "basis-closed": d3_svg_lineBasisClosed,
8963 bundle: d3_svg_lineBundle,
8964 cardinal: d3_svg_lineCardinal,
8965 "cardinal-open": d3_svg_lineCardinalOpen,
8966 "cardinal-closed": d3_svg_lineCardinalClosed,
8967 monotone: d3_svg_lineMonotone
8968 });
8969 d3_svg_lineInterpolators.forEach(function(key, value) {
8970 value.key = key;
8971 value.closed = /-closed$/.test(key);
8972 });
8973 function d3_svg_lineLinear(points) {
8974 return points.length > 1 ? points.join("L") : points + "Z";
8975 }
8976 function d3_svg_lineLinearClosed(points) {
8977 return points.join("L") + "Z";
8978 }
8979 function d3_svg_lineStep(points) {
8980 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
8981 while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
8982 if (n > 1) path.push("H", p[0]);
8983 return path.join("");
8984 }
8985 function d3_svg_lineStepBefore(points) {
8986 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
8987 while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
8988 return path.join("");
8989 }
8990 function d3_svg_lineStepAfter(points) {
8991 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
8992 while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
8993 return path.join("");
8994 }
8995 function d3_svg_lineCardinalOpen(points, tension) {
8996 return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
8997 }
8998 function d3_svg_lineCardinalClosed(points, tension) {
8999 return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
9000 points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
9001 }
9002 function d3_svg_lineCardinal(points, tension) {
9003 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
9004 }
9005 function d3_svg_lineHermite(points, tangents) {
9006 if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
9007 return d3_svg_lineLinear(points);
9008 }
9009 var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
9010 if (quad) {
9011 path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
9012 p0 = points[1];
9013 pi = 2;
9014 }
9015 if (tangents.length > 1) {
9016 t = tangents[1];
9017 p = points[pi];
9018 pi++;
9019 path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
9020 for (var i = 2; i < tangents.length; i++, pi++) {
9021 p = points[pi];
9022 t = tangents[i];
9023 path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
9024 }
9025 }
9026 if (quad) {
9027 var lp = points[pi];
9028 path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
9029 }
9030 return path;
9031 }
9032 function d3_svg_lineCardinalTangents(points, tension) {
9033 var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
9034 while (++i < n) {
9035 p0 = p1;
9036 p1 = p2;
9037 p2 = points[i];
9038 tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
9039 }
9040 return tangents;
9041 }
9042 function d3_svg_lineBasis(points) {
9043 if (points.length < 3) return d3_svg_lineLinear(points);
9044 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) ];
9045 points.push(points[n - 1]);
9046 while (++i <= n) {
9047 pi = points[i];
9048 px.shift();
9049 px.push(pi[0]);
9050 py.shift();
9051 py.push(pi[1]);
9052 d3_svg_lineBasisBezier(path, px, py);
9053 }
9054 points.pop();
9055 path.push("L", pi);
9056 return path.join("");
9057 }
9058 function d3_svg_lineBasisOpen(points) {
9059 if (points.length < 4) return d3_svg_lineLinear(points);
9060 var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
9061 while (++i < 3) {
9062 pi = points[i];
9063 px.push(pi[0]);
9064 py.push(pi[1]);
9065 }
9066 path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
9067 --i;
9068 while (++i < n) {
9069 pi = points[i];
9070 px.shift();
9071 px.push(pi[0]);
9072 py.shift();
9073 py.push(pi[1]);
9074 d3_svg_lineBasisBezier(path, px, py);
9075 }
9076 return path.join("");
9077 }
9078 function d3_svg_lineBasisClosed(points) {
9079 var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
9080 while (++i < 4) {
9081 pi = points[i % n];
9082 px.push(pi[0]);
9083 py.push(pi[1]);
9084 }
9085 path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
9086 --i;
9087 while (++i < m) {
9088 pi = points[i % n];
9089 px.shift();
9090 px.push(pi[0]);
9091 py.shift();
9092 py.push(pi[1]);
9093 d3_svg_lineBasisBezier(path, px, py);
9094 }
9095 return path.join("");
9096 }
9097 function d3_svg_lineBundle(points, tension) {
9098 var n = points.length - 1;
9099 if (n) {
9100 var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
9101 while (++i <= n) {
9102 p = points[i];
9103 t = i / n;
9104 p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
9105 p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
9106 }
9107 }
9108 return d3_svg_lineBasis(points);
9109 }
9110 function d3_svg_lineDot4(a, b) {
9111 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
9112 }
9113 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 ];
9114 function d3_svg_lineBasisBezier(path, x, y) {
9115 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));
9116 }
9117 function d3_svg_lineSlope(p0, p1) {
9118 return (p1[1] - p0[1]) / (p1[0] - p0[0]);
9119 }
9120 function d3_svg_lineFiniteDifferences(points) {
9121 var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
9122 while (++i < j) {
9123 m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
9124 }
9125 m[i] = d;
9126 return m;
9127 }
9128 function d3_svg_lineMonotoneTangents(points) {
9129 var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
9130 while (++i < j) {
9131 d = d3_svg_lineSlope(points[i], points[i + 1]);
9132 if (abs(d) < ε) {
9133 m[i] = m[i + 1] = 0;
9134 } else {
9135 a = m[i] / d;
9136 b = m[i + 1] / d;
9137 s = a * a + b * b;
9138 if (s > 9) {
9139 s = d * 3 / Math.sqrt(s);
9140 m[i] = s * a;
9141 m[i + 1] = s * b;
9142 }
9143 }
9144 }
9145 i = -1;
9146 while (++i <= j) {
9147 s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
9148 tangents.push([ s || 0, m[i] * s || 0 ]);
9149 }
9150 return tangents;
9151 }
9152 function d3_svg_lineMonotone(points) {
9153 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
9154 }
9155 d3.svg.line.radial = function() {
9156 var line = d3_svg_line(d3_svg_lineRadial);
9157 line.radius = line.x, delete line.x;
9158 line.angle = line.y, delete line.y;
9159 return line;
9160 };
9161 function d3_svg_lineRadial(points) {
9162 var point, i = -1, n = points.length, r, a;
9163 while (++i < n) {
9164 point = points[i];
9165 r = point[0];
9166 a = point[1] - halfπ;
9167 point[0] = r * Math.cos(a);
9168 point[1] = r * Math.sin(a);
9169 }
9170 return points;
9171 }
9172 function d3_svg_area(projection) {
9173 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;
9174 function area(data) {
9175 var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
9176 return x;
9177 } : d3_functor(x1), fy1 = y0 === y1 ? function() {
9178 return y;
9179 } : d3_functor(y1), x, y;
9180 function segment() {
9181 segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
9182 }
9183 while (++i < n) {
9184 if (defined.call(this, d = data[i], i)) {
9185 points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
9186 points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
9187 } else if (points0.length) {
9188 segment();
9189 points0 = [];
9190 points1 = [];
9191 }
9192 }
9193 if (points0.length) segment();
9194 return segments.length ? segments.join("") : null;
9195 }
9196 area.x = function(_) {
9197 if (!arguments.length) return x1;
9198 x0 = x1 = _;
9199 return area;
9200 };
9201 area.x0 = function(_) {
9202 if (!arguments.length) return x0;
9203 x0 = _;
9204 return area;
9205 };
9206 area.x1 = function(_) {
9207 if (!arguments.length) return x1;
9208 x1 = _;
9209 return area;
9210 };
9211 area.y = function(_) {
9212 if (!arguments.length) return y1;
9213 y0 = y1 = _;
9214 return area;
9215 };
9216 area.y0 = function(_) {
9217 if (!arguments.length) return y0;
9218 y0 = _;
9219 return area;
9220 };
9221 area.y1 = function(_) {
9222 if (!arguments.length) return y1;
9223 y1 = _;
9224 return area;
9225 };
9226 area.defined = function(_) {
9227 if (!arguments.length) return defined;
9228 defined = _;
9229 return area;
9230 };
9231 area.interpolate = function(_) {
9232 if (!arguments.length) return interpolateKey;
9233 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
9234 interpolateReverse = interpolate.reverse || interpolate;
9235 L = interpolate.closed ? "M" : "L";
9236 return area;
9237 };
9238 area.tension = function(_) {
9239 if (!arguments.length) return tension;
9240 tension = _;
9241 return area;
9242 };
9243 return area;
9244 }
9245 d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
9246 d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
9247 d3.svg.area = function() {
9248 return d3_svg_area(d3_identity);
9249 };
9250 d3.svg.area.radial = function() {
9251 var area = d3_svg_area(d3_svg_lineRadial);
9252 area.radius = area.x, delete area.x;
9253 area.innerRadius = area.x0, delete area.x0;
9254 area.outerRadius = area.x1, delete area.x1;
9255 area.angle = area.y, delete area.y;
9256 area.startAngle = area.y0, delete area.y0;
9257 area.endAngle = area.y1, delete area.y1;
9258 return area;
9259 };
9260 d3.svg.chord = function() {
9261 var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
9262 function chord(d, i) {
9263 var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
9264 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";
9265 }
9266 function subgroup(self, f, d, i) {
9267 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π;
9268 return {
9269 r: r,
9270 a0: a0,
9271 a1: a1,
9272 p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
9273 p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
9274 };
9275 }
9276 function equals(a, b) {
9277 return a.a0 == b.a0 && a.a1 == b.a1;
9278 }
9279 function arc(r, p, a) {
9280 return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
9281 }
9282 function curve(r0, p0, r1, p1) {
9283 return "Q 0,0 " + p1;
9284 }
9285 chord.radius = function(v) {
9286 if (!arguments.length) return radius;
9287 radius = d3_functor(v);
9288 return chord;
9289 };
9290 chord.source = function(v) {
9291 if (!arguments.length) return source;
9292 source = d3_functor(v);
9293 return chord;
9294 };
9295 chord.target = function(v) {
9296 if (!arguments.length) return target;
9297 target = d3_functor(v);
9298 return chord;
9299 };
9300 chord.startAngle = function(v) {
9301 if (!arguments.length) return startAngle;
9302 startAngle = d3_functor(v);
9303 return chord;
9304 };
9305 chord.endAngle = function(v) {
9306 if (!arguments.length) return endAngle;
9307 endAngle = d3_functor(v);
9308 return chord;
9309 };
9310 return chord;
9311 };
9312 function d3_svg_chordRadius(d) {
9313 return d.radius;
9314 }
9315 d3.svg.diagonal = function() {
9316 var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
9317 function diagonal(d, i) {
9318 var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
9319 x: p0.x,
9320 y: m
9321 }, {
9322 x: p3.x,
9323 y: m
9324 }, p3 ];
9325 p = p.map(projection);
9326 return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
9327 }
9328 diagonal.source = function(x) {
9329 if (!arguments.length) return source;
9330 source = d3_functor(x);
9331 return diagonal;
9332 };
9333 diagonal.target = function(x) {
9334 if (!arguments.length) return target;
9335 target = d3_functor(x);
9336 return diagonal;
9337 };
9338 diagonal.projection = function(x) {
9339 if (!arguments.length) return projection;
9340 projection = x;
9341 return diagonal;
9342 };
9343 return diagonal;
9344 };
9345 function d3_svg_diagonalProjection(d) {
9346 return [ d.x, d.y ];
9347 }
9348 d3.svg.diagonal.radial = function() {
9349 var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
9350 diagonal.projection = function(x) {
9351 return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
9352 };
9353 return diagonal;
9354 };
9355 function d3_svg_diagonalRadialProjection(projection) {
9356 return function() {
9357 var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
9358 return [ r * Math.cos(a), r * Math.sin(a) ];
9359 };
9360 }
9361 d3.svg.symbol = function() {
9362 var type = d3_svg_symbolType, size = d3_svg_symbolSize;
9363 function symbol(d, i) {
9364 return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
9365 }
9366 symbol.type = function(x) {
9367 if (!arguments.length) return type;
9368 type = d3_functor(x);
9369 return symbol;
9370 };
9371 symbol.size = function(x) {
9372 if (!arguments.length) return size;
9373 size = d3_functor(x);
9374 return symbol;
9375 };
9376 return symbol;
9377 };
9378 function d3_svg_symbolSize() {
9379 return 64;
9380 }
9381 function d3_svg_symbolType() {
9382 return "circle";
9383 }
9384 function d3_svg_symbolCircle(size) {
9385 var r = Math.sqrt(size / π);
9386 return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
9387 }
9388 var d3_svg_symbols = d3.map({
9389 circle: d3_svg_symbolCircle,
9390 cross: function(size) {
9391 var r = Math.sqrt(size / 5) / 2;
9392 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";
9393 },
9394 diamond: function(size) {
9395 var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
9396 return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
9397 },
9398 square: function(size) {
9399 var r = Math.sqrt(size) / 2;
9400 return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
9401 },
9402 "triangle-down": function(size) {
9403 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
9404 return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
9405 },
9406 "triangle-up": function(size) {
9407 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
9408 return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
9409 }
9410 });
9411 d3.svg.symbolTypes = d3_svg_symbols.keys();
9412 var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
9413 d3_selectionPrototype.transition = function(name) {
9414 var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
9415 time: Date.now(),
9416 ease: d3_ease_cubicInOut,
9417 delay: 0,
9418 duration: 250
9419 };
9420 for (var j = -1, m = this.length; ++j < m; ) {
9421 subgroups.push(subgroup = []);
9422 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
9423 if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
9424 subgroup.push(node);
9425 }
9426 }
9427 return d3_transition(subgroups, ns, id);
9428 };
9429 d3_selectionPrototype.interrupt = function(name) {
9430 return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
9431 };
9432 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
9433 function d3_selection_interruptNS(ns) {
9434 return function() {
9435 var lock, activeId, active;
9436 if ((lock = this[ns]) && (active = lock[activeId = lock.active])) {
9437 active.timer.c = null;
9438 active.timer.t = NaN;
9439 if (--lock.count) delete lock[activeId]; else delete this[ns];
9440 lock.active += .5;
9441 active.event && active.event.interrupt.call(this, this.__data__, active.index);
9442 }
9443 };
9444 }
9445 function d3_transition(groups, ns, id) {
9446 d3_subclass(groups, d3_transitionPrototype);
9447 groups.namespace = ns;
9448 groups.id = id;
9449 return groups;
9450 }
9451 var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
9452 d3_transitionPrototype.call = d3_selectionPrototype.call;
9453 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
9454 d3_transitionPrototype.node = d3_selectionPrototype.node;
9455 d3_transitionPrototype.size = d3_selectionPrototype.size;
9456 d3.transition = function(selection, name) {
9457 return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection);
9458 };
9459 d3.transition.prototype = d3_transitionPrototype;
9460 d3_transitionPrototype.select = function(selector) {
9461 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
9462 selector = d3_selection_selector(selector);
9463 for (var j = -1, m = this.length; ++j < m; ) {
9464 subgroups.push(subgroup = []);
9465 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
9466 if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
9467 if ("__data__" in node) subnode.__data__ = node.__data__;
9468 d3_transitionNode(subnode, i, ns, id, node[ns][id]);
9469 subgroup.push(subnode);
9470 } else {
9471 subgroup.push(null);
9472 }
9473 }
9474 }
9475 return d3_transition(subgroups, ns, id);
9476 };
9477 d3_transitionPrototype.selectAll = function(selector) {
9478 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
9479 selector = d3_selection_selectorAll(selector);
9480 for (var j = -1, m = this.length; ++j < m; ) {
9481 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
9482 if (node = group[i]) {
9483 transition = node[ns][id];
9484 subnodes = selector.call(node, node.__data__, i, j);
9485 subgroups.push(subgroup = []);
9486 for (var k = -1, o = subnodes.length; ++k < o; ) {
9487 if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
9488 subgroup.push(subnode);
9489 }
9490 }
9491 }
9492 }
9493 return d3_transition(subgroups, ns, id);
9494 };
9495 d3_transitionPrototype.filter = function(filter) {
9496 var subgroups = [], subgroup, group, node;
9497 if (typeof filter !== "function") filter = d3_selection_filter(filter);
9498 for (var j = 0, m = this.length; j < m; j++) {
9499 subgroups.push(subgroup = []);
9500 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
9501 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
9502 subgroup.push(node);
9503 }
9504 }
9505 }
9506 return d3_transition(subgroups, this.namespace, this.id);
9507 };
9508 d3_transitionPrototype.tween = function(name, tween) {
9509 var id = this.id, ns = this.namespace;
9510 if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
9511 return d3_selection_each(this, tween == null ? function(node) {
9512 node[ns][id].tween.remove(name);
9513 } : function(node) {
9514 node[ns][id].tween.set(name, tween);
9515 });
9516 };
9517 function d3_transition_tween(groups, name, value, tween) {
9518 var id = groups.id, ns = groups.namespace;
9519 return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
9520 node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
9521 } : (value = tween(value), function(node) {
9522 node[ns][id].tween.set(name, value);
9523 }));
9524 }
9525 d3_transitionPrototype.attr = function(nameNS, value) {
9526 if (arguments.length < 2) {
9527 for (value in nameNS) this.attr(value, nameNS[value]);
9528 return this;
9529 }
9530 var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
9531 function attrNull() {
9532 this.removeAttribute(name);
9533 }
9534 function attrNullNS() {
9535 this.removeAttributeNS(name.space, name.local);
9536 }
9537 function attrTween(b) {
9538 return b == null ? attrNull : (b += "", function() {
9539 var a = this.getAttribute(name), i;
9540 return a !== b && (i = interpolate(a, b), function(t) {
9541 this.setAttribute(name, i(t));
9542 });
9543 });
9544 }
9545 function attrTweenNS(b) {
9546 return b == null ? attrNullNS : (b += "", function() {
9547 var a = this.getAttributeNS(name.space, name.local), i;
9548 return a !== b && (i = interpolate(a, b), function(t) {
9549 this.setAttributeNS(name.space, name.local, i(t));
9550 });
9551 });
9552 }
9553 return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
9554 };
9555 d3_transitionPrototype.attrTween = function(nameNS, tween) {
9556 var name = d3.ns.qualify(nameNS);
9557 function attrTween(d, i) {
9558 var f = tween.call(this, d, i, this.getAttribute(name));
9559 return f && function(t) {
9560 this.setAttribute(name, f(t));
9561 };
9562 }
9563 function attrTweenNS(d, i) {
9564 var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
9565 return f && function(t) {
9566 this.setAttributeNS(name.space, name.local, f(t));
9567 };
9568 }
9569 return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
9570 };
9571 d3_transitionPrototype.style = function(name, value, priority) {
9572 var n = arguments.length;
9573 if (n < 3) {
9574 if (typeof name !== "string") {
9575 if (n < 2) value = "";
9576 for (priority in name) this.style(priority, name[priority], value);
9577 return this;
9578 }
9579 priority = "";
9580 }
9581 function styleNull() {
9582 this.style.removeProperty(name);
9583 }
9584 function styleString(b) {
9585 return b == null ? styleNull : (b += "", function() {
9586 var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
9587 return a !== b && (i = d3_interpolate(a, b), function(t) {
9588 this.style.setProperty(name, i(t), priority);
9589 });
9590 });
9591 }
9592 return d3_transition_tween(this, "style." + name, value, styleString);
9593 };
9594 d3_transitionPrototype.styleTween = function(name, tween, priority) {
9595 if (arguments.length < 3) priority = "";
9596 function styleTween(d, i) {
9597 var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
9598 return f && function(t) {
9599 this.style.setProperty(name, f(t), priority);
9600 };
9601 }
9602 return this.tween("style." + name, styleTween);
9603 };
9604 d3_transitionPrototype.text = function(value) {
9605 return d3_transition_tween(this, "text", value, d3_transition_text);
9606 };
9607 function d3_transition_text(b) {
9608 if (b == null) b = "";
9609 return function() {
9610 this.textContent = b;
9611 };
9612 }
9613 d3_transitionPrototype.remove = function() {
9614 var ns = this.namespace;
9615 return this.each("end.transition", function() {
9616 var p;
9617 if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
9618 });
9619 };
9620 d3_transitionPrototype.ease = function(value) {
9621 var id = this.id, ns = this.namespace;
9622 if (arguments.length < 1) return this.node()[ns][id].ease;
9623 if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
9624 return d3_selection_each(this, function(node) {
9625 node[ns][id].ease = value;
9626 });
9627 };
9628 d3_transitionPrototype.delay = function(value) {
9629 var id = this.id, ns = this.namespace;
9630 if (arguments.length < 1) return this.node()[ns][id].delay;
9631 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
9632 node[ns][id].delay = +value.call(node, node.__data__, i, j);
9633 } : (value = +value, function(node) {
9634 node[ns][id].delay = value;
9635 }));
9636 };
9637 d3_transitionPrototype.duration = function(value) {
9638 var id = this.id, ns = this.namespace;
9639 if (arguments.length < 1) return this.node()[ns][id].duration;
9640 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
9641 node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
9642 } : (value = Math.max(1, value), function(node) {
9643 node[ns][id].duration = value;
9644 }));
9645 };
9646 d3_transitionPrototype.each = function(type, listener) {
9647 var id = this.id, ns = this.namespace;
9648 if (arguments.length < 2) {
9649 var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
9650 try {
9651 d3_transitionInheritId = id;
9652 d3_selection_each(this, function(node, i, j) {
9653 d3_transitionInherit = node[ns][id];
9654 type.call(node, node.__data__, i, j);
9655 });
9656 } finally {
9657 d3_transitionInherit = inherit;
9658 d3_transitionInheritId = inheritId;
9659 }
9660 } else {
9661 d3_selection_each(this, function(node) {
9662 var transition = node[ns][id];
9663 (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
9664 });
9665 }
9666 return this;
9667 };
9668 d3_transitionPrototype.transition = function() {
9669 var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
9670 for (var j = 0, m = this.length; j < m; j++) {
9671 subgroups.push(subgroup = []);
9672 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
9673 if (node = group[i]) {
9674 transition = node[ns][id0];
9675 d3_transitionNode(node, i, ns, id1, {
9676 time: transition.time,
9677 ease: transition.ease,
9678 delay: transition.delay + transition.duration,
9679 duration: transition.duration
9680 });
9681 }
9682 subgroup.push(node);
9683 }
9684 }
9685 return d3_transition(subgroups, ns, id1);
9686 };
9687 function d3_transitionNamespace(name) {
9688 return name == null ? "__transition__" : "__transition_" + name + "__";
9689 }
9690 function d3_transitionNode(node, i, ns, id, inherit) {
9691 var lock = node[ns] || (node[ns] = {
9692 active: 0,
9693 count: 0
9694 }), transition = lock[id], time, timer, duration, ease, tweens;
9695 function schedule(elapsed) {
9696 var delay = transition.delay;
9697 timer.t = delay + time;
9698 if (delay <= elapsed) return start(elapsed - delay);
9699 timer.c = start;
9700 }
9701 function start(elapsed) {
9702 var activeId = lock.active, active = lock[activeId];
9703 if (active) {
9704 active.timer.c = null;
9705 active.timer.t = NaN;
9706 --lock.count;
9707 delete lock[activeId];
9708 active.event && active.event.interrupt.call(node, node.__data__, active.index);
9709 }
9710 for (var cancelId in lock) {
9711 if (+cancelId < id) {
9712 var cancel = lock[cancelId];
9713 cancel.timer.c = null;
9714 cancel.timer.t = NaN;
9715 --lock.count;
9716 delete lock[cancelId];
9717 }
9718 }
9719 timer.c = tick;
9720 d3_timer(function() {
9721 if (timer.c && tick(elapsed || 1)) {
9722 timer.c = null;
9723 timer.t = NaN;
9724 }
9725 return 1;
9726 }, 0, time);
9727 lock.active = id;
9728 transition.event && transition.event.start.call(node, node.__data__, i);
9729 tweens = [];
9730 transition.tween.forEach(function(key, value) {
9731 if (value = value.call(node, node.__data__, i)) {
9732 tweens.push(value);
9733 }
9734 });
9735 ease = transition.ease;
9736 duration = transition.duration;
9737 }
9738 function tick(elapsed) {
9739 var t = elapsed / duration, e = ease(t), n = tweens.length;
9740 while (n > 0) {
9741 tweens[--n].call(node, e);
9742 }
9743 if (t >= 1) {
9744 transition.event && transition.event.end.call(node, node.__data__, i);
9745 if (--lock.count) delete lock[id]; else delete node[ns];
9746 return 1;
9747 }
9748 }
9749 if (!transition) {
9750 time = inherit.time;
9751 timer = d3_timer(schedule, 0, time);
9752 transition = lock[id] = {
9753 tween: new d3_Map(),
9754 time: time,
9755 timer: timer,
9756 delay: inherit.delay,
9757 duration: inherit.duration,
9758 ease: inherit.ease,
9759 index: i
9760 };
9761 inherit = null;
9762 ++lock.count;
9763 }
9764 }
9765 d3.svg.axis = function() {
9766 var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
9767 function axis(g) {
9768 g.each(function() {
9769 var g = d3.select(this);
9770 var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
9771 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;
9772 var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
9773 d3.transition(path));
9774 tickEnter.append("line");
9775 tickEnter.append("text");
9776 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;
9777 if (orient === "bottom" || orient === "top") {
9778 tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
9779 text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
9780 pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
9781 } else {
9782 tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
9783 text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
9784 pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
9785 }
9786 lineEnter.attr(y2, sign * innerTickSize);
9787 textEnter.attr(y1, sign * tickSpacing);
9788 lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
9789 textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
9790 if (scale1.rangeBand) {
9791 var x = scale1, dx = x.rangeBand() / 2;
9792 scale0 = scale1 = function(d) {
9793 return x(d) + dx;
9794 };
9795 } else if (scale0.rangeBand) {
9796 scale0 = scale1;
9797 } else {
9798 tickExit.call(tickTransform, scale1, scale0);
9799 }
9800 tickEnter.call(tickTransform, scale0, scale1);
9801 tickUpdate.call(tickTransform, scale1, scale1);
9802 });
9803 }
9804 axis.scale = function(x) {
9805 if (!arguments.length) return scale;
9806 scale = x;
9807 return axis;
9808 };
9809 axis.orient = function(x) {
9810 if (!arguments.length) return orient;
9811 orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
9812 return axis;
9813 };
9814 axis.ticks = function() {
9815 if (!arguments.length) return tickArguments_;
9816 tickArguments_ = d3_array(arguments);
9817 return axis;
9818 };
9819 axis.tickValues = function(x) {
9820 if (!arguments.length) return tickValues;
9821 tickValues = x;
9822 return axis;
9823 };
9824 axis.tickFormat = function(x) {
9825 if (!arguments.length) return tickFormat_;
9826 tickFormat_ = x;
9827 return axis;
9828 };
9829 axis.tickSize = function(x) {
9830 var n = arguments.length;
9831 if (!n) return innerTickSize;
9832 innerTickSize = +x;
9833 outerTickSize = +arguments[n - 1];
9834 return axis;
9835 };
9836 axis.innerTickSize = function(x) {
9837 if (!arguments.length) return innerTickSize;
9838 innerTickSize = +x;
9839 return axis;
9840 };
9841 axis.outerTickSize = function(x) {
9842 if (!arguments.length) return outerTickSize;
9843 outerTickSize = +x;
9844 return axis;
9845 };
9846 axis.tickPadding = function(x) {
9847 if (!arguments.length) return tickPadding;
9848 tickPadding = +x;
9849 return axis;
9850 };
9851 axis.tickSubdivide = function() {
9852 return arguments.length && axis;
9853 };
9854 return axis;
9855 };
9856 var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
9857 top: 1,
9858 right: 1,
9859 bottom: 1,
9860 left: 1
9861 };
9862 function d3_svg_axisX(selection, x0, x1) {
9863 selection.attr("transform", function(d) {
9864 var v0 = x0(d);
9865 return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
9866 });
9867 }
9868 function d3_svg_axisY(selection, y0, y1) {
9869 selection.attr("transform", function(d) {
9870 var v0 = y0(d);
9871 return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
9872 });
9873 }
9874 d3.svg.brush = function() {
9875 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];
9876 function brush(g) {
9877 g.each(function() {
9878 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);
9879 var background = g.selectAll(".background").data([ 0 ]);
9880 background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
9881 g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
9882 var resize = g.selectAll(".resize").data(resizes, d3_identity);
9883 resize.exit().remove();
9884 resize.enter().append("g").attr("class", function(d) {
9885 return "resize " + d;
9886 }).style("cursor", function(d) {
9887 return d3_svg_brushCursor[d];
9888 }).append("rect").attr("x", function(d) {
9889 return /[ew]$/.test(d) ? -3 : null;
9890 }).attr("y", function(d) {
9891 return /^[ns]/.test(d) ? -3 : null;
9892 }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
9893 resize.style("display", brush.empty() ? "none" : null);
9894 var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
9895 if (x) {
9896 range = d3_scaleRange(x);
9897 backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
9898 redrawX(gUpdate);
9899 }
9900 if (y) {
9901 range = d3_scaleRange(y);
9902 backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
9903 redrawY(gUpdate);
9904 }
9905 redraw(gUpdate);
9906 });
9907 }
9908 brush.event = function(g) {
9909 g.each(function() {
9910 var event_ = event.of(this, arguments), extent1 = {
9911 x: xExtent,
9912 y: yExtent,
9913 i: xExtentDomain,
9914 j: yExtentDomain
9915 }, extent0 = this.__chart__ || extent1;
9916 this.__chart__ = extent1;
9917 if (d3_transitionInheritId) {
9918 d3.select(this).transition().each("start.brush", function() {
9919 xExtentDomain = extent0.i;
9920 yExtentDomain = extent0.j;
9921 xExtent = extent0.x;
9922 yExtent = extent0.y;
9923 event_({
9924 type: "brushstart"
9925 });
9926 }).tween("brush:brush", function() {
9927 var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
9928 xExtentDomain = yExtentDomain = null;
9929 return function(t) {
9930 xExtent = extent1.x = xi(t);
9931 yExtent = extent1.y = yi(t);
9932 event_({
9933 type: "brush",
9934 mode: "resize"
9935 });
9936 };
9937 }).each("end.brush", function() {
9938 xExtentDomain = extent1.i;
9939 yExtentDomain = extent1.j;
9940 event_({
9941 type: "brush",
9942 mode: "resize"
9943 });
9944 event_({
9945 type: "brushend"
9946 });
9947 });
9948 } else {
9949 event_({
9950 type: "brushstart"
9951 });
9952 event_({
9953 type: "brush",
9954 mode: "resize"
9955 });
9956 event_({
9957 type: "brushend"
9958 });
9959 }
9960 });
9961 };
9962 function redraw(g) {
9963 g.selectAll(".resize").attr("transform", function(d) {
9964 return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
9965 });
9966 }
9967 function redrawX(g) {
9968 g.select(".extent").attr("x", xExtent[0]);
9969 g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
9970 }
9971 function redrawY(g) {
9972 g.select(".extent").attr("y", yExtent[0]);
9973 g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
9974 }
9975 function brushstart() {
9976 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;
9977 var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup);
9978 if (d3.event.changedTouches) {
9979 w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
9980 } else {
9981 w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
9982 }
9983 g.interrupt().selectAll("*").interrupt();
9984 if (dragging) {
9985 origin[0] = xExtent[0] - origin[0];
9986 origin[1] = yExtent[0] - origin[1];
9987 } else if (resizing) {
9988 var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
9989 offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
9990 origin[0] = xExtent[ex];
9991 origin[1] = yExtent[ey];
9992 } else if (d3.event.altKey) center = origin.slice();
9993 g.style("pointer-events", "none").selectAll(".resize").style("display", null);
9994 d3.select("body").style("cursor", eventTarget.style("cursor"));
9995 event_({
9996 type: "brushstart"
9997 });
9998 brushmove();
9999 function keydown() {
10000 if (d3.event.keyCode == 32) {
10001 if (!dragging) {
10002 center = null;
10003 origin[0] -= xExtent[1];
10004 origin[1] -= yExtent[1];
10005 dragging = 2;
10006 }
10007 d3_eventPreventDefault();
10008 }
10009 }
10010 function keyup() {
10011 if (d3.event.keyCode == 32 && dragging == 2) {
10012 origin[0] += xExtent[1];
10013 origin[1] += yExtent[1];
10014 dragging = 0;
10015 d3_eventPreventDefault();
10016 }
10017 }
10018 function brushmove() {
10019 var point = d3.mouse(target), moved = false;
10020 if (offset) {
10021 point[0] += offset[0];
10022 point[1] += offset[1];
10023 }
10024 if (!dragging) {
10025 if (d3.event.altKey) {
10026 if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
10027 origin[0] = xExtent[+(point[0] < center[0])];
10028 origin[1] = yExtent[+(point[1] < center[1])];
10029 } else center = null;
10030 }
10031 if (resizingX && move1(point, x, 0)) {
10032 redrawX(g);
10033 moved = true;
10034 }
10035 if (resizingY && move1(point, y, 1)) {
10036 redrawY(g);
10037 moved = true;
10038 }
10039 if (moved) {
10040 redraw(g);
10041 event_({
10042 type: "brush",
10043 mode: dragging ? "move" : "resize"
10044 });
10045 }
10046 }
10047 function move1(point, scale, i) {
10048 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;
10049 if (dragging) {
10050 r0 -= position;
10051 r1 -= size + position;
10052 }
10053 min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
10054 if (dragging) {
10055 max = (min += position) + size;
10056 } else {
10057 if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
10058 if (position < min) {
10059 max = min;
10060 min = position;
10061 } else {
10062 max = position;
10063 }
10064 }
10065 if (extent[0] != min || extent[1] != max) {
10066 if (i) yExtentDomain = null; else xExtentDomain = null;
10067 extent[0] = min;
10068 extent[1] = max;
10069 return true;
10070 }
10071 }
10072 function brushend() {
10073 brushmove();
10074 g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
10075 d3.select("body").style("cursor", null);
10076 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);
10077 dragRestore();
10078 event_({
10079 type: "brushend"
10080 });
10081 }
10082 }
10083 brush.x = function(z) {
10084 if (!arguments.length) return x;
10085 x = z;
10086 resizes = d3_svg_brushResizes[!x << 1 | !y];
10087 return brush;
10088 };
10089 brush.y = function(z) {
10090 if (!arguments.length) return y;
10091 y = z;
10092 resizes = d3_svg_brushResizes[!x << 1 | !y];
10093 return brush;
10094 };
10095 brush.clamp = function(z) {
10096 if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
10097 if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
10098 return brush;
10099 };
10100 brush.extent = function(z) {
10101 var x0, x1, y0, y1, t;
10102 if (!arguments.length) {
10103 if (x) {
10104 if (xExtentDomain) {
10105 x0 = xExtentDomain[0], x1 = xExtentDomain[1];
10106 } else {
10107 x0 = xExtent[0], x1 = xExtent[1];
10108 if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
10109 if (x1 < x0) t = x0, x0 = x1, x1 = t;
10110 }
10111 }
10112 if (y) {
10113 if (yExtentDomain) {
10114 y0 = yExtentDomain[0], y1 = yExtentDomain[1];
10115 } else {
10116 y0 = yExtent[0], y1 = yExtent[1];
10117 if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
10118 if (y1 < y0) t = y0, y0 = y1, y1 = t;
10119 }
10120 }
10121 return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
10122 }
10123 if (x) {
10124 x0 = z[0], x1 = z[1];
10125 if (y) x0 = x0[0], x1 = x1[0];
10126 xExtentDomain = [ x0, x1 ];
10127 if (x.invert) x0 = x(x0), x1 = x(x1);
10128 if (x1 < x0) t = x0, x0 = x1, x1 = t;
10129 if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
10130 }
10131 if (y) {
10132 y0 = z[0], y1 = z[1];
10133 if (x) y0 = y0[1], y1 = y1[1];
10134 yExtentDomain = [ y0, y1 ];
10135 if (y.invert) y0 = y(y0), y1 = y(y1);
10136 if (y1 < y0) t = y0, y0 = y1, y1 = t;
10137 if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
10138 }
10139 return brush;
10140 };
10141 brush.clear = function() {
10142 if (!brush.empty()) {
10143 xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
10144 xExtentDomain = yExtentDomain = null;
10145 }
10146 return brush;
10147 };
10148 brush.empty = function() {
10149 return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
10150 };
10151 return d3.rebind(brush, event, "on");
10152 };
10153 var d3_svg_brushCursor = {
10154 n: "ns-resize",
10155 e: "ew-resize",
10156 s: "ns-resize",
10157 w: "ew-resize",
10158 nw: "nwse-resize",
10159 ne: "nesw-resize",
10160 se: "nwse-resize",
10161 sw: "nesw-resize"
10162 };
10163 var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
10164 var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
10165 var d3_time_formatUtc = d3_time_format.utc;
10166 var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
10167 d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
10168 function d3_time_formatIsoNative(date) {
10169 return date.toISOString();
10170 }
10171 d3_time_formatIsoNative.parse = function(string) {
10172 var date = new Date(string);
10173 return isNaN(date) ? null : date;
10174 };
10175 d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
10176 d3_time.second = d3_time_interval(function(date) {
10177 return new d3_date(Math.floor(date / 1e3) * 1e3);
10178 }, function(date, offset) {
10179 date.setTime(date.getTime() + Math.floor(offset) * 1e3);
10180 }, function(date) {
10181 return date.getSeconds();
10182 });
10183 d3_time.seconds = d3_time.second.range;
10184 d3_time.seconds.utc = d3_time.second.utc.range;
10185 d3_time.minute = d3_time_interval(function(date) {
10186 return new d3_date(Math.floor(date / 6e4) * 6e4);
10187 }, function(date, offset) {
10188 date.setTime(date.getTime() + Math.floor(offset) * 6e4);
10189 }, function(date) {
10190 return date.getMinutes();
10191 });
10192 d3_time.minutes = d3_time.minute.range;
10193 d3_time.minutes.utc = d3_time.minute.utc.range;
10194 d3_time.hour = d3_time_interval(function(date) {
10195 var timezone = date.getTimezoneOffset() / 60;
10196 return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
10197 }, function(date, offset) {
10198 date.setTime(date.getTime() + Math.floor(offset) * 36e5);
10199 }, function(date) {
10200 return date.getHours();
10201 });
10202 d3_time.hours = d3_time.hour.range;
10203 d3_time.hours.utc = d3_time.hour.utc.range;
10204 d3_time.month = d3_time_interval(function(date) {
10205 date = d3_time.day(date);
10206 date.setDate(1);
10207 return date;
10208 }, function(date, offset) {
10209 date.setMonth(date.getMonth() + offset);
10210 }, function(date) {
10211 return date.getMonth();
10212 });
10213 d3_time.months = d3_time.month.range;
10214 d3_time.months.utc = d3_time.month.utc.range;
10215 function d3_time_scale(linear, methods, format) {
10216 function scale(x) {
10217 return linear(x);
10218 }
10219 scale.invert = function(x) {
10220 return d3_time_scaleDate(linear.invert(x));
10221 };
10222 scale.domain = function(x) {
10223 if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
10224 linear.domain(x);
10225 return scale;
10226 };
10227 function tickMethod(extent, count) {
10228 var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
10229 return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
10230 return d / 31536e6;
10231 }), 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];
10232 }
10233 scale.nice = function(interval, skip) {
10234 var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
10235 if (method) interval = method[0], skip = method[1];
10236 function skipped(date) {
10237 return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
10238 }
10239 return scale.domain(d3_scale_nice(domain, skip > 1 ? {
10240 floor: function(date) {
10241 while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
10242 return date;
10243 },
10244 ceil: function(date) {
10245 while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
10246 return date;
10247 }
10248 } : interval));
10249 };
10250 scale.ticks = function(interval, skip) {
10251 var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
10252 range: interval
10253 }, skip ];
10254 if (method) interval = method[0], skip = method[1];
10255 return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
10256 };
10257 scale.tickFormat = function() {
10258 return format;
10259 };
10260 scale.copy = function() {
10261 return d3_time_scale(linear.copy(), methods, format);
10262 };
10263 return d3_scale_linearRebind(scale, linear);
10264 }
10265 function d3_time_scaleDate(t) {
10266 return new Date(t);
10267 }
10268 var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
10269 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 ] ];
10270 var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
10271 return d.getMilliseconds();
10272 } ], [ ":%S", function(d) {
10273 return d.getSeconds();
10274 } ], [ "%I:%M", function(d) {
10275 return d.getMinutes();
10276 } ], [ "%I %p", function(d) {
10277 return d.getHours();
10278 } ], [ "%a %d", function(d) {
10279 return d.getDay() && d.getDate() != 1;
10280 } ], [ "%b %d", function(d) {
10281 return d.getDate() != 1;
10282 } ], [ "%B", function(d) {
10283 return d.getMonth();
10284 } ], [ "%Y", d3_true ] ]);
10285 var d3_time_scaleMilliseconds = {
10286 range: function(start, stop, step) {
10287 return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
10288 },
10289 floor: d3_identity,
10290 ceil: d3_identity
10291 };
10292 d3_time_scaleLocalMethods.year = d3_time.year;
10293 d3_time.scale = function() {
10294 return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
10295 };
10296 var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
10297 return [ m[0].utc, m[1] ];
10298 });
10299 var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
10300 return d.getUTCMilliseconds();
10301 } ], [ ":%S", function(d) {
10302 return d.getUTCSeconds();
10303 } ], [ "%I:%M", function(d) {
10304 return d.getUTCMinutes();
10305 } ], [ "%I %p", function(d) {
10306 return d.getUTCHours();
10307 } ], [ "%a %d", function(d) {
10308 return d.getUTCDay() && d.getUTCDate() != 1;
10309 } ], [ "%b %d", function(d) {
10310 return d.getUTCDate() != 1;
10311 } ], [ "%B", function(d) {
10312 return d.getUTCMonth();
10313 } ], [ "%Y", d3_true ] ]);
10314 d3_time_scaleUtcMethods.year = d3_time.year.utc;
10315 d3_time.scale.utc = function() {
10316 return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
10317 };
10318 d3.text = d3_xhrType(function(request) {
10319 return request.responseText;
10320 });
10321 d3.json = function(url, callback) {
10322 return d3_xhr(url, "application/json", d3_json, callback);
10323 };
10324 function d3_json(request) {
10325 return JSON.parse(request.responseText);
10326 }
10327 d3.html = function(url, callback) {
10328 return d3_xhr(url, "text/html", d3_html, callback);
10329 };
10330 function d3_html(request) {
10331 var range = d3_document.createRange();
10332 range.selectNode(d3_document.body);
10333 return range.createContextualFragment(request.responseText);
10334 }
10335 d3.xml = d3_xhrType(function(request) {
10336 return request.responseXML;
10337 });
10338 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;
10339}();
10340},{}],17:[function(_dereq_,module,exports){
10341(function (process,global){
10342/*!
10343 * @overview es6-promise - a tiny implementation of Promises/A+.
10344 * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
10345 * @license Licensed under MIT license
10346 * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
10347 * @version v4.2.8+1e68dce6
10348 */
10349
10350(function (global, factory) {
10351 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
10352 typeof define === 'function' && define.amd ? define(factory) :
10353 (global.ES6Promise = factory());
10354}(this, (function () { 'use strict';
10355
10356function objectOrFunction(x) {
10357 var type = typeof x;
10358 return x !== null && (type === 'object' || type === 'function');
10359}
10360
10361function isFunction(x) {
10362 return typeof x === 'function';
10363}
10364
10365
10366
10367var _isArray = void 0;
10368if (Array.isArray) {
10369 _isArray = Array.isArray;
10370} else {
10371 _isArray = function (x) {
10372 return Object.prototype.toString.call(x) === '[object Array]';
10373 };
10374}
10375
10376var isArray = _isArray;
10377
10378var len = 0;
10379var vertxNext = void 0;
10380var customSchedulerFn = void 0;
10381
10382var asap = function asap(callback, arg) {
10383 queue[len] = callback;
10384 queue[len + 1] = arg;
10385 len += 2;
10386 if (len === 2) {
10387 // If len is 2, that means that we need to schedule an async flush.
10388 // If additional callbacks are queued before the queue is flushed, they
10389 // will be processed by this flush that we are scheduling.
10390 if (customSchedulerFn) {
10391 customSchedulerFn(flush);
10392 } else {
10393 scheduleFlush();
10394 }
10395 }
10396};
10397
10398function setScheduler(scheduleFn) {
10399 customSchedulerFn = scheduleFn;
10400}
10401
10402function setAsap(asapFn) {
10403 asap = asapFn;
10404}
10405
10406var browserWindow = typeof window !== 'undefined' ? window : undefined;
10407var browserGlobal = browserWindow || {};
10408var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
10409var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
10410
10411// test for web worker but not in IE10
10412var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
10413
10414// node
10415function useNextTick() {
10416 // node version 0.10.x displays a deprecation warning when nextTick is used recursively
10417 // see https://github.com/cujojs/when/issues/410 for details
10418 return function () {
10419 return process.nextTick(flush);
10420 };
10421}
10422
10423// vertx
10424function useVertxTimer() {
10425 if (typeof vertxNext !== 'undefined') {
10426 return function () {
10427 vertxNext(flush);
10428 };
10429 }
10430
10431 return useSetTimeout();
10432}
10433
10434function useMutationObserver() {
10435 var iterations = 0;
10436 var observer = new BrowserMutationObserver(flush);
10437 var node = document.createTextNode('');
10438 observer.observe(node, { characterData: true });
10439
10440 return function () {
10441 node.data = iterations = ++iterations % 2;
10442 };
10443}
10444
10445// web worker
10446function useMessageChannel() {
10447 var channel = new MessageChannel();
10448 channel.port1.onmessage = flush;
10449 return function () {
10450 return channel.port2.postMessage(0);
10451 };
10452}
10453
10454function useSetTimeout() {
10455 // Store setTimeout reference so es6-promise will be unaffected by
10456 // other code modifying setTimeout (like sinon.useFakeTimers())
10457 var globalSetTimeout = setTimeout;
10458 return function () {
10459 return globalSetTimeout(flush, 1);
10460 };
10461}
10462
10463var queue = new Array(1000);
10464function flush() {
10465 for (var i = 0; i < len; i += 2) {
10466 var callback = queue[i];
10467 var arg = queue[i + 1];
10468
10469 callback(arg);
10470
10471 queue[i] = undefined;
10472 queue[i + 1] = undefined;
10473 }
10474
10475 len = 0;
10476}
10477
10478function attemptVertx() {
10479 try {
10480 var vertx = Function('return this')().require('vertx');
10481 vertxNext = vertx.runOnLoop || vertx.runOnContext;
10482 return useVertxTimer();
10483 } catch (e) {
10484 return useSetTimeout();
10485 }
10486}
10487
10488var scheduleFlush = void 0;
10489// Decide what async method to use to triggering processing of queued callbacks:
10490if (isNode) {
10491 scheduleFlush = useNextTick();
10492} else if (BrowserMutationObserver) {
10493 scheduleFlush = useMutationObserver();
10494} else if (isWorker) {
10495 scheduleFlush = useMessageChannel();
10496} else if (browserWindow === undefined && typeof _dereq_ === 'function') {
10497 scheduleFlush = attemptVertx();
10498} else {
10499 scheduleFlush = useSetTimeout();
10500}
10501
10502function then(onFulfillment, onRejection) {
10503 var parent = this;
10504
10505 var child = new this.constructor(noop);
10506
10507 if (child[PROMISE_ID] === undefined) {
10508 makePromise(child);
10509 }
10510
10511 var _state = parent._state;
10512
10513
10514 if (_state) {
10515 var callback = arguments[_state - 1];
10516 asap(function () {
10517 return invokeCallback(_state, child, callback, parent._result);
10518 });
10519 } else {
10520 subscribe(parent, child, onFulfillment, onRejection);
10521 }
10522
10523 return child;
10524}
10525
10526/**
10527 `Promise.resolve` returns a promise that will become resolved with the
10528 passed `value`. It is shorthand for the following:
10529
10530 ```javascript
10531 let promise = new Promise(function(resolve, reject){
10532 resolve(1);
10533 });
10534
10535 promise.then(function(value){
10536 // value === 1
10537 });
10538 ```
10539
10540 Instead of writing the above, your code now simply becomes the following:
10541
10542 ```javascript
10543 let promise = Promise.resolve(1);
10544
10545 promise.then(function(value){
10546 // value === 1
10547 });
10548 ```
10549
10550 @method resolve
10551 @static
10552 @param {Any} value value that the returned promise will be resolved with
10553 Useful for tooling.
10554 @return {Promise} a promise that will become fulfilled with the given
10555 `value`
10556*/
10557function resolve$1(object) {
10558 /*jshint validthis:true */
10559 var Constructor = this;
10560
10561 if (object && typeof object === 'object' && object.constructor === Constructor) {
10562 return object;
10563 }
10564
10565 var promise = new Constructor(noop);
10566 resolve(promise, object);
10567 return promise;
10568}
10569
10570var PROMISE_ID = Math.random().toString(36).substring(2);
10571
10572function noop() {}
10573
10574var PENDING = void 0;
10575var FULFILLED = 1;
10576var REJECTED = 2;
10577
10578function selfFulfillment() {
10579 return new TypeError("You cannot resolve a promise with itself");
10580}
10581
10582function cannotReturnOwn() {
10583 return new TypeError('A promises callback cannot return that same promise.');
10584}
10585
10586function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
10587 try {
10588 then$$1.call(value, fulfillmentHandler, rejectionHandler);
10589 } catch (e) {
10590 return e;
10591 }
10592}
10593
10594function handleForeignThenable(promise, thenable, then$$1) {
10595 asap(function (promise) {
10596 var sealed = false;
10597 var error = tryThen(then$$1, thenable, function (value) {
10598 if (sealed) {
10599 return;
10600 }
10601 sealed = true;
10602 if (thenable !== value) {
10603 resolve(promise, value);
10604 } else {
10605 fulfill(promise, value);
10606 }
10607 }, function (reason) {
10608 if (sealed) {
10609 return;
10610 }
10611 sealed = true;
10612
10613 reject(promise, reason);
10614 }, 'Settle: ' + (promise._label || ' unknown promise'));
10615
10616 if (!sealed && error) {
10617 sealed = true;
10618 reject(promise, error);
10619 }
10620 }, promise);
10621}
10622
10623function handleOwnThenable(promise, thenable) {
10624 if (thenable._state === FULFILLED) {
10625 fulfill(promise, thenable._result);
10626 } else if (thenable._state === REJECTED) {
10627 reject(promise, thenable._result);
10628 } else {
10629 subscribe(thenable, undefined, function (value) {
10630 return resolve(promise, value);
10631 }, function (reason) {
10632 return reject(promise, reason);
10633 });
10634 }
10635}
10636
10637function handleMaybeThenable(promise, maybeThenable, then$$1) {
10638 if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
10639 handleOwnThenable(promise, maybeThenable);
10640 } else {
10641 if (then$$1 === undefined) {
10642 fulfill(promise, maybeThenable);
10643 } else if (isFunction(then$$1)) {
10644 handleForeignThenable(promise, maybeThenable, then$$1);
10645 } else {
10646 fulfill(promise, maybeThenable);
10647 }
10648 }
10649}
10650
10651function resolve(promise, value) {
10652 if (promise === value) {
10653 reject(promise, selfFulfillment());
10654 } else if (objectOrFunction(value)) {
10655 var then$$1 = void 0;
10656 try {
10657 then$$1 = value.then;
10658 } catch (error) {
10659 reject(promise, error);
10660 return;
10661 }
10662 handleMaybeThenable(promise, value, then$$1);
10663 } else {
10664 fulfill(promise, value);
10665 }
10666}
10667
10668function publishRejection(promise) {
10669 if (promise._onerror) {
10670 promise._onerror(promise._result);
10671 }
10672
10673 publish(promise);
10674}
10675
10676function fulfill(promise, value) {
10677 if (promise._state !== PENDING) {
10678 return;
10679 }
10680
10681 promise._result = value;
10682 promise._state = FULFILLED;
10683
10684 if (promise._subscribers.length !== 0) {
10685 asap(publish, promise);
10686 }
10687}
10688
10689function reject(promise, reason) {
10690 if (promise._state !== PENDING) {
10691 return;
10692 }
10693 promise._state = REJECTED;
10694 promise._result = reason;
10695
10696 asap(publishRejection, promise);
10697}
10698
10699function subscribe(parent, child, onFulfillment, onRejection) {
10700 var _subscribers = parent._subscribers;
10701 var length = _subscribers.length;
10702
10703
10704 parent._onerror = null;
10705
10706 _subscribers[length] = child;
10707 _subscribers[length + FULFILLED] = onFulfillment;
10708 _subscribers[length + REJECTED] = onRejection;
10709
10710 if (length === 0 && parent._state) {
10711 asap(publish, parent);
10712 }
10713}
10714
10715function publish(promise) {
10716 var subscribers = promise._subscribers;
10717 var settled = promise._state;
10718
10719 if (subscribers.length === 0) {
10720 return;
10721 }
10722
10723 var child = void 0,
10724 callback = void 0,
10725 detail = promise._result;
10726
10727 for (var i = 0; i < subscribers.length; i += 3) {
10728 child = subscribers[i];
10729 callback = subscribers[i + settled];
10730
10731 if (child) {
10732 invokeCallback(settled, child, callback, detail);
10733 } else {
10734 callback(detail);
10735 }
10736 }
10737
10738 promise._subscribers.length = 0;
10739}
10740
10741function invokeCallback(settled, promise, callback, detail) {
10742 var hasCallback = isFunction(callback),
10743 value = void 0,
10744 error = void 0,
10745 succeeded = true;
10746
10747 if (hasCallback) {
10748 try {
10749 value = callback(detail);
10750 } catch (e) {
10751 succeeded = false;
10752 error = e;
10753 }
10754
10755 if (promise === value) {
10756 reject(promise, cannotReturnOwn());
10757 return;
10758 }
10759 } else {
10760 value = detail;
10761 }
10762
10763 if (promise._state !== PENDING) {
10764 // noop
10765 } else if (hasCallback && succeeded) {
10766 resolve(promise, value);
10767 } else if (succeeded === false) {
10768 reject(promise, error);
10769 } else if (settled === FULFILLED) {
10770 fulfill(promise, value);
10771 } else if (settled === REJECTED) {
10772 reject(promise, value);
10773 }
10774}
10775
10776function initializePromise(promise, resolver) {
10777 try {
10778 resolver(function resolvePromise(value) {
10779 resolve(promise, value);
10780 }, function rejectPromise(reason) {
10781 reject(promise, reason);
10782 });
10783 } catch (e) {
10784 reject(promise, e);
10785 }
10786}
10787
10788var id = 0;
10789function nextId() {
10790 return id++;
10791}
10792
10793function makePromise(promise) {
10794 promise[PROMISE_ID] = id++;
10795 promise._state = undefined;
10796 promise._result = undefined;
10797 promise._subscribers = [];
10798}
10799
10800function validationError() {
10801 return new Error('Array Methods must be provided an Array');
10802}
10803
10804var Enumerator = function () {
10805 function Enumerator(Constructor, input) {
10806 this._instanceConstructor = Constructor;
10807 this.promise = new Constructor(noop);
10808
10809 if (!this.promise[PROMISE_ID]) {
10810 makePromise(this.promise);
10811 }
10812
10813 if (isArray(input)) {
10814 this.length = input.length;
10815 this._remaining = input.length;
10816
10817 this._result = new Array(this.length);
10818
10819 if (this.length === 0) {
10820 fulfill(this.promise, this._result);
10821 } else {
10822 this.length = this.length || 0;
10823 this._enumerate(input);
10824 if (this._remaining === 0) {
10825 fulfill(this.promise, this._result);
10826 }
10827 }
10828 } else {
10829 reject(this.promise, validationError());
10830 }
10831 }
10832
10833 Enumerator.prototype._enumerate = function _enumerate(input) {
10834 for (var i = 0; this._state === PENDING && i < input.length; i++) {
10835 this._eachEntry(input[i], i);
10836 }
10837 };
10838
10839 Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {
10840 var c = this._instanceConstructor;
10841 var resolve$$1 = c.resolve;
10842
10843
10844 if (resolve$$1 === resolve$1) {
10845 var _then = void 0;
10846 var error = void 0;
10847 var didError = false;
10848 try {
10849 _then = entry.then;
10850 } catch (e) {
10851 didError = true;
10852 error = e;
10853 }
10854
10855 if (_then === then && entry._state !== PENDING) {
10856 this._settledAt(entry._state, i, entry._result);
10857 } else if (typeof _then !== 'function') {
10858 this._remaining--;
10859 this._result[i] = entry;
10860 } else if (c === Promise$1) {
10861 var promise = new c(noop);
10862 if (didError) {
10863 reject(promise, error);
10864 } else {
10865 handleMaybeThenable(promise, entry, _then);
10866 }
10867 this._willSettleAt(promise, i);
10868 } else {
10869 this._willSettleAt(new c(function (resolve$$1) {
10870 return resolve$$1(entry);
10871 }), i);
10872 }
10873 } else {
10874 this._willSettleAt(resolve$$1(entry), i);
10875 }
10876 };
10877
10878 Enumerator.prototype._settledAt = function _settledAt(state, i, value) {
10879 var promise = this.promise;
10880
10881
10882 if (promise._state === PENDING) {
10883 this._remaining--;
10884
10885 if (state === REJECTED) {
10886 reject(promise, value);
10887 } else {
10888 this._result[i] = value;
10889 }
10890 }
10891
10892 if (this._remaining === 0) {
10893 fulfill(promise, this._result);
10894 }
10895 };
10896
10897 Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {
10898 var enumerator = this;
10899
10900 subscribe(promise, undefined, function (value) {
10901 return enumerator._settledAt(FULFILLED, i, value);
10902 }, function (reason) {
10903 return enumerator._settledAt(REJECTED, i, reason);
10904 });
10905 };
10906
10907 return Enumerator;
10908}();
10909
10910/**
10911 `Promise.all` accepts an array of promises, and returns a new promise which
10912 is fulfilled with an array of fulfillment values for the passed promises, or
10913 rejected with the reason of the first passed promise to be rejected. It casts all
10914 elements of the passed iterable to promises as it runs this algorithm.
10915
10916 Example:
10917
10918 ```javascript
10919 let promise1 = resolve(1);
10920 let promise2 = resolve(2);
10921 let promise3 = resolve(3);
10922 let promises = [ promise1, promise2, promise3 ];
10923
10924 Promise.all(promises).then(function(array){
10925 // The array here would be [ 1, 2, 3 ];
10926 });
10927 ```
10928
10929 If any of the `promises` given to `all` are rejected, the first promise
10930 that is rejected will be given as an argument to the returned promises's
10931 rejection handler. For example:
10932
10933 Example:
10934
10935 ```javascript
10936 let promise1 = resolve(1);
10937 let promise2 = reject(new Error("2"));
10938 let promise3 = reject(new Error("3"));
10939 let promises = [ promise1, promise2, promise3 ];
10940
10941 Promise.all(promises).then(function(array){
10942 // Code here never runs because there are rejected promises!
10943 }, function(error) {
10944 // error.message === "2"
10945 });
10946 ```
10947
10948 @method all
10949 @static
10950 @param {Array} entries array of promises
10951 @param {String} label optional string for labeling the promise.
10952 Useful for tooling.
10953 @return {Promise} promise that is fulfilled when all `promises` have been
10954 fulfilled, or rejected if any of them become rejected.
10955 @static
10956*/
10957function all(entries) {
10958 return new Enumerator(this, entries).promise;
10959}
10960
10961/**
10962 `Promise.race` returns a new promise which is settled in the same way as the
10963 first passed promise to settle.
10964
10965 Example:
10966
10967 ```javascript
10968 let promise1 = new Promise(function(resolve, reject){
10969 setTimeout(function(){
10970 resolve('promise 1');
10971 }, 200);
10972 });
10973
10974 let promise2 = new Promise(function(resolve, reject){
10975 setTimeout(function(){
10976 resolve('promise 2');
10977 }, 100);
10978 });
10979
10980 Promise.race([promise1, promise2]).then(function(result){
10981 // result === 'promise 2' because it was resolved before promise1
10982 // was resolved.
10983 });
10984 ```
10985
10986 `Promise.race` is deterministic in that only the state of the first
10987 settled promise matters. For example, even if other promises given to the
10988 `promises` array argument are resolved, but the first settled promise has
10989 become rejected before the other promises became fulfilled, the returned
10990 promise will become rejected:
10991
10992 ```javascript
10993 let promise1 = new Promise(function(resolve, reject){
10994 setTimeout(function(){
10995 resolve('promise 1');
10996 }, 200);
10997 });
10998
10999 let promise2 = new Promise(function(resolve, reject){
11000 setTimeout(function(){
11001 reject(new Error('promise 2'));
11002 }, 100);
11003 });
11004
11005 Promise.race([promise1, promise2]).then(function(result){
11006 // Code here never runs
11007 }, function(reason){
11008 // reason.message === 'promise 2' because promise 2 became rejected before
11009 // promise 1 became fulfilled
11010 });
11011 ```
11012
11013 An example real-world use case is implementing timeouts:
11014
11015 ```javascript
11016 Promise.race([ajax('foo.json'), timeout(5000)])
11017 ```
11018
11019 @method race
11020 @static
11021 @param {Array} promises array of promises to observe
11022 Useful for tooling.
11023 @return {Promise} a promise which settles in the same way as the first passed
11024 promise to settle.
11025*/
11026function race(entries) {
11027 /*jshint validthis:true */
11028 var Constructor = this;
11029
11030 if (!isArray(entries)) {
11031 return new Constructor(function (_, reject) {
11032 return reject(new TypeError('You must pass an array to race.'));
11033 });
11034 } else {
11035 return new Constructor(function (resolve, reject) {
11036 var length = entries.length;
11037 for (var i = 0; i < length; i++) {
11038 Constructor.resolve(entries[i]).then(resolve, reject);
11039 }
11040 });
11041 }
11042}
11043
11044/**
11045 `Promise.reject` returns a promise rejected with the passed `reason`.
11046 It is shorthand for the following:
11047
11048 ```javascript
11049 let promise = new Promise(function(resolve, reject){
11050 reject(new Error('WHOOPS'));
11051 });
11052
11053 promise.then(function(value){
11054 // Code here doesn't run because the promise is rejected!
11055 }, function(reason){
11056 // reason.message === 'WHOOPS'
11057 });
11058 ```
11059
11060 Instead of writing the above, your code now simply becomes the following:
11061
11062 ```javascript
11063 let promise = Promise.reject(new Error('WHOOPS'));
11064
11065 promise.then(function(value){
11066 // Code here doesn't run because the promise is rejected!
11067 }, function(reason){
11068 // reason.message === 'WHOOPS'
11069 });
11070 ```
11071
11072 @method reject
11073 @static
11074 @param {Any} reason value that the returned promise will be rejected with.
11075 Useful for tooling.
11076 @return {Promise} a promise rejected with the given `reason`.
11077*/
11078function reject$1(reason) {
11079 /*jshint validthis:true */
11080 var Constructor = this;
11081 var promise = new Constructor(noop);
11082 reject(promise, reason);
11083 return promise;
11084}
11085
11086function needsResolver() {
11087 throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
11088}
11089
11090function needsNew() {
11091 throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
11092}
11093
11094/**
11095 Promise objects represent the eventual result of an asynchronous operation. The
11096 primary way of interacting with a promise is through its `then` method, which
11097 registers callbacks to receive either a promise's eventual value or the reason
11098 why the promise cannot be fulfilled.
11099
11100 Terminology
11101 -----------
11102
11103 - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
11104 - `thenable` is an object or function that defines a `then` method.
11105 - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
11106 - `exception` is a value that is thrown using the throw statement.
11107 - `reason` is a value that indicates why a promise was rejected.
11108 - `settled` the final resting state of a promise, fulfilled or rejected.
11109
11110 A promise can be in one of three states: pending, fulfilled, or rejected.
11111
11112 Promises that are fulfilled have a fulfillment value and are in the fulfilled
11113 state. Promises that are rejected have a rejection reason and are in the
11114 rejected state. A fulfillment value is never a thenable.
11115
11116 Promises can also be said to *resolve* a value. If this value is also a
11117 promise, then the original promise's settled state will match the value's
11118 settled state. So a promise that *resolves* a promise that rejects will
11119 itself reject, and a promise that *resolves* a promise that fulfills will
11120 itself fulfill.
11121
11122
11123 Basic Usage:
11124 ------------
11125
11126 ```js
11127 let promise = new Promise(function(resolve, reject) {
11128 // on success
11129 resolve(value);
11130
11131 // on failure
11132 reject(reason);
11133 });
11134
11135 promise.then(function(value) {
11136 // on fulfillment
11137 }, function(reason) {
11138 // on rejection
11139 });
11140 ```
11141
11142 Advanced Usage:
11143 ---------------
11144
11145 Promises shine when abstracting away asynchronous interactions such as
11146 `XMLHttpRequest`s.
11147
11148 ```js
11149 function getJSON(url) {
11150 return new Promise(function(resolve, reject){
11151 let xhr = new XMLHttpRequest();
11152
11153 xhr.open('GET', url);
11154 xhr.onreadystatechange = handler;
11155 xhr.responseType = 'json';
11156 xhr.setRequestHeader('Accept', 'application/json');
11157 xhr.send();
11158
11159 function handler() {
11160 if (this.readyState === this.DONE) {
11161 if (this.status === 200) {
11162 resolve(this.response);
11163 } else {
11164 reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
11165 }
11166 }
11167 };
11168 });
11169 }
11170
11171 getJSON('/posts.json').then(function(json) {
11172 // on fulfillment
11173 }, function(reason) {
11174 // on rejection
11175 });
11176 ```
11177
11178 Unlike callbacks, promises are great composable primitives.
11179
11180 ```js
11181 Promise.all([
11182 getJSON('/posts'),
11183 getJSON('/comments')
11184 ]).then(function(values){
11185 values[0] // => postsJSON
11186 values[1] // => commentsJSON
11187
11188 return values;
11189 });
11190 ```
11191
11192 @class Promise
11193 @param {Function} resolver
11194 Useful for tooling.
11195 @constructor
11196*/
11197
11198var Promise$1 = function () {
11199 function Promise(resolver) {
11200 this[PROMISE_ID] = nextId();
11201 this._result = this._state = undefined;
11202 this._subscribers = [];
11203
11204 if (noop !== resolver) {
11205 typeof resolver !== 'function' && needsResolver();
11206 this instanceof Promise ? initializePromise(this, resolver) : needsNew();
11207 }
11208 }
11209
11210 /**
11211 The primary way of interacting with a promise is through its `then` method,
11212 which registers callbacks to receive either a promise's eventual value or the
11213 reason why the promise cannot be fulfilled.
11214 ```js
11215 findUser().then(function(user){
11216 // user is available
11217 }, function(reason){
11218 // user is unavailable, and you are given the reason why
11219 });
11220 ```
11221 Chaining
11222 --------
11223 The return value of `then` is itself a promise. This second, 'downstream'
11224 promise is resolved with the return value of the first promise's fulfillment
11225 or rejection handler, or rejected if the handler throws an exception.
11226 ```js
11227 findUser().then(function (user) {
11228 return user.name;
11229 }, function (reason) {
11230 return 'default name';
11231 }).then(function (userName) {
11232 // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
11233 // will be `'default name'`
11234 });
11235 findUser().then(function (user) {
11236 throw new Error('Found user, but still unhappy');
11237 }, function (reason) {
11238 throw new Error('`findUser` rejected and we're unhappy');
11239 }).then(function (value) {
11240 // never reached
11241 }, function (reason) {
11242 // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
11243 // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
11244 });
11245 ```
11246 If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
11247 ```js
11248 findUser().then(function (user) {
11249 throw new PedagogicalException('Upstream error');
11250 }).then(function (value) {
11251 // never reached
11252 }).then(function (value) {
11253 // never reached
11254 }, function (reason) {
11255 // The `PedgagocialException` is propagated all the way down to here
11256 });
11257 ```
11258 Assimilation
11259 ------------
11260 Sometimes the value you want to propagate to a downstream promise can only be
11261 retrieved asynchronously. This can be achieved by returning a promise in the
11262 fulfillment or rejection handler. The downstream promise will then be pending
11263 until the returned promise is settled. This is called *assimilation*.
11264 ```js
11265 findUser().then(function (user) {
11266 return findCommentsByAuthor(user);
11267 }).then(function (comments) {
11268 // The user's comments are now available
11269 });
11270 ```
11271 If the assimliated promise rejects, then the downstream promise will also reject.
11272 ```js
11273 findUser().then(function (user) {
11274 return findCommentsByAuthor(user);
11275 }).then(function (comments) {
11276 // If `findCommentsByAuthor` fulfills, we'll have the value here
11277 }, function (reason) {
11278 // If `findCommentsByAuthor` rejects, we'll have the reason here
11279 });
11280 ```
11281 Simple Example
11282 --------------
11283 Synchronous Example
11284 ```javascript
11285 let result;
11286 try {
11287 result = findResult();
11288 // success
11289 } catch(reason) {
11290 // failure
11291 }
11292 ```
11293 Errback Example
11294 ```js
11295 findResult(function(result, err){
11296 if (err) {
11297 // failure
11298 } else {
11299 // success
11300 }
11301 });
11302 ```
11303 Promise Example;
11304 ```javascript
11305 findResult().then(function(result){
11306 // success
11307 }, function(reason){
11308 // failure
11309 });
11310 ```
11311 Advanced Example
11312 --------------
11313 Synchronous Example
11314 ```javascript
11315 let author, books;
11316 try {
11317 author = findAuthor();
11318 books = findBooksByAuthor(author);
11319 // success
11320 } catch(reason) {
11321 // failure
11322 }
11323 ```
11324 Errback Example
11325 ```js
11326 function foundBooks(books) {
11327 }
11328 function failure(reason) {
11329 }
11330 findAuthor(function(author, err){
11331 if (err) {
11332 failure(err);
11333 // failure
11334 } else {
11335 try {
11336 findBoooksByAuthor(author, function(books, err) {
11337 if (err) {
11338 failure(err);
11339 } else {
11340 try {
11341 foundBooks(books);
11342 } catch(reason) {
11343 failure(reason);
11344 }
11345 }
11346 });
11347 } catch(error) {
11348 failure(err);
11349 }
11350 // success
11351 }
11352 });
11353 ```
11354 Promise Example;
11355 ```javascript
11356 findAuthor().
11357 then(findBooksByAuthor).
11358 then(function(books){
11359 // found books
11360 }).catch(function(reason){
11361 // something went wrong
11362 });
11363 ```
11364 @method then
11365 @param {Function} onFulfilled
11366 @param {Function} onRejected
11367 Useful for tooling.
11368 @return {Promise}
11369 */
11370
11371 /**
11372 `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
11373 as the catch block of a try/catch statement.
11374 ```js
11375 function findAuthor(){
11376 throw new Error('couldn't find that author');
11377 }
11378 // synchronous
11379 try {
11380 findAuthor();
11381 } catch(reason) {
11382 // something went wrong
11383 }
11384 // async with promises
11385 findAuthor().catch(function(reason){
11386 // something went wrong
11387 });
11388 ```
11389 @method catch
11390 @param {Function} onRejection
11391 Useful for tooling.
11392 @return {Promise}
11393 */
11394
11395
11396 Promise.prototype.catch = function _catch(onRejection) {
11397 return this.then(null, onRejection);
11398 };
11399
11400 /**
11401 `finally` will be invoked regardless of the promise's fate just as native
11402 try/catch/finally behaves
11403
11404 Synchronous example:
11405
11406 ```js
11407 findAuthor() {
11408 if (Math.random() > 0.5) {
11409 throw new Error();
11410 }
11411 return new Author();
11412 }
11413
11414 try {
11415 return findAuthor(); // succeed or fail
11416 } catch(error) {
11417 return findOtherAuther();
11418 } finally {
11419 // always runs
11420 // doesn't affect the return value
11421 }
11422 ```
11423
11424 Asynchronous example:
11425
11426 ```js
11427 findAuthor().catch(function(reason){
11428 return findOtherAuther();
11429 }).finally(function(){
11430 // author was either found, or not
11431 });
11432 ```
11433
11434 @method finally
11435 @param {Function} callback
11436 @return {Promise}
11437 */
11438
11439
11440 Promise.prototype.finally = function _finally(callback) {
11441 var promise = this;
11442 var constructor = promise.constructor;
11443
11444 if (isFunction(callback)) {
11445 return promise.then(function (value) {
11446 return constructor.resolve(callback()).then(function () {
11447 return value;
11448 });
11449 }, function (reason) {
11450 return constructor.resolve(callback()).then(function () {
11451 throw reason;
11452 });
11453 });
11454 }
11455
11456 return promise.then(callback, callback);
11457 };
11458
11459 return Promise;
11460}();
11461
11462Promise$1.prototype.then = then;
11463Promise$1.all = all;
11464Promise$1.race = race;
11465Promise$1.resolve = resolve$1;
11466Promise$1.reject = reject$1;
11467Promise$1._setScheduler = setScheduler;
11468Promise$1._setAsap = setAsap;
11469Promise$1._asap = asap;
11470
11471/*global self*/
11472function polyfill() {
11473 var local = void 0;
11474
11475 if (typeof global !== 'undefined') {
11476 local = global;
11477 } else if (typeof self !== 'undefined') {
11478 local = self;
11479 } else {
11480 try {
11481 local = Function('return this')();
11482 } catch (e) {
11483 throw new Error('polyfill failed because global object is unavailable in this environment');
11484 }
11485 }
11486
11487 var P = local.Promise;
11488
11489 if (P) {
11490 var promiseToString = null;
11491 try {
11492 promiseToString = Object.prototype.toString.call(P.resolve());
11493 } catch (e) {
11494 // silently ignored
11495 }
11496
11497 if (promiseToString === '[object Promise]' && !P.cast) {
11498 return;
11499 }
11500 }
11501
11502 local.Promise = Promise$1;
11503}
11504
11505// Strange compat..
11506Promise$1.polyfill = polyfill;
11507Promise$1.Promise = Promise$1;
11508
11509return Promise$1;
11510
11511})));
11512
11513
11514
11515
11516
11517}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
11518},{"_process":34}],18:[function(_dereq_,module,exports){
11519/**
11520 * inspired by is-number <https://github.com/jonschlinkert/is-number>
11521 * but significantly simplified and sped up by ignoring number and string constructors
11522 * ie these return false:
11523 * new Number(1)
11524 * new String('1')
11525 */
11526
11527'use strict';
11528
11529var allBlankCharCodes = _dereq_('is-string-blank');
11530
11531module.exports = function(n) {
11532 var type = typeof n;
11533 if(type === 'string') {
11534 var original = n;
11535 n = +n;
11536 // whitespace strings cast to zero - filter them out
11537 if(n===0 && allBlankCharCodes(original)) return false;
11538 }
11539 else if(type !== 'number') return false;
11540
11541 return n - n < 1;
11542};
11543
11544},{"is-string-blank":23}],19:[function(_dereq_,module,exports){
11545module.exports = fromQuat;
11546
11547/**
11548 * Creates a matrix from a quaternion rotation.
11549 *
11550 * @param {mat4} out mat4 receiving operation result
11551 * @param {quat4} q Rotation quaternion
11552 * @returns {mat4} out
11553 */
11554function fromQuat(out, q) {
11555 var x = q[0], y = q[1], z = q[2], w = q[3],
11556 x2 = x + x,
11557 y2 = y + y,
11558 z2 = z + z,
11559
11560 xx = x * x2,
11561 yx = y * x2,
11562 yy = y * y2,
11563 zx = z * x2,
11564 zy = z * y2,
11565 zz = z * z2,
11566 wx = w * x2,
11567 wy = w * y2,
11568 wz = w * z2;
11569
11570 out[0] = 1 - yy - zz;
11571 out[1] = yx + wz;
11572 out[2] = zx - wy;
11573 out[3] = 0;
11574
11575 out[4] = yx - wz;
11576 out[5] = 1 - xx - zz;
11577 out[6] = zy + wx;
11578 out[7] = 0;
11579
11580 out[8] = zx + wy;
11581 out[9] = zy - wx;
11582 out[10] = 1 - xx - yy;
11583 out[11] = 0;
11584
11585 out[12] = 0;
11586 out[13] = 0;
11587 out[14] = 0;
11588 out[15] = 1;
11589
11590 return out;
11591};
11592},{}],20:[function(_dereq_,module,exports){
11593(function (global){
11594'use strict'
11595
11596var isBrowser = _dereq_('is-browser')
11597var hasHover
11598
11599if (typeof global.matchMedia === 'function') {
11600 hasHover = !global.matchMedia('(hover: none)').matches
11601}
11602else {
11603 hasHover = isBrowser
11604}
11605
11606module.exports = hasHover
11607
11608}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
11609},{"is-browser":22}],21:[function(_dereq_,module,exports){
11610'use strict'
11611
11612var isBrowser = _dereq_('is-browser')
11613
11614function detect() {
11615 var supported = false
11616
11617 try {
11618 var opts = Object.defineProperty({}, 'passive', {
11619 get: function() {
11620 supported = true
11621 }
11622 })
11623
11624 window.addEventListener('test', null, opts)
11625 window.removeEventListener('test', null, opts)
11626 } catch(e) {
11627 supported = false
11628 }
11629
11630 return supported
11631}
11632
11633module.exports = isBrowser && detect()
11634
11635},{"is-browser":22}],22:[function(_dereq_,module,exports){
11636module.exports = true;
11637},{}],23:[function(_dereq_,module,exports){
11638'use strict';
11639
11640/**
11641 * Is this string all whitespace?
11642 * This solution kind of makes my brain hurt, but it's significantly faster
11643 * than !str.trim() or any other solution I could find.
11644 *
11645 * whitespace codes from: http://en.wikipedia.org/wiki/Whitespace_character
11646 * and verified with:
11647 *
11648 * for(var i = 0; i < 65536; i++) {
11649 * var s = String.fromCharCode(i);
11650 * if(+s===0 && !s.trim()) console.log(i, s);
11651 * }
11652 *
11653 * which counts a couple of these as *not* whitespace, but finds nothing else
11654 * that *is* whitespace. Note that charCodeAt stops at 16 bits, but it appears
11655 * that there are no whitespace characters above this, and code points above
11656 * this do not map onto white space characters.
11657 */
11658
11659module.exports = function(str){
11660 var l = str.length,
11661 a;
11662 for(var i = 0; i < l; i++) {
11663 a = str.charCodeAt(i);
11664 if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) &&
11665 (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) &&
11666 (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) &&
11667 (a !== 8288) && (a !== 12288) && (a !== 65279)) {
11668 return false;
11669 }
11670 }
11671 return true;
11672}
11673
11674},{}],24:[function(_dereq_,module,exports){
11675var rootPosition = { left: 0, top: 0 }
11676
11677module.exports = mouseEventOffset
11678function mouseEventOffset (ev, target, out) {
11679 target = target || ev.currentTarget || ev.srcElement
11680 if (!Array.isArray(out)) {
11681 out = [ 0, 0 ]
11682 }
11683 var cx = ev.clientX || 0
11684 var cy = ev.clientY || 0
11685 var rect = getBoundingClientOffset(target)
11686 out[0] = cx - rect.left
11687 out[1] = cy - rect.top
11688 return out
11689}
11690
11691function getBoundingClientOffset (element) {
11692 if (element === window ||
11693 element === document ||
11694 element === document.body) {
11695 return rootPosition
11696 } else {
11697 return element.getBoundingClientRect()
11698 }
11699}
11700
11701},{}],25:[function(_dereq_,module,exports){
11702
11703module.exports = parse
11704
11705/**
11706 * expected argument lengths
11707 * @type {Object}
11708 */
11709
11710var length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}
11711
11712/**
11713 * segment pattern
11714 * @type {RegExp}
11715 */
11716
11717var segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig
11718
11719/**
11720 * parse an svg path data string. Generates an Array
11721 * of commands where each command is an Array of the
11722 * form `[command, arg1, arg2, ...]`
11723 *
11724 * @param {String} path
11725 * @return {Array}
11726 */
11727
11728function parse(path) {
11729 var data = []
11730 path.replace(segment, function(_, command, args){
11731 var type = command.toLowerCase()
11732 args = parseValues(args)
11733
11734 // overloaded moveTo
11735 if (type == 'm' && args.length > 2) {
11736 data.push([command].concat(args.splice(0, 2)))
11737 type = 'l'
11738 command = command == 'm' ? 'l' : 'L'
11739 }
11740
11741 while (true) {
11742 if (args.length == length[type]) {
11743 args.unshift(command)
11744 return data.push(args)
11745 }
11746 if (args.length < length[type]) throw new Error('malformed path data')
11747 data.push([command].concat(args.splice(0, length[type])))
11748 }
11749 })
11750 return data
11751}
11752
11753var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig
11754
11755function parseValues(args) {
11756 var numbers = args.match(number)
11757 return numbers ? numbers.map(Number) : []
11758}
11759
11760},{}],26:[function(_dereq_,module,exports){
11761/*
11762 * @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc
11763 * @license MIT
11764 * @preserve Project Home: https://github.com/voidqk/polybooljs
11765 */
11766
11767var BuildLog = _dereq_('./lib/build-log');
11768var Epsilon = _dereq_('./lib/epsilon');
11769var Intersecter = _dereq_('./lib/intersecter');
11770var SegmentChainer = _dereq_('./lib/segment-chainer');
11771var SegmentSelector = _dereq_('./lib/segment-selector');
11772var GeoJSON = _dereq_('./lib/geojson');
11773
11774var buildLog = false;
11775var epsilon = Epsilon();
11776
11777var PolyBool;
11778PolyBool = {
11779 // getter/setter for buildLog
11780 buildLog: function(bl){
11781 if (bl === true)
11782 buildLog = BuildLog();
11783 else if (bl === false)
11784 buildLog = false;
11785 return buildLog === false ? false : buildLog.list;
11786 },
11787 // getter/setter for epsilon
11788 epsilon: function(v){
11789 return epsilon.epsilon(v);
11790 },
11791
11792 // core API
11793 segments: function(poly){
11794 var i = Intersecter(true, epsilon, buildLog);
11795 poly.regions.forEach(i.addRegion);
11796 return {
11797 segments: i.calculate(poly.inverted),
11798 inverted: poly.inverted
11799 };
11800 },
11801 combine: function(segments1, segments2){
11802 var i3 = Intersecter(false, epsilon, buildLog);
11803 return {
11804 combined: i3.calculate(
11805 segments1.segments, segments1.inverted,
11806 segments2.segments, segments2.inverted
11807 ),
11808 inverted1: segments1.inverted,
11809 inverted2: segments2.inverted
11810 };
11811 },
11812 selectUnion: function(combined){
11813 return {
11814 segments: SegmentSelector.union(combined.combined, buildLog),
11815 inverted: combined.inverted1 || combined.inverted2
11816 }
11817 },
11818 selectIntersect: function(combined){
11819 return {
11820 segments: SegmentSelector.intersect(combined.combined, buildLog),
11821 inverted: combined.inverted1 && combined.inverted2
11822 }
11823 },
11824 selectDifference: function(combined){
11825 return {
11826 segments: SegmentSelector.difference(combined.combined, buildLog),
11827 inverted: combined.inverted1 && !combined.inverted2
11828 }
11829 },
11830 selectDifferenceRev: function(combined){
11831 return {
11832 segments: SegmentSelector.differenceRev(combined.combined, buildLog),
11833 inverted: !combined.inverted1 && combined.inverted2
11834 }
11835 },
11836 selectXor: function(combined){
11837 return {
11838 segments: SegmentSelector.xor(combined.combined, buildLog),
11839 inverted: combined.inverted1 !== combined.inverted2
11840 }
11841 },
11842 polygon: function(segments){
11843 return {
11844 regions: SegmentChainer(segments.segments, epsilon, buildLog),
11845 inverted: segments.inverted
11846 };
11847 },
11848
11849 // GeoJSON converters
11850 polygonFromGeoJSON: function(geojson){
11851 return GeoJSON.toPolygon(PolyBool, geojson);
11852 },
11853 polygonToGeoJSON: function(poly){
11854 return GeoJSON.fromPolygon(PolyBool, epsilon, poly);
11855 },
11856
11857 // helper functions for common operations
11858 union: function(poly1, poly2){
11859 return operate(poly1, poly2, PolyBool.selectUnion);
11860 },
11861 intersect: function(poly1, poly2){
11862 return operate(poly1, poly2, PolyBool.selectIntersect);
11863 },
11864 difference: function(poly1, poly2){
11865 return operate(poly1, poly2, PolyBool.selectDifference);
11866 },
11867 differenceRev: function(poly1, poly2){
11868 return operate(poly1, poly2, PolyBool.selectDifferenceRev);
11869 },
11870 xor: function(poly1, poly2){
11871 return operate(poly1, poly2, PolyBool.selectXor);
11872 }
11873};
11874
11875function operate(poly1, poly2, selector){
11876 var seg1 = PolyBool.segments(poly1);
11877 var seg2 = PolyBool.segments(poly2);
11878 var comb = PolyBool.combine(seg1, seg2);
11879 var seg3 = selector(comb);
11880 return PolyBool.polygon(seg3);
11881}
11882
11883if (typeof window === 'object')
11884 window.PolyBool = PolyBool;
11885
11886module.exports = PolyBool;
11887
11888},{"./lib/build-log":27,"./lib/epsilon":28,"./lib/geojson":29,"./lib/intersecter":30,"./lib/segment-chainer":32,"./lib/segment-selector":33}],27:[function(_dereq_,module,exports){
11889// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
11890// MIT License
11891// Project Home: https://github.com/voidqk/polybooljs
11892
11893//
11894// used strictly for logging the processing of the algorithm... only useful if you intend on
11895// looking under the covers (for pretty UI's or debugging)
11896//
11897
11898function BuildLog(){
11899 var my;
11900 var nextSegmentId = 0;
11901 var curVert = false;
11902
11903 function push(type, data){
11904 my.list.push({
11905 type: type,
11906 data: data ? JSON.parse(JSON.stringify(data)) : void 0
11907 });
11908 return my;
11909 }
11910
11911 my = {
11912 list: [],
11913 segmentId: function(){
11914 return nextSegmentId++;
11915 },
11916 checkIntersection: function(seg1, seg2){
11917 return push('check', { seg1: seg1, seg2: seg2 });
11918 },
11919 segmentChop: function(seg, end){
11920 push('div_seg', { seg: seg, pt: end });
11921 return push('chop', { seg: seg, pt: end });
11922 },
11923 statusRemove: function(seg){
11924 return push('pop_seg', { seg: seg });
11925 },
11926 segmentUpdate: function(seg){
11927 return push('seg_update', { seg: seg });
11928 },
11929 segmentNew: function(seg, primary){
11930 return push('new_seg', { seg: seg, primary: primary });
11931 },
11932 segmentRemove: function(seg){
11933 return push('rem_seg', { seg: seg });
11934 },
11935 tempStatus: function(seg, above, below){
11936 return push('temp_status', { seg: seg, above: above, below: below });
11937 },
11938 rewind: function(seg){
11939 return push('rewind', { seg: seg });
11940 },
11941 status: function(seg, above, below){
11942 return push('status', { seg: seg, above: above, below: below });
11943 },
11944 vert: function(x){
11945 if (x === curVert)
11946 return my;
11947 curVert = x;
11948 return push('vert', { x: x });
11949 },
11950 log: function(data){
11951 if (typeof data !== 'string')
11952 data = JSON.stringify(data, false, ' ');
11953 return push('log', { txt: data });
11954 },
11955 reset: function(){
11956 return push('reset');
11957 },
11958 selected: function(segs){
11959 return push('selected', { segs: segs });
11960 },
11961 chainStart: function(seg){
11962 return push('chain_start', { seg: seg });
11963 },
11964 chainRemoveHead: function(index, pt){
11965 return push('chain_rem_head', { index: index, pt: pt });
11966 },
11967 chainRemoveTail: function(index, pt){
11968 return push('chain_rem_tail', { index: index, pt: pt });
11969 },
11970 chainNew: function(pt1, pt2){
11971 return push('chain_new', { pt1: pt1, pt2: pt2 });
11972 },
11973 chainMatch: function(index){
11974 return push('chain_match', { index: index });
11975 },
11976 chainClose: function(index){
11977 return push('chain_close', { index: index });
11978 },
11979 chainAddHead: function(index, pt){
11980 return push('chain_add_head', { index: index, pt: pt });
11981 },
11982 chainAddTail: function(index, pt){
11983 return push('chain_add_tail', { index: index, pt: pt, });
11984 },
11985 chainConnect: function(index1, index2){
11986 return push('chain_con', { index1: index1, index2: index2 });
11987 },
11988 chainReverse: function(index){
11989 return push('chain_rev', { index: index });
11990 },
11991 chainJoin: function(index1, index2){
11992 return push('chain_join', { index1: index1, index2: index2 });
11993 },
11994 done: function(){
11995 return push('done');
11996 }
11997 };
11998 return my;
11999}
12000
12001module.exports = BuildLog;
12002
12003},{}],28:[function(_dereq_,module,exports){
12004// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
12005// MIT License
12006// Project Home: https://github.com/voidqk/polybooljs
12007
12008//
12009// provides the raw computation functions that takes epsilon into account
12010//
12011// zero is defined to be between (-epsilon, epsilon) exclusive
12012//
12013
12014function Epsilon(eps){
12015 if (typeof eps !== 'number')
12016 eps = 0.0000000001; // sane default? sure why not
12017 var my = {
12018 epsilon: function(v){
12019 if (typeof v === 'number')
12020 eps = v;
12021 return eps;
12022 },
12023 pointAboveOrOnLine: function(pt, left, right){
12024 var Ax = left[0];
12025 var Ay = left[1];
12026 var Bx = right[0];
12027 var By = right[1];
12028 var Cx = pt[0];
12029 var Cy = pt[1];
12030 return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax) >= -eps;
12031 },
12032 pointBetween: function(p, left, right){
12033 // p must be collinear with left->right
12034 // returns false if p == left, p == right, or left == right
12035 var d_py_ly = p[1] - left[1];
12036 var d_rx_lx = right[0] - left[0];
12037 var d_px_lx = p[0] - left[0];
12038 var d_ry_ly = right[1] - left[1];
12039
12040 var dot = d_px_lx * d_rx_lx + d_py_ly * d_ry_ly;
12041 // if `dot` is 0, then `p` == `left` or `left` == `right` (reject)
12042 // if `dot` is less than 0, then `p` is to the left of `left` (reject)
12043 if (dot < eps)
12044 return false;
12045
12046 var sqlen = d_rx_lx * d_rx_lx + d_ry_ly * d_ry_ly;
12047 // if `dot` > `sqlen`, then `p` is to the right of `right` (reject)
12048 // therefore, if `dot - sqlen` is greater than 0, then `p` is to the right of `right` (reject)
12049 if (dot - sqlen > -eps)
12050 return false;
12051
12052 return true;
12053 },
12054 pointsSameX: function(p1, p2){
12055 return Math.abs(p1[0] - p2[0]) < eps;
12056 },
12057 pointsSameY: function(p1, p2){
12058 return Math.abs(p1[1] - p2[1]) < eps;
12059 },
12060 pointsSame: function(p1, p2){
12061 return my.pointsSameX(p1, p2) && my.pointsSameY(p1, p2);
12062 },
12063 pointsCompare: function(p1, p2){
12064 // returns -1 if p1 is smaller, 1 if p2 is smaller, 0 if equal
12065 if (my.pointsSameX(p1, p2))
12066 return my.pointsSameY(p1, p2) ? 0 : (p1[1] < p2[1] ? -1 : 1);
12067 return p1[0] < p2[0] ? -1 : 1;
12068 },
12069 pointsCollinear: function(pt1, pt2, pt3){
12070 // does pt1->pt2->pt3 make a straight line?
12071 // essentially this is just checking to see if the slope(pt1->pt2) === slope(pt2->pt3)
12072 // if slopes are equal, then they must be collinear, because they share pt2
12073 var dx1 = pt1[0] - pt2[0];
12074 var dy1 = pt1[1] - pt2[1];
12075 var dx2 = pt2[0] - pt3[0];
12076 var dy2 = pt2[1] - pt3[1];
12077 return Math.abs(dx1 * dy2 - dx2 * dy1) < eps;
12078 },
12079 linesIntersect: function(a0, a1, b0, b1){
12080 // returns false if the lines are coincident (e.g., parallel or on top of each other)
12081 //
12082 // returns an object if the lines intersect:
12083 // {
12084 // pt: [x, y], where the intersection point is at
12085 // alongA: where intersection point is along A,
12086 // alongB: where intersection point is along B
12087 // }
12088 //
12089 // alongA and alongB will each be one of: -2, -1, 0, 1, 2
12090 //
12091 // with the following meaning:
12092 //
12093 // -2 intersection point is before segment's first point
12094 // -1 intersection point is directly on segment's first point
12095 // 0 intersection point is between segment's first and second points (exclusive)
12096 // 1 intersection point is directly on segment's second point
12097 // 2 intersection point is after segment's second point
12098 var adx = a1[0] - a0[0];
12099 var ady = a1[1] - a0[1];
12100 var bdx = b1[0] - b0[0];
12101 var bdy = b1[1] - b0[1];
12102
12103 var axb = adx * bdy - ady * bdx;
12104 if (Math.abs(axb) < eps)
12105 return false; // lines are coincident
12106
12107 var dx = a0[0] - b0[0];
12108 var dy = a0[1] - b0[1];
12109
12110 var A = (bdx * dy - bdy * dx) / axb;
12111 var B = (adx * dy - ady * dx) / axb;
12112
12113 var ret = {
12114 alongA: 0,
12115 alongB: 0,
12116 pt: [
12117 a0[0] + A * adx,
12118 a0[1] + A * ady
12119 ]
12120 };
12121
12122 // categorize where intersection point is along A and B
12123
12124 if (A <= -eps)
12125 ret.alongA = -2;
12126 else if (A < eps)
12127 ret.alongA = -1;
12128 else if (A - 1 <= -eps)
12129 ret.alongA = 0;
12130 else if (A - 1 < eps)
12131 ret.alongA = 1;
12132 else
12133 ret.alongA = 2;
12134
12135 if (B <= -eps)
12136 ret.alongB = -2;
12137 else if (B < eps)
12138 ret.alongB = -1;
12139 else if (B - 1 <= -eps)
12140 ret.alongB = 0;
12141 else if (B - 1 < eps)
12142 ret.alongB = 1;
12143 else
12144 ret.alongB = 2;
12145
12146 return ret;
12147 },
12148 pointInsideRegion: function(pt, region){
12149 var x = pt[0];
12150 var y = pt[1];
12151 var last_x = region[region.length - 1][0];
12152 var last_y = region[region.length - 1][1];
12153 var inside = false;
12154 for (var i = 0; i < region.length; i++){
12155 var curr_x = region[i][0];
12156 var curr_y = region[i][1];
12157
12158 // if y is between curr_y and last_y, and
12159 // x is to the right of the boundary created by the line
12160 if ((curr_y - y > eps) != (last_y - y > eps) &&
12161 (last_x - curr_x) * (y - curr_y) / (last_y - curr_y) + curr_x - x > eps)
12162 inside = !inside
12163
12164 last_x = curr_x;
12165 last_y = curr_y;
12166 }
12167 return inside;
12168 }
12169 };
12170 return my;
12171}
12172
12173module.exports = Epsilon;
12174
12175},{}],29:[function(_dereq_,module,exports){
12176// (c) Copyright 2017, Sean Connelly (@voidqk), http://syntheti.cc
12177// MIT License
12178// Project Home: https://github.com/voidqk/polybooljs
12179
12180//
12181// convert between PolyBool polygon format and GeoJSON formats (Polygon and MultiPolygon)
12182//
12183
12184var GeoJSON = {
12185 // convert a GeoJSON object to a PolyBool polygon
12186 toPolygon: function(PolyBool, geojson){
12187
12188 // converts list of LineString's to segments
12189 function GeoPoly(coords){
12190 // check for empty coords
12191 if (coords.length <= 0)
12192 return PolyBool.segments({ inverted: false, regions: [] });
12193
12194 // convert LineString to segments
12195 function LineString(ls){
12196 // remove tail which should be the same as head
12197 var reg = ls.slice(0, ls.length - 1);
12198 return PolyBool.segments({ inverted: false, regions: [reg] });
12199 }
12200
12201 // the first LineString is considered the outside
12202 var out = LineString(coords[0]);
12203
12204 // the rest of the LineStrings are considered interior holes, so subtract them from the
12205 // current result
12206 for (var i = 1; i < coords.length; i++)
12207 out = PolyBool.selectDifference(PolyBool.combine(out, LineString(coords[i])));
12208
12209 return out;
12210 }
12211
12212 if (geojson.type === 'Polygon'){
12213 // single polygon, so just convert it and we're done
12214 return PolyBool.polygon(GeoPoly(geojson.coordinates));
12215 }
12216 else if (geojson.type === 'MultiPolygon'){
12217 // multiple polygons, so union all the polygons together
12218 var out = PolyBool.segments({ inverted: false, regions: [] });
12219 for (var i = 0; i < geojson.coordinates.length; i++)
12220 out = PolyBool.selectUnion(PolyBool.combine(out, GeoPoly(geojson.coordinates[i])));
12221 return PolyBool.polygon(out);
12222 }
12223 throw new Error('PolyBool: Cannot convert GeoJSON object to PolyBool polygon');
12224 },
12225
12226 // convert a PolyBool polygon to a GeoJSON object
12227 fromPolygon: function(PolyBool, eps, poly){
12228 // make sure out polygon is clean
12229 poly = PolyBool.polygon(PolyBool.segments(poly));
12230
12231 // test if r1 is inside r2
12232 function regionInsideRegion(r1, r2){
12233 // we're guaranteed no lines intersect (because the polygon is clean), but a vertex
12234 // could be on the edge -- so we just average pt[0] and pt[1] to produce a point on the
12235 // edge of the first line, which cannot be on an edge
12236 return eps.pointInsideRegion([
12237 (r1[0][0] + r1[1][0]) * 0.5,
12238 (r1[0][1] + r1[1][1]) * 0.5
12239 ], r2);
12240 }
12241
12242 // calculate inside heirarchy
12243 //
12244 // _____________________ _______ roots -> A -> F
12245 // | A | | F | | |
12246 // | _______ _______ | | ___ | +-- B +-- G
12247 // | | B | | C | | | | | | | |
12248 // | | ___ | | ___ | | | | | | | +-- D
12249 // | | | D | | | | E | | | | | G | | |
12250 // | | |___| | | |___| | | | | | | +-- C
12251 // | |_______| |_______| | | |___| | |
12252 // |_____________________| |_______| +-- E
12253
12254 function newNode(region){
12255 return {
12256 region: region,
12257 children: []
12258 };
12259 }
12260
12261 var roots = newNode(null);
12262
12263 function addChild(root, region){
12264 // first check if we're inside any children
12265 for (var i = 0; i < root.children.length; i++){
12266 var child = root.children[i];
12267 if (regionInsideRegion(region, child.region)){
12268 // we are, so insert inside them instead
12269 addChild(child, region);
12270 return;
12271 }
12272 }
12273
12274 // not inside any children, so check to see if any children are inside us
12275 var node = newNode(region);
12276 for (var i = 0; i < root.children.length; i++){
12277 var child = root.children[i];
12278 if (regionInsideRegion(child.region, region)){
12279 // oops... move the child beneath us, and remove them from root
12280 node.children.push(child);
12281 root.children.splice(i, 1);
12282 i--;
12283 }
12284 }
12285
12286 // now we can add ourselves
12287 root.children.push(node);
12288 }
12289
12290 // add all regions to the root
12291 for (var i = 0; i < poly.regions.length; i++){
12292 var region = poly.regions[i];
12293 if (region.length < 3) // regions must have at least 3 points (sanity check)
12294 continue;
12295 addChild(roots, region);
12296 }
12297
12298 // with our heirarchy, we can distinguish between exterior borders, and interior holes
12299 // the root nodes are exterior, children are interior, children's children are exterior,
12300 // children's children's children are interior, etc
12301
12302 // while we're at it, exteriors are counter-clockwise, and interiors are clockwise
12303
12304 function forceWinding(region, clockwise){
12305 // first, see if we're clockwise or counter-clockwise
12306 // https://en.wikipedia.org/wiki/Shoelace_formula
12307 var winding = 0;
12308 var last_x = region[region.length - 1][0];
12309 var last_y = region[region.length - 1][1];
12310 var copy = [];
12311 for (var i = 0; i < region.length; i++){
12312 var curr_x = region[i][0];
12313 var curr_y = region[i][1];
12314 copy.push([curr_x, curr_y]); // create a copy while we're at it
12315 winding += curr_y * last_x - curr_x * last_y;
12316 last_x = curr_x;
12317 last_y = curr_y;
12318 }
12319 // this assumes Cartesian coordinates (Y is positive going up)
12320 var isclockwise = winding < 0;
12321 if (isclockwise !== clockwise)
12322 copy.reverse();
12323 // while we're here, the last point must be the first point...
12324 copy.push([copy[0][0], copy[0][1]]);
12325 return copy;
12326 }
12327
12328 var geopolys = [];
12329
12330 function addExterior(node){
12331 var poly = [forceWinding(node.region, false)];
12332 geopolys.push(poly);
12333 // children of exteriors are interior
12334 for (var i = 0; i < node.children.length; i++)
12335 poly.push(getInterior(node.children[i]));
12336 }
12337
12338 function getInterior(node){
12339 // children of interiors are exterior
12340 for (var i = 0; i < node.children.length; i++)
12341 addExterior(node.children[i]);
12342 // return the clockwise interior
12343 return forceWinding(node.region, true);
12344 }
12345
12346 // root nodes are exterior
12347 for (var i = 0; i < roots.children.length; i++)
12348 addExterior(roots.children[i]);
12349
12350 // lastly, construct the approrpriate GeoJSON object
12351
12352 if (geopolys.length <= 0) // empty GeoJSON Polygon
12353 return { type: 'Polygon', coordinates: [] };
12354 if (geopolys.length == 1) // use a GeoJSON Polygon
12355 return { type: 'Polygon', coordinates: geopolys[0] };
12356 return { // otherwise, use a GeoJSON MultiPolygon
12357 type: 'MultiPolygon',
12358 coordinates: geopolys
12359 };
12360 }
12361};
12362
12363module.exports = GeoJSON;
12364
12365},{}],30:[function(_dereq_,module,exports){
12366// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
12367// MIT License
12368// Project Home: https://github.com/voidqk/polybooljs
12369
12370//
12371// this is the core work-horse
12372//
12373
12374var LinkedList = _dereq_('./linked-list');
12375
12376function Intersecter(selfIntersection, eps, buildLog){
12377 // selfIntersection is true/false depending on the phase of the overall algorithm
12378
12379 //
12380 // segment creation
12381 //
12382
12383 function segmentNew(start, end){
12384 return {
12385 id: buildLog ? buildLog.segmentId() : -1,
12386 start: start,
12387 end: end,
12388 myFill: {
12389 above: null, // is there fill above us?
12390 below: null // is there fill below us?
12391 },
12392 otherFill: null
12393 };
12394 }
12395
12396 function segmentCopy(start, end, seg){
12397 return {
12398 id: buildLog ? buildLog.segmentId() : -1,
12399 start: start,
12400 end: end,
12401 myFill: {
12402 above: seg.myFill.above,
12403 below: seg.myFill.below
12404 },
12405 otherFill: null
12406 };
12407 }
12408
12409 //
12410 // event logic
12411 //
12412
12413 var event_root = LinkedList.create();
12414
12415 function eventCompare(p1_isStart, p1_1, p1_2, p2_isStart, p2_1, p2_2){
12416 // compare the selected points first
12417 var comp = eps.pointsCompare(p1_1, p2_1);
12418 if (comp !== 0)
12419 return comp;
12420 // the selected points are the same
12421
12422 if (eps.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too...
12423 return 0; // then the segments are equal
12424
12425 if (p1_isStart !== p2_isStart) // if one is a start and the other isn't...
12426 return p1_isStart ? 1 : -1; // favor the one that isn't the start
12427
12428 // otherwise, we'll have to calculate which one is below the other manually
12429 return eps.pointAboveOrOnLine(p1_2,
12430 p2_isStart ? p2_1 : p2_2, // order matters
12431 p2_isStart ? p2_2 : p2_1
12432 ) ? 1 : -1;
12433 }
12434
12435 function eventAdd(ev, other_pt){
12436 event_root.insertBefore(ev, function(here){
12437 // should ev be inserted before here?
12438 var comp = eventCompare(
12439 ev .isStart, ev .pt, other_pt,
12440 here.isStart, here.pt, here.other.pt
12441 );
12442 return comp < 0;
12443 });
12444 }
12445
12446 function eventAddSegmentStart(seg, primary){
12447 var ev_start = LinkedList.node({
12448 isStart: true,
12449 pt: seg.start,
12450 seg: seg,
12451 primary: primary,
12452 other: null,
12453 status: null
12454 });
12455 eventAdd(ev_start, seg.end);
12456 return ev_start;
12457 }
12458
12459 function eventAddSegmentEnd(ev_start, seg, primary){
12460 var ev_end = LinkedList.node({
12461 isStart: false,
12462 pt: seg.end,
12463 seg: seg,
12464 primary: primary,
12465 other: ev_start,
12466 status: null
12467 });
12468 ev_start.other = ev_end;
12469 eventAdd(ev_end, ev_start.pt);
12470 }
12471
12472 function eventAddSegment(seg, primary){
12473 var ev_start = eventAddSegmentStart(seg, primary);
12474 eventAddSegmentEnd(ev_start, seg, primary);
12475 return ev_start;
12476 }
12477
12478 function eventUpdateEnd(ev, end){
12479 // slides an end backwards
12480 // (start)------------(end) to:
12481 // (start)---(end)
12482
12483 if (buildLog)
12484 buildLog.segmentChop(ev.seg, end);
12485
12486 ev.other.remove();
12487 ev.seg.end = end;
12488 ev.other.pt = end;
12489 eventAdd(ev.other, ev.pt);
12490 }
12491
12492 function eventDivide(ev, pt){
12493 var ns = segmentCopy(pt, ev.seg.end, ev.seg);
12494 eventUpdateEnd(ev, pt);
12495 return eventAddSegment(ns, ev.primary);
12496 }
12497
12498 function calculate(primaryPolyInverted, secondaryPolyInverted){
12499 // if selfIntersection is true then there is no secondary polygon, so that isn't used
12500
12501 //
12502 // status logic
12503 //
12504
12505 var status_root = LinkedList.create();
12506
12507 function statusCompare(ev1, ev2){
12508 var a1 = ev1.seg.start;
12509 var a2 = ev1.seg.end;
12510 var b1 = ev2.seg.start;
12511 var b2 = ev2.seg.end;
12512
12513 if (eps.pointsCollinear(a1, b1, b2)){
12514 if (eps.pointsCollinear(a2, b1, b2))
12515 return 1;//eventCompare(true, a1, a2, true, b1, b2);
12516 return eps.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1;
12517 }
12518 return eps.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1;
12519 }
12520
12521 function statusFindSurrounding(ev){
12522 return status_root.findTransition(function(here){
12523 var comp = statusCompare(ev, here.ev);
12524 return comp > 0;
12525 });
12526 }
12527
12528 function checkIntersection(ev1, ev2){
12529 // returns the segment equal to ev1, or false if nothing equal
12530
12531 var seg1 = ev1.seg;
12532 var seg2 = ev2.seg;
12533 var a1 = seg1.start;
12534 var a2 = seg1.end;
12535 var b1 = seg2.start;
12536 var b2 = seg2.end;
12537
12538 if (buildLog)
12539 buildLog.checkIntersection(seg1, seg2);
12540
12541 var i = eps.linesIntersect(a1, a2, b1, b2);
12542
12543 if (i === false){
12544 // segments are parallel or coincident
12545
12546 // if points aren't collinear, then the segments are parallel, so no intersections
12547 if (!eps.pointsCollinear(a1, a2, b1))
12548 return false;
12549 // otherwise, segments are on top of each other somehow (aka coincident)
12550
12551 if (eps.pointsSame(a1, b2) || eps.pointsSame(a2, b1))
12552 return false; // segments touch at endpoints... no intersection
12553
12554 var a1_equ_b1 = eps.pointsSame(a1, b1);
12555 var a2_equ_b2 = eps.pointsSame(a2, b2);
12556
12557 if (a1_equ_b1 && a2_equ_b2)
12558 return ev2; // segments are exactly equal
12559
12560 var a1_between = !a1_equ_b1 && eps.pointBetween(a1, b1, b2);
12561 var a2_between = !a2_equ_b2 && eps.pointBetween(a2, b1, b2);
12562
12563 // handy for debugging:
12564 // buildLog.log({
12565 // a1_equ_b1: a1_equ_b1,
12566 // a2_equ_b2: a2_equ_b2,
12567 // a1_between: a1_between,
12568 // a2_between: a2_between
12569 // });
12570
12571 if (a1_equ_b1){
12572 if (a2_between){
12573 // (a1)---(a2)
12574 // (b1)----------(b2)
12575 eventDivide(ev2, a2);
12576 }
12577 else{
12578 // (a1)----------(a2)
12579 // (b1)---(b2)
12580 eventDivide(ev1, b2);
12581 }
12582 return ev2;
12583 }
12584 else if (a1_between){
12585 if (!a2_equ_b2){
12586 // make a2 equal to b2
12587 if (a2_between){
12588 // (a1)---(a2)
12589 // (b1)-----------------(b2)
12590 eventDivide(ev2, a2);
12591 }
12592 else{
12593 // (a1)----------(a2)
12594 // (b1)----------(b2)
12595 eventDivide(ev1, b2);
12596 }
12597 }
12598
12599 // (a1)---(a2)
12600 // (b1)----------(b2)
12601 eventDivide(ev2, a1);
12602 }
12603 }
12604 else{
12605 // otherwise, lines intersect at i.pt, which may or may not be between the endpoints
12606
12607 // is A divided between its endpoints? (exclusive)
12608 if (i.alongA === 0){
12609 if (i.alongB === -1) // yes, at exactly b1
12610 eventDivide(ev1, b1);
12611 else if (i.alongB === 0) // yes, somewhere between B's endpoints
12612 eventDivide(ev1, i.pt);
12613 else if (i.alongB === 1) // yes, at exactly b2
12614 eventDivide(ev1, b2);
12615 }
12616
12617 // is B divided between its endpoints? (exclusive)
12618 if (i.alongB === 0){
12619 if (i.alongA === -1) // yes, at exactly a1
12620 eventDivide(ev2, a1);
12621 else if (i.alongA === 0) // yes, somewhere between A's endpoints (exclusive)
12622 eventDivide(ev2, i.pt);
12623 else if (i.alongA === 1) // yes, at exactly a2
12624 eventDivide(ev2, a2);
12625 }
12626 }
12627 return false;
12628 }
12629
12630 //
12631 // main event loop
12632 //
12633 var segments = [];
12634 while (!event_root.isEmpty()){
12635 var ev = event_root.getHead();
12636
12637 if (buildLog)
12638 buildLog.vert(ev.pt[0]);
12639
12640 if (ev.isStart){
12641
12642 if (buildLog)
12643 buildLog.segmentNew(ev.seg, ev.primary);
12644
12645 var surrounding = statusFindSurrounding(ev);
12646 var above = surrounding.before ? surrounding.before.ev : null;
12647 var below = surrounding.after ? surrounding.after.ev : null;
12648
12649 if (buildLog){
12650 buildLog.tempStatus(
12651 ev.seg,
12652 above ? above.seg : false,
12653 below ? below.seg : false
12654 );
12655 }
12656
12657 function checkBothIntersections(){
12658 if (above){
12659 var eve = checkIntersection(ev, above);
12660 if (eve)
12661 return eve;
12662 }
12663 if (below)
12664 return checkIntersection(ev, below);
12665 return false;
12666 }
12667
12668 var eve = checkBothIntersections();
12669 if (eve){
12670 // ev and eve are equal
12671 // we'll keep eve and throw away ev
12672
12673 // merge ev.seg's fill information into eve.seg
12674
12675 if (selfIntersection){
12676 var toggle; // are we a toggling edge?
12677 if (ev.seg.myFill.below === null)
12678 toggle = true;
12679 else
12680 toggle = ev.seg.myFill.above !== ev.seg.myFill.below;
12681
12682 // merge two segments that belong to the same polygon
12683 // think of this as sandwiching two segments together, where `eve.seg` is
12684 // the bottom -- this will cause the above fill flag to toggle
12685 if (toggle)
12686 eve.seg.myFill.above = !eve.seg.myFill.above;
12687 }
12688 else{
12689 // merge two segments that belong to different polygons
12690 // each segment has distinct knowledge, so no special logic is needed
12691 // note that this can only happen once per segment in this phase, because we
12692 // are guaranteed that all self-intersections are gone
12693 eve.seg.otherFill = ev.seg.myFill;
12694 }
12695
12696 if (buildLog)
12697 buildLog.segmentUpdate(eve.seg);
12698
12699 ev.other.remove();
12700 ev.remove();
12701 }
12702
12703 if (event_root.getHead() !== ev){
12704 // something was inserted before us in the event queue, so loop back around and
12705 // process it before continuing
12706 if (buildLog)
12707 buildLog.rewind(ev.seg);
12708 continue;
12709 }
12710
12711 //
12712 // calculate fill flags
12713 //
12714 if (selfIntersection){
12715 var toggle; // are we a toggling edge?
12716 if (ev.seg.myFill.below === null) // if we are a new segment...
12717 toggle = true; // then we toggle
12718 else // we are a segment that has previous knowledge from a division
12719 toggle = ev.seg.myFill.above !== ev.seg.myFill.below; // calculate toggle
12720
12721 // next, calculate whether we are filled below us
12722 if (!below){ // if nothing is below us...
12723 // we are filled below us if the polygon is inverted
12724 ev.seg.myFill.below = primaryPolyInverted;
12725 }
12726 else{
12727 // otherwise, we know the answer -- it's the same if whatever is below
12728 // us is filled above it
12729 ev.seg.myFill.below = below.seg.myFill.above;
12730 }
12731
12732 // since now we know if we're filled below us, we can calculate whether
12733 // we're filled above us by applying toggle to whatever is below us
12734 if (toggle)
12735 ev.seg.myFill.above = !ev.seg.myFill.below;
12736 else
12737 ev.seg.myFill.above = ev.seg.myFill.below;
12738 }
12739 else{
12740 // now we fill in any missing transition information, since we are all-knowing
12741 // at this point
12742
12743 if (ev.seg.otherFill === null){
12744 // if we don't have other information, then we need to figure out if we're
12745 // inside the other polygon
12746 var inside;
12747 if (!below){
12748 // if nothing is below us, then we're inside if the other polygon is
12749 // inverted
12750 inside =
12751 ev.primary ? secondaryPolyInverted : primaryPolyInverted;
12752 }
12753 else{ // otherwise, something is below us
12754 // so copy the below segment's other polygon's above
12755 if (ev.primary === below.primary)
12756 inside = below.seg.otherFill.above;
12757 else
12758 inside = below.seg.myFill.above;
12759 }
12760 ev.seg.otherFill = {
12761 above: inside,
12762 below: inside
12763 };
12764 }
12765 }
12766
12767 if (buildLog){
12768 buildLog.status(
12769 ev.seg,
12770 above ? above.seg : false,
12771 below ? below.seg : false
12772 );
12773 }
12774
12775 // insert the status and remember it for later removal
12776 ev.other.status = surrounding.insert(LinkedList.node({ ev: ev }));
12777 }
12778 else{
12779 var st = ev.status;
12780
12781 if (st === null){
12782 throw new Error('PolyBool: Zero-length segment detected; your epsilon is ' +
12783 'probably too small or too large');
12784 }
12785
12786 // removing the status will create two new adjacent edges, so we'll need to check
12787 // for those
12788 if (status_root.exists(st.prev) && status_root.exists(st.next))
12789 checkIntersection(st.prev.ev, st.next.ev);
12790
12791 if (buildLog)
12792 buildLog.statusRemove(st.ev.seg);
12793
12794 // remove the status
12795 st.remove();
12796
12797 // if we've reached this point, we've calculated everything there is to know, so
12798 // save the segment for reporting
12799 if (!ev.primary){
12800 // make sure `seg.myFill` actually points to the primary polygon though
12801 var s = ev.seg.myFill;
12802 ev.seg.myFill = ev.seg.otherFill;
12803 ev.seg.otherFill = s;
12804 }
12805 segments.push(ev.seg);
12806 }
12807
12808 // remove the event and continue
12809 event_root.getHead().remove();
12810 }
12811
12812 if (buildLog)
12813 buildLog.done();
12814
12815 return segments;
12816 }
12817
12818 // return the appropriate API depending on what we're doing
12819 if (!selfIntersection){
12820 // performing combination of polygons, so only deal with already-processed segments
12821 return {
12822 calculate: function(segments1, inverted1, segments2, inverted2){
12823 // segmentsX come from the self-intersection API, or this API
12824 // invertedX is whether we treat that list of segments as an inverted polygon or not
12825 // returns segments that can be used for further operations
12826 segments1.forEach(function(seg){
12827 eventAddSegment(segmentCopy(seg.start, seg.end, seg), true);
12828 });
12829 segments2.forEach(function(seg){
12830 eventAddSegment(segmentCopy(seg.start, seg.end, seg), false);
12831 });
12832 return calculate(inverted1, inverted2);
12833 }
12834 };
12835 }
12836
12837 // otherwise, performing self-intersection, so deal with regions
12838 return {
12839 addRegion: function(region){
12840 // regions are a list of points:
12841 // [ [0, 0], [100, 0], [50, 100] ]
12842 // you can add multiple regions before running calculate
12843 var pt1;
12844 var pt2 = region[region.length - 1];
12845 for (var i = 0; i < region.length; i++){
12846 pt1 = pt2;
12847 pt2 = region[i];
12848
12849 var forward = eps.pointsCompare(pt1, pt2);
12850 if (forward === 0) // points are equal, so we have a zero-length segment
12851 continue; // just skip it
12852
12853 eventAddSegment(
12854 segmentNew(
12855 forward < 0 ? pt1 : pt2,
12856 forward < 0 ? pt2 : pt1
12857 ),
12858 true
12859 );
12860 }
12861 },
12862 calculate: function(inverted){
12863 // is the polygon inverted?
12864 // returns segments
12865 return calculate(inverted, false);
12866 }
12867 };
12868}
12869
12870module.exports = Intersecter;
12871
12872},{"./linked-list":31}],31:[function(_dereq_,module,exports){
12873// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
12874// MIT License
12875// Project Home: https://github.com/voidqk/polybooljs
12876
12877//
12878// simple linked list implementation that allows you to traverse down nodes and save positions
12879//
12880
12881var LinkedList = {
12882 create: function(){
12883 var my = {
12884 root: { root: true, next: null },
12885 exists: function(node){
12886 if (node === null || node === my.root)
12887 return false;
12888 return true;
12889 },
12890 isEmpty: function(){
12891 return my.root.next === null;
12892 },
12893 getHead: function(){
12894 return my.root.next;
12895 },
12896 insertBefore: function(node, check){
12897 var last = my.root;
12898 var here = my.root.next;
12899 while (here !== null){
12900 if (check(here)){
12901 node.prev = here.prev;
12902 node.next = here;
12903 here.prev.next = node;
12904 here.prev = node;
12905 return;
12906 }
12907 last = here;
12908 here = here.next;
12909 }
12910 last.next = node;
12911 node.prev = last;
12912 node.next = null;
12913 },
12914 findTransition: function(check){
12915 var prev = my.root;
12916 var here = my.root.next;
12917 while (here !== null){
12918 if (check(here))
12919 break;
12920 prev = here;
12921 here = here.next;
12922 }
12923 return {
12924 before: prev === my.root ? null : prev,
12925 after: here,
12926 insert: function(node){
12927 node.prev = prev;
12928 node.next = here;
12929 prev.next = node;
12930 if (here !== null)
12931 here.prev = node;
12932 return node;
12933 }
12934 };
12935 }
12936 };
12937 return my;
12938 },
12939 node: function(data){
12940 data.prev = null;
12941 data.next = null;
12942 data.remove = function(){
12943 data.prev.next = data.next;
12944 if (data.next)
12945 data.next.prev = data.prev;
12946 data.prev = null;
12947 data.next = null;
12948 };
12949 return data;
12950 }
12951};
12952
12953module.exports = LinkedList;
12954
12955},{}],32:[function(_dereq_,module,exports){
12956// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
12957// MIT License
12958// Project Home: https://github.com/voidqk/polybooljs
12959
12960//
12961// converts a list of segments into a list of regions, while also removing unnecessary verticies
12962//
12963
12964function SegmentChainer(segments, eps, buildLog){
12965 var chains = [];
12966 var regions = [];
12967
12968 segments.forEach(function(seg){
12969 var pt1 = seg.start;
12970 var pt2 = seg.end;
12971 if (eps.pointsSame(pt1, pt2)){
12972 console.warn('PolyBool: Warning: Zero-length segment detected; your epsilon is ' +
12973 'probably too small or too large');
12974 return;
12975 }
12976
12977 if (buildLog)
12978 buildLog.chainStart(seg);
12979
12980 // search for two chains that this segment matches
12981 var first_match = {
12982 index: 0,
12983 matches_head: false,
12984 matches_pt1: false
12985 };
12986 var second_match = {
12987 index: 0,
12988 matches_head: false,
12989 matches_pt1: false
12990 };
12991 var next_match = first_match;
12992 function setMatch(index, matches_head, matches_pt1){
12993 // return true if we've matched twice
12994 next_match.index = index;
12995 next_match.matches_head = matches_head;
12996 next_match.matches_pt1 = matches_pt1;
12997 if (next_match === first_match){
12998 next_match = second_match;
12999 return false;
13000 }
13001 next_match = null;
13002 return true; // we've matched twice, we're done here
13003 }
13004 for (var i = 0; i < chains.length; i++){
13005 var chain = chains[i];
13006 var head = chain[0];
13007 var head2 = chain[1];
13008 var tail = chain[chain.length - 1];
13009 var tail2 = chain[chain.length - 2];
13010 if (eps.pointsSame(head, pt1)){
13011 if (setMatch(i, true, true))
13012 break;
13013 }
13014 else if (eps.pointsSame(head, pt2)){
13015 if (setMatch(i, true, false))
13016 break;
13017 }
13018 else if (eps.pointsSame(tail, pt1)){
13019 if (setMatch(i, false, true))
13020 break;
13021 }
13022 else if (eps.pointsSame(tail, pt2)){
13023 if (setMatch(i, false, false))
13024 break;
13025 }
13026 }
13027
13028 if (next_match === first_match){
13029 // we didn't match anything, so create a new chain
13030 chains.push([ pt1, pt2 ]);
13031 if (buildLog)
13032 buildLog.chainNew(pt1, pt2);
13033 return;
13034 }
13035
13036 if (next_match === second_match){
13037 // we matched a single chain
13038
13039 if (buildLog)
13040 buildLog.chainMatch(first_match.index);
13041
13042 // add the other point to the apporpriate end, and check to see if we've closed the
13043 // chain into a loop
13044
13045 var index = first_match.index;
13046 var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc
13047 var addToHead = first_match.matches_head; // if we matched at head, then add to the head
13048
13049 var chain = chains[index];
13050 var grow = addToHead ? chain[0] : chain[chain.length - 1];
13051 var grow2 = addToHead ? chain[1] : chain[chain.length - 2];
13052 var oppo = addToHead ? chain[chain.length - 1] : chain[0];
13053 var oppo2 = addToHead ? chain[chain.length - 2] : chain[1];
13054
13055 if (eps.pointsCollinear(grow2, grow, pt)){
13056 // grow isn't needed because it's directly between grow2 and pt:
13057 // grow2 ---grow---> pt
13058 if (addToHead){
13059 if (buildLog)
13060 buildLog.chainRemoveHead(first_match.index, pt);
13061 chain.shift();
13062 }
13063 else{
13064 if (buildLog)
13065 buildLog.chainRemoveTail(first_match.index, pt);
13066 chain.pop();
13067 }
13068 grow = grow2; // old grow is gone... new grow is what grow2 was
13069 }
13070
13071 if (eps.pointsSame(oppo, pt)){
13072 // we're closing the loop, so remove chain from chains
13073 chains.splice(index, 1);
13074
13075 if (eps.pointsCollinear(oppo2, oppo, grow)){
13076 // oppo isn't needed because it's directly between oppo2 and grow:
13077 // oppo2 ---oppo--->grow
13078 if (addToHead){
13079 if (buildLog)
13080 buildLog.chainRemoveTail(first_match.index, grow);
13081 chain.pop();
13082 }
13083 else{
13084 if (buildLog)
13085 buildLog.chainRemoveHead(first_match.index, grow);
13086 chain.shift();
13087 }
13088 }
13089
13090 if (buildLog)
13091 buildLog.chainClose(first_match.index);
13092
13093 // we have a closed chain!
13094 regions.push(chain);
13095 return;
13096 }
13097
13098 // not closing a loop, so just add it to the apporpriate side
13099 if (addToHead){
13100 if (buildLog)
13101 buildLog.chainAddHead(first_match.index, pt);
13102 chain.unshift(pt);
13103 }
13104 else{
13105 if (buildLog)
13106 buildLog.chainAddTail(first_match.index, pt);
13107 chain.push(pt);
13108 }
13109 return;
13110 }
13111
13112 // otherwise, we matched two chains, so we need to combine those chains together
13113
13114 function reverseChain(index){
13115 if (buildLog)
13116 buildLog.chainReverse(index);
13117 chains[index].reverse(); // gee, that's easy
13118 }
13119
13120 function appendChain(index1, index2){
13121 // index1 gets index2 appended to it, and index2 is removed
13122 var chain1 = chains[index1];
13123 var chain2 = chains[index2];
13124 var tail = chain1[chain1.length - 1];
13125 var tail2 = chain1[chain1.length - 2];
13126 var head = chain2[0];
13127 var head2 = chain2[1];
13128
13129 if (eps.pointsCollinear(tail2, tail, head)){
13130 // tail isn't needed because it's directly between tail2 and head
13131 // tail2 ---tail---> head
13132 if (buildLog)
13133 buildLog.chainRemoveTail(index1, tail);
13134 chain1.pop();
13135 tail = tail2; // old tail is gone... new tail is what tail2 was
13136 }
13137
13138 if (eps.pointsCollinear(tail, head, head2)){
13139 // head isn't needed because it's directly between tail and head2
13140 // tail ---head---> head2
13141 if (buildLog)
13142 buildLog.chainRemoveHead(index2, head);
13143 chain2.shift();
13144 }
13145
13146 if (buildLog)
13147 buildLog.chainJoin(index1, index2);
13148 chains[index1] = chain1.concat(chain2);
13149 chains.splice(index2, 1);
13150 }
13151
13152 var F = first_match.index;
13153 var S = second_match.index;
13154
13155 if (buildLog)
13156 buildLog.chainConnect(F, S);
13157
13158 var reverseF = chains[F].length < chains[S].length; // reverse the shorter chain, if needed
13159 if (first_match.matches_head){
13160 if (second_match.matches_head){
13161 if (reverseF){
13162 // <<<< F <<<< --- >>>> S >>>>
13163 reverseChain(F);
13164 // >>>> F >>>> --- >>>> S >>>>
13165 appendChain(F, S);
13166 }
13167 else{
13168 // <<<< F <<<< --- >>>> S >>>>
13169 reverseChain(S);
13170 // <<<< F <<<< --- <<<< S <<<< logically same as:
13171 // >>>> S >>>> --- >>>> F >>>>
13172 appendChain(S, F);
13173 }
13174 }
13175 else{
13176 // <<<< F <<<< --- <<<< S <<<< logically same as:
13177 // >>>> S >>>> --- >>>> F >>>>
13178 appendChain(S, F);
13179 }
13180 }
13181 else{
13182 if (second_match.matches_head){
13183 // >>>> F >>>> --- >>>> S >>>>
13184 appendChain(F, S);
13185 }
13186 else{
13187 if (reverseF){
13188 // >>>> F >>>> --- <<<< S <<<<
13189 reverseChain(F);
13190 // <<<< F <<<< --- <<<< S <<<< logically same as:
13191 // >>>> S >>>> --- >>>> F >>>>
13192 appendChain(S, F);
13193 }
13194 else{
13195 // >>>> F >>>> --- <<<< S <<<<
13196 reverseChain(S);
13197 // >>>> F >>>> --- >>>> S >>>>
13198 appendChain(F, S);
13199 }
13200 }
13201 }
13202 });
13203
13204 return regions;
13205}
13206
13207module.exports = SegmentChainer;
13208
13209},{}],33:[function(_dereq_,module,exports){
13210// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
13211// MIT License
13212// Project Home: https://github.com/voidqk/polybooljs
13213
13214//
13215// filter a list of segments based on boolean operations
13216//
13217
13218function select(segments, selection, buildLog){
13219 var result = [];
13220 segments.forEach(function(seg){
13221 var index =
13222 (seg.myFill.above ? 8 : 0) +
13223 (seg.myFill.below ? 4 : 0) +
13224 ((seg.otherFill && seg.otherFill.above) ? 2 : 0) +
13225 ((seg.otherFill && seg.otherFill.below) ? 1 : 0);
13226 if (selection[index] !== 0){
13227 // copy the segment to the results, while also calculating the fill status
13228 result.push({
13229 id: buildLog ? buildLog.segmentId() : -1,
13230 start: seg.start,
13231 end: seg.end,
13232 myFill: {
13233 above: selection[index] === 1, // 1 if filled above
13234 below: selection[index] === 2 // 2 if filled below
13235 },
13236 otherFill: null
13237 });
13238 }
13239 });
13240
13241 if (buildLog)
13242 buildLog.selected(result);
13243
13244 return result;
13245}
13246
13247var SegmentSelector = {
13248 union: function(segments, buildLog){ // primary | secondary
13249 // above1 below1 above2 below2 Keep? Value
13250 // 0 0 0 0 => no 0
13251 // 0 0 0 1 => yes filled below 2
13252 // 0 0 1 0 => yes filled above 1
13253 // 0 0 1 1 => no 0
13254 // 0 1 0 0 => yes filled below 2
13255 // 0 1 0 1 => yes filled below 2
13256 // 0 1 1 0 => no 0
13257 // 0 1 1 1 => no 0
13258 // 1 0 0 0 => yes filled above 1
13259 // 1 0 0 1 => no 0
13260 // 1 0 1 0 => yes filled above 1
13261 // 1 0 1 1 => no 0
13262 // 1 1 0 0 => no 0
13263 // 1 1 0 1 => no 0
13264 // 1 1 1 0 => no 0
13265 // 1 1 1 1 => no 0
13266 return select(segments, [
13267 0, 2, 1, 0,
13268 2, 2, 0, 0,
13269 1, 0, 1, 0,
13270 0, 0, 0, 0
13271 ], buildLog);
13272 },
13273 intersect: function(segments, buildLog){ // primary & secondary
13274 // above1 below1 above2 below2 Keep? Value
13275 // 0 0 0 0 => no 0
13276 // 0 0 0 1 => no 0
13277 // 0 0 1 0 => no 0
13278 // 0 0 1 1 => no 0
13279 // 0 1 0 0 => no 0
13280 // 0 1 0 1 => yes filled below 2
13281 // 0 1 1 0 => no 0
13282 // 0 1 1 1 => yes filled below 2
13283 // 1 0 0 0 => no 0
13284 // 1 0 0 1 => no 0
13285 // 1 0 1 0 => yes filled above 1
13286 // 1 0 1 1 => yes filled above 1
13287 // 1 1 0 0 => no 0
13288 // 1 1 0 1 => yes filled below 2
13289 // 1 1 1 0 => yes filled above 1
13290 // 1 1 1 1 => no 0
13291 return select(segments, [
13292 0, 0, 0, 0,
13293 0, 2, 0, 2,
13294 0, 0, 1, 1,
13295 0, 2, 1, 0
13296 ], buildLog);
13297 },
13298 difference: function(segments, buildLog){ // primary - secondary
13299 // above1 below1 above2 below2 Keep? Value
13300 // 0 0 0 0 => no 0
13301 // 0 0 0 1 => no 0
13302 // 0 0 1 0 => no 0
13303 // 0 0 1 1 => no 0
13304 // 0 1 0 0 => yes filled below 2
13305 // 0 1 0 1 => no 0
13306 // 0 1 1 0 => yes filled below 2
13307 // 0 1 1 1 => no 0
13308 // 1 0 0 0 => yes filled above 1
13309 // 1 0 0 1 => yes filled above 1
13310 // 1 0 1 0 => no 0
13311 // 1 0 1 1 => no 0
13312 // 1 1 0 0 => no 0
13313 // 1 1 0 1 => yes filled above 1
13314 // 1 1 1 0 => yes filled below 2
13315 // 1 1 1 1 => no 0
13316 return select(segments, [
13317 0, 0, 0, 0,
13318 2, 0, 2, 0,
13319 1, 1, 0, 0,
13320 0, 1, 2, 0
13321 ], buildLog);
13322 },
13323 differenceRev: function(segments, buildLog){ // secondary - primary
13324 // above1 below1 above2 below2 Keep? Value
13325 // 0 0 0 0 => no 0
13326 // 0 0 0 1 => yes filled below 2
13327 // 0 0 1 0 => yes filled above 1
13328 // 0 0 1 1 => no 0
13329 // 0 1 0 0 => no 0
13330 // 0 1 0 1 => no 0
13331 // 0 1 1 0 => yes filled above 1
13332 // 0 1 1 1 => yes filled above 1
13333 // 1 0 0 0 => no 0
13334 // 1 0 0 1 => yes filled below 2
13335 // 1 0 1 0 => no 0
13336 // 1 0 1 1 => yes filled below 2
13337 // 1 1 0 0 => no 0
13338 // 1 1 0 1 => no 0
13339 // 1 1 1 0 => no 0
13340 // 1 1 1 1 => no 0
13341 return select(segments, [
13342 0, 2, 1, 0,
13343 0, 0, 1, 1,
13344 0, 2, 0, 2,
13345 0, 0, 0, 0
13346 ], buildLog);
13347 },
13348 xor: function(segments, buildLog){ // primary ^ secondary
13349 // above1 below1 above2 below2 Keep? Value
13350 // 0 0 0 0 => no 0
13351 // 0 0 0 1 => yes filled below 2
13352 // 0 0 1 0 => yes filled above 1
13353 // 0 0 1 1 => no 0
13354 // 0 1 0 0 => yes filled below 2
13355 // 0 1 0 1 => no 0
13356 // 0 1 1 0 => no 0
13357 // 0 1 1 1 => yes filled above 1
13358 // 1 0 0 0 => yes filled above 1
13359 // 1 0 0 1 => no 0
13360 // 1 0 1 0 => no 0
13361 // 1 0 1 1 => yes filled below 2
13362 // 1 1 0 0 => no 0
13363 // 1 1 0 1 => yes filled above 1
13364 // 1 1 1 0 => yes filled below 2
13365 // 1 1 1 1 => no 0
13366 return select(segments, [
13367 0, 2, 1, 0,
13368 2, 0, 0, 1,
13369 1, 0, 0, 2,
13370 0, 1, 2, 0
13371 ], buildLog);
13372 }
13373};
13374
13375module.exports = SegmentSelector;
13376
13377},{}],34:[function(_dereq_,module,exports){
13378// shim for using process in browser
13379var process = module.exports = {};
13380
13381// cached from whatever global is present so that test runners that stub it
13382// don't break things. But we need to wrap it in a try catch in case it is
13383// wrapped in strict mode code which doesn't define any globals. It's inside a
13384// function because try/catches deoptimize in certain engines.
13385
13386var cachedSetTimeout;
13387var cachedClearTimeout;
13388
13389function defaultSetTimout() {
13390 throw new Error('setTimeout has not been defined');
13391}
13392function defaultClearTimeout () {
13393 throw new Error('clearTimeout has not been defined');
13394}
13395(function () {
13396 try {
13397 if (typeof setTimeout === 'function') {
13398 cachedSetTimeout = setTimeout;
13399 } else {
13400 cachedSetTimeout = defaultSetTimout;
13401 }
13402 } catch (e) {
13403 cachedSetTimeout = defaultSetTimout;
13404 }
13405 try {
13406 if (typeof clearTimeout === 'function') {
13407 cachedClearTimeout = clearTimeout;
13408 } else {
13409 cachedClearTimeout = defaultClearTimeout;
13410 }
13411 } catch (e) {
13412 cachedClearTimeout = defaultClearTimeout;
13413 }
13414} ())
13415function runTimeout(fun) {
13416 if (cachedSetTimeout === setTimeout) {
13417 //normal enviroments in sane situations
13418 return setTimeout(fun, 0);
13419 }
13420 // if setTimeout wasn't available but was latter defined
13421 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
13422 cachedSetTimeout = setTimeout;
13423 return setTimeout(fun, 0);
13424 }
13425 try {
13426 // when when somebody has screwed with setTimeout but no I.E. maddness
13427 return cachedSetTimeout(fun, 0);
13428 } catch(e){
13429 try {
13430 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
13431 return cachedSetTimeout.call(null, fun, 0);
13432 } catch(e){
13433 // 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
13434 return cachedSetTimeout.call(this, fun, 0);
13435 }
13436 }
13437
13438
13439}
13440function runClearTimeout(marker) {
13441 if (cachedClearTimeout === clearTimeout) {
13442 //normal enviroments in sane situations
13443 return clearTimeout(marker);
13444 }
13445 // if clearTimeout wasn't available but was latter defined
13446 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
13447 cachedClearTimeout = clearTimeout;
13448 return clearTimeout(marker);
13449 }
13450 try {
13451 // when when somebody has screwed with setTimeout but no I.E. maddness
13452 return cachedClearTimeout(marker);
13453 } catch (e){
13454 try {
13455 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
13456 return cachedClearTimeout.call(null, marker);
13457 } catch (e){
13458 // 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.
13459 // Some versions of I.E. have different rules for clearTimeout vs setTimeout
13460 return cachedClearTimeout.call(this, marker);
13461 }
13462 }
13463
13464
13465
13466}
13467var queue = [];
13468var draining = false;
13469var currentQueue;
13470var queueIndex = -1;
13471
13472function cleanUpNextTick() {
13473 if (!draining || !currentQueue) {
13474 return;
13475 }
13476 draining = false;
13477 if (currentQueue.length) {
13478 queue = currentQueue.concat(queue);
13479 } else {
13480 queueIndex = -1;
13481 }
13482 if (queue.length) {
13483 drainQueue();
13484 }
13485}
13486
13487function drainQueue() {
13488 if (draining) {
13489 return;
13490 }
13491 var timeout = runTimeout(cleanUpNextTick);
13492 draining = true;
13493
13494 var len = queue.length;
13495 while(len) {
13496 currentQueue = queue;
13497 queue = [];
13498 while (++queueIndex < len) {
13499 if (currentQueue) {
13500 currentQueue[queueIndex].run();
13501 }
13502 }
13503 queueIndex = -1;
13504 len = queue.length;
13505 }
13506 currentQueue = null;
13507 draining = false;
13508 runClearTimeout(timeout);
13509}
13510
13511process.nextTick = function (fun) {
13512 var args = new Array(arguments.length - 1);
13513 if (arguments.length > 1) {
13514 for (var i = 1; i < arguments.length; i++) {
13515 args[i - 1] = arguments[i];
13516 }
13517 }
13518 queue.push(new Item(fun, args));
13519 if (queue.length === 1 && !draining) {
13520 runTimeout(drainQueue);
13521 }
13522};
13523
13524// v8 likes predictible objects
13525function Item(fun, array) {
13526 this.fun = fun;
13527 this.array = array;
13528}
13529Item.prototype.run = function () {
13530 this.fun.apply(null, this.array);
13531};
13532process.title = 'browser';
13533process.browser = true;
13534process.env = {};
13535process.argv = [];
13536process.version = ''; // empty string to avoid regexp issues
13537process.versions = {};
13538
13539function noop() {}
13540
13541process.on = noop;
13542process.addListener = noop;
13543process.once = noop;
13544process.off = noop;
13545process.removeListener = noop;
13546process.removeAllListeners = noop;
13547process.emit = noop;
13548process.prependListener = noop;
13549process.prependOnceListener = noop;
13550
13551process.listeners = function (name) { return [] }
13552
13553process.binding = function (name) {
13554 throw new Error('process.binding is not supported');
13555};
13556
13557process.cwd = function () { return '/' };
13558process.chdir = function (dir) {
13559 throw new Error('process.chdir is not supported');
13560};
13561process.umask = function() { return 0; };
13562
13563},{}],35:[function(_dereq_,module,exports){
13564// TinyColor v1.4.1
13565// https://github.com/bgrins/TinyColor
13566// Brian Grinstead, MIT License
13567
13568(function(Math) {
13569
13570var trimLeft = /^\s+/,
13571 trimRight = /\s+$/,
13572 tinyCounter = 0,
13573 mathRound = Math.round,
13574 mathMin = Math.min,
13575 mathMax = Math.max,
13576 mathRandom = Math.random;
13577
13578function tinycolor (color, opts) {
13579
13580 color = (color) ? color : '';
13581 opts = opts || { };
13582
13583 // If input is already a tinycolor, return itself
13584 if (color instanceof tinycolor) {
13585 return color;
13586 }
13587 // If we are called as a function, call using new instead
13588 if (!(this instanceof tinycolor)) {
13589 return new tinycolor(color, opts);
13590 }
13591
13592 var rgb = inputToRGB(color);
13593 this._originalInput = color,
13594 this._r = rgb.r,
13595 this._g = rgb.g,
13596 this._b = rgb.b,
13597 this._a = rgb.a,
13598 this._roundA = mathRound(100*this._a) / 100,
13599 this._format = opts.format || rgb.format;
13600 this._gradientType = opts.gradientType;
13601
13602 // Don't let the range of [0,255] come back in [0,1].
13603 // Potentially lose a little bit of precision here, but will fix issues where
13604 // .5 gets interpreted as half of the total, instead of half of 1
13605 // If it was supposed to be 128, this was already taken care of by `inputToRgb`
13606 if (this._r < 1) { this._r = mathRound(this._r); }
13607 if (this._g < 1) { this._g = mathRound(this._g); }
13608 if (this._b < 1) { this._b = mathRound(this._b); }
13609
13610 this._ok = rgb.ok;
13611 this._tc_id = tinyCounter++;
13612}
13613
13614tinycolor.prototype = {
13615 isDark: function() {
13616 return this.getBrightness() < 128;
13617 },
13618 isLight: function() {
13619 return !this.isDark();
13620 },
13621 isValid: function() {
13622 return this._ok;
13623 },
13624 getOriginalInput: function() {
13625 return this._originalInput;
13626 },
13627 getFormat: function() {
13628 return this._format;
13629 },
13630 getAlpha: function() {
13631 return this._a;
13632 },
13633 getBrightness: function() {
13634 //http://www.w3.org/TR/AERT#color-contrast
13635 var rgb = this.toRgb();
13636 return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
13637 },
13638 getLuminance: function() {
13639 //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
13640 var rgb = this.toRgb();
13641 var RsRGB, GsRGB, BsRGB, R, G, B;
13642 RsRGB = rgb.r/255;
13643 GsRGB = rgb.g/255;
13644 BsRGB = rgb.b/255;
13645
13646 if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
13647 if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
13648 if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
13649 return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
13650 },
13651 setAlpha: function(value) {
13652 this._a = boundAlpha(value);
13653 this._roundA = mathRound(100*this._a) / 100;
13654 return this;
13655 },
13656 toHsv: function() {
13657 var hsv = rgbToHsv(this._r, this._g, this._b);
13658 return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
13659 },
13660 toHsvString: function() {
13661 var hsv = rgbToHsv(this._r, this._g, this._b);
13662 var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
13663 return (this._a == 1) ?
13664 "hsv(" + h + ", " + s + "%, " + v + "%)" :
13665 "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
13666 },
13667 toHsl: function() {
13668 var hsl = rgbToHsl(this._r, this._g, this._b);
13669 return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
13670 },
13671 toHslString: function() {
13672 var hsl = rgbToHsl(this._r, this._g, this._b);
13673 var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
13674 return (this._a == 1) ?
13675 "hsl(" + h + ", " + s + "%, " + l + "%)" :
13676 "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
13677 },
13678 toHex: function(allow3Char) {
13679 return rgbToHex(this._r, this._g, this._b, allow3Char);
13680 },
13681 toHexString: function(allow3Char) {
13682 return '#' + this.toHex(allow3Char);
13683 },
13684 toHex8: function(allow4Char) {
13685 return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
13686 },
13687 toHex8String: function(allow4Char) {
13688 return '#' + this.toHex8(allow4Char);
13689 },
13690 toRgb: function() {
13691 return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
13692 },
13693 toRgbString: function() {
13694 return (this._a == 1) ?
13695 "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
13696 "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
13697 },
13698 toPercentageRgb: function() {
13699 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 };
13700 },
13701 toPercentageRgbString: function() {
13702 return (this._a == 1) ?
13703 "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
13704 "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
13705 },
13706 toName: function() {
13707 if (this._a === 0) {
13708 return "transparent";
13709 }
13710
13711 if (this._a < 1) {
13712 return false;
13713 }
13714
13715 return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
13716 },
13717 toFilter: function(secondColor) {
13718 var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
13719 var secondHex8String = hex8String;
13720 var gradientType = this._gradientType ? "GradientType = 1, " : "";
13721
13722 if (secondColor) {
13723 var s = tinycolor(secondColor);
13724 secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
13725 }
13726
13727 return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
13728 },
13729 toString: function(format) {
13730 var formatSet = !!format;
13731 format = format || this._format;
13732
13733 var formattedString = false;
13734 var hasAlpha = this._a < 1 && this._a >= 0;
13735 var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
13736
13737 if (needsAlphaFormat) {
13738 // Special case for "transparent", all other non-alpha formats
13739 // will return rgba when there is transparency.
13740 if (format === "name" && this._a === 0) {
13741 return this.toName();
13742 }
13743 return this.toRgbString();
13744 }
13745 if (format === "rgb") {
13746 formattedString = this.toRgbString();
13747 }
13748 if (format === "prgb") {
13749 formattedString = this.toPercentageRgbString();
13750 }
13751 if (format === "hex" || format === "hex6") {
13752 formattedString = this.toHexString();
13753 }
13754 if (format === "hex3") {
13755 formattedString = this.toHexString(true);
13756 }
13757 if (format === "hex4") {
13758 formattedString = this.toHex8String(true);
13759 }
13760 if (format === "hex8") {
13761 formattedString = this.toHex8String();
13762 }
13763 if (format === "name") {
13764 formattedString = this.toName();
13765 }
13766 if (format === "hsl") {
13767 formattedString = this.toHslString();
13768 }
13769 if (format === "hsv") {
13770 formattedString = this.toHsvString();
13771 }
13772
13773 return formattedString || this.toHexString();
13774 },
13775 clone: function() {
13776 return tinycolor(this.toString());
13777 },
13778
13779 _applyModification: function(fn, args) {
13780 var color = fn.apply(null, [this].concat([].slice.call(args)));
13781 this._r = color._r;
13782 this._g = color._g;
13783 this._b = color._b;
13784 this.setAlpha(color._a);
13785 return this;
13786 },
13787 lighten: function() {
13788 return this._applyModification(lighten, arguments);
13789 },
13790 brighten: function() {
13791 return this._applyModification(brighten, arguments);
13792 },
13793 darken: function() {
13794 return this._applyModification(darken, arguments);
13795 },
13796 desaturate: function() {
13797 return this._applyModification(desaturate, arguments);
13798 },
13799 saturate: function() {
13800 return this._applyModification(saturate, arguments);
13801 },
13802 greyscale: function() {
13803 return this._applyModification(greyscale, arguments);
13804 },
13805 spin: function() {
13806 return this._applyModification(spin, arguments);
13807 },
13808
13809 _applyCombination: function(fn, args) {
13810 return fn.apply(null, [this].concat([].slice.call(args)));
13811 },
13812 analogous: function() {
13813 return this._applyCombination(analogous, arguments);
13814 },
13815 complement: function() {
13816 return this._applyCombination(complement, arguments);
13817 },
13818 monochromatic: function() {
13819 return this._applyCombination(monochromatic, arguments);
13820 },
13821 splitcomplement: function() {
13822 return this._applyCombination(splitcomplement, arguments);
13823 },
13824 triad: function() {
13825 return this._applyCombination(triad, arguments);
13826 },
13827 tetrad: function() {
13828 return this._applyCombination(tetrad, arguments);
13829 }
13830};
13831
13832// If input is an object, force 1 into "1.0" to handle ratios properly
13833// String input requires "1.0" as input, so 1 will be treated as 1
13834tinycolor.fromRatio = function(color, opts) {
13835 if (typeof color == "object") {
13836 var newColor = {};
13837 for (var i in color) {
13838 if (color.hasOwnProperty(i)) {
13839 if (i === "a") {
13840 newColor[i] = color[i];
13841 }
13842 else {
13843 newColor[i] = convertToPercentage(color[i]);
13844 }
13845 }
13846 }
13847 color = newColor;
13848 }
13849
13850 return tinycolor(color, opts);
13851};
13852
13853// Given a string or object, convert that input to RGB
13854// Possible string inputs:
13855//
13856// "red"
13857// "#f00" or "f00"
13858// "#ff0000" or "ff0000"
13859// "#ff000000" or "ff000000"
13860// "rgb 255 0 0" or "rgb (255, 0, 0)"
13861// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
13862// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
13863// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
13864// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
13865// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
13866// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
13867//
13868function inputToRGB(color) {
13869
13870 var rgb = { r: 0, g: 0, b: 0 };
13871 var a = 1;
13872 var s = null;
13873 var v = null;
13874 var l = null;
13875 var ok = false;
13876 var format = false;
13877
13878 if (typeof color == "string") {
13879 color = stringInputToObject(color);
13880 }
13881
13882 if (typeof color == "object") {
13883 if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
13884 rgb = rgbToRgb(color.r, color.g, color.b);
13885 ok = true;
13886 format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
13887 }
13888 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
13889 s = convertToPercentage(color.s);
13890 v = convertToPercentage(color.v);
13891 rgb = hsvToRgb(color.h, s, v);
13892 ok = true;
13893 format = "hsv";
13894 }
13895 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
13896 s = convertToPercentage(color.s);
13897 l = convertToPercentage(color.l);
13898 rgb = hslToRgb(color.h, s, l);
13899 ok = true;
13900 format = "hsl";
13901 }
13902
13903 if (color.hasOwnProperty("a")) {
13904 a = color.a;
13905 }
13906 }
13907
13908 a = boundAlpha(a);
13909
13910 return {
13911 ok: ok,
13912 format: color.format || format,
13913 r: mathMin(255, mathMax(rgb.r, 0)),
13914 g: mathMin(255, mathMax(rgb.g, 0)),
13915 b: mathMin(255, mathMax(rgb.b, 0)),
13916 a: a
13917 };
13918}
13919
13920
13921// Conversion Functions
13922// --------------------
13923
13924// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
13925// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
13926
13927// `rgbToRgb`
13928// Handle bounds / percentage checking to conform to CSS color spec
13929// <http://www.w3.org/TR/css3-color/>
13930// *Assumes:* r, g, b in [0, 255] or [0, 1]
13931// *Returns:* { r, g, b } in [0, 255]
13932function rgbToRgb(r, g, b){
13933 return {
13934 r: bound01(r, 255) * 255,
13935 g: bound01(g, 255) * 255,
13936 b: bound01(b, 255) * 255
13937 };
13938}
13939
13940// `rgbToHsl`
13941// Converts an RGB color value to HSL.
13942// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
13943// *Returns:* { h, s, l } in [0,1]
13944function rgbToHsl(r, g, b) {
13945
13946 r = bound01(r, 255);
13947 g = bound01(g, 255);
13948 b = bound01(b, 255);
13949
13950 var max = mathMax(r, g, b), min = mathMin(r, g, b);
13951 var h, s, l = (max + min) / 2;
13952
13953 if(max == min) {
13954 h = s = 0; // achromatic
13955 }
13956 else {
13957 var d = max - min;
13958 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
13959 switch(max) {
13960 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
13961 case g: h = (b - r) / d + 2; break;
13962 case b: h = (r - g) / d + 4; break;
13963 }
13964
13965 h /= 6;
13966 }
13967
13968 return { h: h, s: s, l: l };
13969}
13970
13971// `hslToRgb`
13972// Converts an HSL color value to RGB.
13973// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
13974// *Returns:* { r, g, b } in the set [0, 255]
13975function hslToRgb(h, s, l) {
13976 var r, g, b;
13977
13978 h = bound01(h, 360);
13979 s = bound01(s, 100);
13980 l = bound01(l, 100);
13981
13982 function hue2rgb(p, q, t) {
13983 if(t < 0) t += 1;
13984 if(t > 1) t -= 1;
13985 if(t < 1/6) return p + (q - p) * 6 * t;
13986 if(t < 1/2) return q;
13987 if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
13988 return p;
13989 }
13990
13991 if(s === 0) {
13992 r = g = b = l; // achromatic
13993 }
13994 else {
13995 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
13996 var p = 2 * l - q;
13997 r = hue2rgb(p, q, h + 1/3);
13998 g = hue2rgb(p, q, h);
13999 b = hue2rgb(p, q, h - 1/3);
14000 }
14001
14002 return { r: r * 255, g: g * 255, b: b * 255 };
14003}
14004
14005// `rgbToHsv`
14006// Converts an RGB color value to HSV
14007// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
14008// *Returns:* { h, s, v } in [0,1]
14009function rgbToHsv(r, g, b) {
14010
14011 r = bound01(r, 255);
14012 g = bound01(g, 255);
14013 b = bound01(b, 255);
14014
14015 var max = mathMax(r, g, b), min = mathMin(r, g, b);
14016 var h, s, v = max;
14017
14018 var d = max - min;
14019 s = max === 0 ? 0 : d / max;
14020
14021 if(max == min) {
14022 h = 0; // achromatic
14023 }
14024 else {
14025 switch(max) {
14026 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
14027 case g: h = (b - r) / d + 2; break;
14028 case b: h = (r - g) / d + 4; break;
14029 }
14030 h /= 6;
14031 }
14032 return { h: h, s: s, v: v };
14033}
14034
14035// `hsvToRgb`
14036// Converts an HSV color value to RGB.
14037// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
14038// *Returns:* { r, g, b } in the set [0, 255]
14039 function hsvToRgb(h, s, v) {
14040
14041 h = bound01(h, 360) * 6;
14042 s = bound01(s, 100);
14043 v = bound01(v, 100);
14044
14045 var i = Math.floor(h),
14046 f = h - i,
14047 p = v * (1 - s),
14048 q = v * (1 - f * s),
14049 t = v * (1 - (1 - f) * s),
14050 mod = i % 6,
14051 r = [v, q, p, p, t, v][mod],
14052 g = [t, v, v, q, p, p][mod],
14053 b = [p, p, t, v, v, q][mod];
14054
14055 return { r: r * 255, g: g * 255, b: b * 255 };
14056}
14057
14058// `rgbToHex`
14059// Converts an RGB color to hex
14060// Assumes r, g, and b are contained in the set [0, 255]
14061// Returns a 3 or 6 character hex
14062function rgbToHex(r, g, b, allow3Char) {
14063
14064 var hex = [
14065 pad2(mathRound(r).toString(16)),
14066 pad2(mathRound(g).toString(16)),
14067 pad2(mathRound(b).toString(16))
14068 ];
14069
14070 // Return a 3 character hex if possible
14071 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)) {
14072 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
14073 }
14074
14075 return hex.join("");
14076}
14077
14078// `rgbaToHex`
14079// Converts an RGBA color plus alpha transparency to hex
14080// Assumes r, g, b are contained in the set [0, 255] and
14081// a in [0, 1]. Returns a 4 or 8 character rgba hex
14082function rgbaToHex(r, g, b, a, allow4Char) {
14083
14084 var hex = [
14085 pad2(mathRound(r).toString(16)),
14086 pad2(mathRound(g).toString(16)),
14087 pad2(mathRound(b).toString(16)),
14088 pad2(convertDecimalToHex(a))
14089 ];
14090
14091 // Return a 4 character hex if possible
14092 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)) {
14093 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
14094 }
14095
14096 return hex.join("");
14097}
14098
14099// `rgbaToArgbHex`
14100// Converts an RGBA color to an ARGB Hex8 string
14101// Rarely used, but required for "toFilter()"
14102function rgbaToArgbHex(r, g, b, a) {
14103
14104 var hex = [
14105 pad2(convertDecimalToHex(a)),
14106 pad2(mathRound(r).toString(16)),
14107 pad2(mathRound(g).toString(16)),
14108 pad2(mathRound(b).toString(16))
14109 ];
14110
14111 return hex.join("");
14112}
14113
14114// `equals`
14115// Can be called with any tinycolor input
14116tinycolor.equals = function (color1, color2) {
14117 if (!color1 || !color2) { return false; }
14118 return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
14119};
14120
14121tinycolor.random = function() {
14122 return tinycolor.fromRatio({
14123 r: mathRandom(),
14124 g: mathRandom(),
14125 b: mathRandom()
14126 });
14127};
14128
14129
14130// Modification Functions
14131// ----------------------
14132// Thanks to less.js for some of the basics here
14133// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
14134
14135function desaturate(color, amount) {
14136 amount = (amount === 0) ? 0 : (amount || 10);
14137 var hsl = tinycolor(color).toHsl();
14138 hsl.s -= amount / 100;
14139 hsl.s = clamp01(hsl.s);
14140 return tinycolor(hsl);
14141}
14142
14143function saturate(color, amount) {
14144 amount = (amount === 0) ? 0 : (amount || 10);
14145 var hsl = tinycolor(color).toHsl();
14146 hsl.s += amount / 100;
14147 hsl.s = clamp01(hsl.s);
14148 return tinycolor(hsl);
14149}
14150
14151function greyscale(color) {
14152 return tinycolor(color).desaturate(100);
14153}
14154
14155function lighten (color, amount) {
14156 amount = (amount === 0) ? 0 : (amount || 10);
14157 var hsl = tinycolor(color).toHsl();
14158 hsl.l += amount / 100;
14159 hsl.l = clamp01(hsl.l);
14160 return tinycolor(hsl);
14161}
14162
14163function brighten(color, amount) {
14164 amount = (amount === 0) ? 0 : (amount || 10);
14165 var rgb = tinycolor(color).toRgb();
14166 rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
14167 rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
14168 rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
14169 return tinycolor(rgb);
14170}
14171
14172function darken (color, amount) {
14173 amount = (amount === 0) ? 0 : (amount || 10);
14174 var hsl = tinycolor(color).toHsl();
14175 hsl.l -= amount / 100;
14176 hsl.l = clamp01(hsl.l);
14177 return tinycolor(hsl);
14178}
14179
14180// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
14181// Values outside of this range will be wrapped into this range.
14182function spin(color, amount) {
14183 var hsl = tinycolor(color).toHsl();
14184 var hue = (hsl.h + amount) % 360;
14185 hsl.h = hue < 0 ? 360 + hue : hue;
14186 return tinycolor(hsl);
14187}
14188
14189// Combination Functions
14190// ---------------------
14191// Thanks to jQuery xColor for some of the ideas behind these
14192// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
14193
14194function complement(color) {
14195 var hsl = tinycolor(color).toHsl();
14196 hsl.h = (hsl.h + 180) % 360;
14197 return tinycolor(hsl);
14198}
14199
14200function triad(color) {
14201 var hsl = tinycolor(color).toHsl();
14202 var h = hsl.h;
14203 return [
14204 tinycolor(color),
14205 tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
14206 tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
14207 ];
14208}
14209
14210function tetrad(color) {
14211 var hsl = tinycolor(color).toHsl();
14212 var h = hsl.h;
14213 return [
14214 tinycolor(color),
14215 tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
14216 tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
14217 tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
14218 ];
14219}
14220
14221function splitcomplement(color) {
14222 var hsl = tinycolor(color).toHsl();
14223 var h = hsl.h;
14224 return [
14225 tinycolor(color),
14226 tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
14227 tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
14228 ];
14229}
14230
14231function analogous(color, results, slices) {
14232 results = results || 6;
14233 slices = slices || 30;
14234
14235 var hsl = tinycolor(color).toHsl();
14236 var part = 360 / slices;
14237 var ret = [tinycolor(color)];
14238
14239 for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
14240 hsl.h = (hsl.h + part) % 360;
14241 ret.push(tinycolor(hsl));
14242 }
14243 return ret;
14244}
14245
14246function monochromatic(color, results) {
14247 results = results || 6;
14248 var hsv = tinycolor(color).toHsv();
14249 var h = hsv.h, s = hsv.s, v = hsv.v;
14250 var ret = [];
14251 var modification = 1 / results;
14252
14253 while (results--) {
14254 ret.push(tinycolor({ h: h, s: s, v: v}));
14255 v = (v + modification) % 1;
14256 }
14257
14258 return ret;
14259}
14260
14261// Utility Functions
14262// ---------------------
14263
14264tinycolor.mix = function(color1, color2, amount) {
14265 amount = (amount === 0) ? 0 : (amount || 50);
14266
14267 var rgb1 = tinycolor(color1).toRgb();
14268 var rgb2 = tinycolor(color2).toRgb();
14269
14270 var p = amount / 100;
14271
14272 var rgba = {
14273 r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
14274 g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
14275 b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
14276 a: ((rgb2.a - rgb1.a) * p) + rgb1.a
14277 };
14278
14279 return tinycolor(rgba);
14280};
14281
14282
14283// Readability Functions
14284// ---------------------
14285// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
14286
14287// `contrast`
14288// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
14289tinycolor.readability = function(color1, color2) {
14290 var c1 = tinycolor(color1);
14291 var c2 = tinycolor(color2);
14292 return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
14293};
14294
14295// `isReadable`
14296// Ensure that foreground and background color combinations meet WCAG2 guidelines.
14297// The third argument is an optional Object.
14298// the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
14299// the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
14300// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
14301
14302// *Example*
14303// tinycolor.isReadable("#000", "#111") => false
14304// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
14305tinycolor.isReadable = function(color1, color2, wcag2) {
14306 var readability = tinycolor.readability(color1, color2);
14307 var wcag2Parms, out;
14308
14309 out = false;
14310
14311 wcag2Parms = validateWCAG2Parms(wcag2);
14312 switch (wcag2Parms.level + wcag2Parms.size) {
14313 case "AAsmall":
14314 case "AAAlarge":
14315 out = readability >= 4.5;
14316 break;
14317 case "AAlarge":
14318 out = readability >= 3;
14319 break;
14320 case "AAAsmall":
14321 out = readability >= 7;
14322 break;
14323 }
14324 return out;
14325
14326};
14327
14328// `mostReadable`
14329// Given a base color and a list of possible foreground or background
14330// colors for that base, returns the most readable color.
14331// Optionally returns Black or White if the most readable color is unreadable.
14332// *Example*
14333// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
14334// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
14335// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
14336// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
14337tinycolor.mostReadable = function(baseColor, colorList, args) {
14338 var bestColor = null;
14339 var bestScore = 0;
14340 var readability;
14341 var includeFallbackColors, level, size ;
14342 args = args || {};
14343 includeFallbackColors = args.includeFallbackColors ;
14344 level = args.level;
14345 size = args.size;
14346
14347 for (var i= 0; i < colorList.length ; i++) {
14348 readability = tinycolor.readability(baseColor, colorList[i]);
14349 if (readability > bestScore) {
14350 bestScore = readability;
14351 bestColor = tinycolor(colorList[i]);
14352 }
14353 }
14354
14355 if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
14356 return bestColor;
14357 }
14358 else {
14359 args.includeFallbackColors=false;
14360 return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
14361 }
14362};
14363
14364
14365// Big List of Colors
14366// ------------------
14367// <http://www.w3.org/TR/css3-color/#svg-color>
14368var names = tinycolor.names = {
14369 aliceblue: "f0f8ff",
14370 antiquewhite: "faebd7",
14371 aqua: "0ff",
14372 aquamarine: "7fffd4",
14373 azure: "f0ffff",
14374 beige: "f5f5dc",
14375 bisque: "ffe4c4",
14376 black: "000",
14377 blanchedalmond: "ffebcd",
14378 blue: "00f",
14379 blueviolet: "8a2be2",
14380 brown: "a52a2a",
14381 burlywood: "deb887",
14382 burntsienna: "ea7e5d",
14383 cadetblue: "5f9ea0",
14384 chartreuse: "7fff00",
14385 chocolate: "d2691e",
14386 coral: "ff7f50",
14387 cornflowerblue: "6495ed",
14388 cornsilk: "fff8dc",
14389 crimson: "dc143c",
14390 cyan: "0ff",
14391 darkblue: "00008b",
14392 darkcyan: "008b8b",
14393 darkgoldenrod: "b8860b",
14394 darkgray: "a9a9a9",
14395 darkgreen: "006400",
14396 darkgrey: "a9a9a9",
14397 darkkhaki: "bdb76b",
14398 darkmagenta: "8b008b",
14399 darkolivegreen: "556b2f",
14400 darkorange: "ff8c00",
14401 darkorchid: "9932cc",
14402 darkred: "8b0000",
14403 darksalmon: "e9967a",
14404 darkseagreen: "8fbc8f",
14405 darkslateblue: "483d8b",
14406 darkslategray: "2f4f4f",
14407 darkslategrey: "2f4f4f",
14408 darkturquoise: "00ced1",
14409 darkviolet: "9400d3",
14410 deeppink: "ff1493",
14411 deepskyblue: "00bfff",
14412 dimgray: "696969",
14413 dimgrey: "696969",
14414 dodgerblue: "1e90ff",
14415 firebrick: "b22222",
14416 floralwhite: "fffaf0",
14417 forestgreen: "228b22",
14418 fuchsia: "f0f",
14419 gainsboro: "dcdcdc",
14420 ghostwhite: "f8f8ff",
14421 gold: "ffd700",
14422 goldenrod: "daa520",
14423 gray: "808080",
14424 green: "008000",
14425 greenyellow: "adff2f",
14426 grey: "808080",
14427 honeydew: "f0fff0",
14428 hotpink: "ff69b4",
14429 indianred: "cd5c5c",
14430 indigo: "4b0082",
14431 ivory: "fffff0",
14432 khaki: "f0e68c",
14433 lavender: "e6e6fa",
14434 lavenderblush: "fff0f5",
14435 lawngreen: "7cfc00",
14436 lemonchiffon: "fffacd",
14437 lightblue: "add8e6",
14438 lightcoral: "f08080",
14439 lightcyan: "e0ffff",
14440 lightgoldenrodyellow: "fafad2",
14441 lightgray: "d3d3d3",
14442 lightgreen: "90ee90",
14443 lightgrey: "d3d3d3",
14444 lightpink: "ffb6c1",
14445 lightsalmon: "ffa07a",
14446 lightseagreen: "20b2aa",
14447 lightskyblue: "87cefa",
14448 lightslategray: "789",
14449 lightslategrey: "789",
14450 lightsteelblue: "b0c4de",
14451 lightyellow: "ffffe0",
14452 lime: "0f0",
14453 limegreen: "32cd32",
14454 linen: "faf0e6",
14455 magenta: "f0f",
14456 maroon: "800000",
14457 mediumaquamarine: "66cdaa",
14458 mediumblue: "0000cd",
14459 mediumorchid: "ba55d3",
14460 mediumpurple: "9370db",
14461 mediumseagreen: "3cb371",
14462 mediumslateblue: "7b68ee",
14463 mediumspringgreen: "00fa9a",
14464 mediumturquoise: "48d1cc",
14465 mediumvioletred: "c71585",
14466 midnightblue: "191970",
14467 mintcream: "f5fffa",
14468 mistyrose: "ffe4e1",
14469 moccasin: "ffe4b5",
14470 navajowhite: "ffdead",
14471 navy: "000080",
14472 oldlace: "fdf5e6",
14473 olive: "808000",
14474 olivedrab: "6b8e23",
14475 orange: "ffa500",
14476 orangered: "ff4500",
14477 orchid: "da70d6",
14478 palegoldenrod: "eee8aa",
14479 palegreen: "98fb98",
14480 paleturquoise: "afeeee",
14481 palevioletred: "db7093",
14482 papayawhip: "ffefd5",
14483 peachpuff: "ffdab9",
14484 peru: "cd853f",
14485 pink: "ffc0cb",
14486 plum: "dda0dd",
14487 powderblue: "b0e0e6",
14488 purple: "800080",
14489 rebeccapurple: "663399",
14490 red: "f00",
14491 rosybrown: "bc8f8f",
14492 royalblue: "4169e1",
14493 saddlebrown: "8b4513",
14494 salmon: "fa8072",
14495 sandybrown: "f4a460",
14496 seagreen: "2e8b57",
14497 seashell: "fff5ee",
14498 sienna: "a0522d",
14499 silver: "c0c0c0",
14500 skyblue: "87ceeb",
14501 slateblue: "6a5acd",
14502 slategray: "708090",
14503 slategrey: "708090",
14504 snow: "fffafa",
14505 springgreen: "00ff7f",
14506 steelblue: "4682b4",
14507 tan: "d2b48c",
14508 teal: "008080",
14509 thistle: "d8bfd8",
14510 tomato: "ff6347",
14511 turquoise: "40e0d0",
14512 violet: "ee82ee",
14513 wheat: "f5deb3",
14514 white: "fff",
14515 whitesmoke: "f5f5f5",
14516 yellow: "ff0",
14517 yellowgreen: "9acd32"
14518};
14519
14520// Make it easy to access colors via `hexNames[hex]`
14521var hexNames = tinycolor.hexNames = flip(names);
14522
14523
14524// Utilities
14525// ---------
14526
14527// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
14528function flip(o) {
14529 var flipped = { };
14530 for (var i in o) {
14531 if (o.hasOwnProperty(i)) {
14532 flipped[o[i]] = i;
14533 }
14534 }
14535 return flipped;
14536}
14537
14538// Return a valid alpha value [0,1] with all invalid values being set to 1
14539function boundAlpha(a) {
14540 a = parseFloat(a);
14541
14542 if (isNaN(a) || a < 0 || a > 1) {
14543 a = 1;
14544 }
14545
14546 return a;
14547}
14548
14549// Take input from [0, n] and return it as [0, 1]
14550function bound01(n, max) {
14551 if (isOnePointZero(n)) { n = "100%"; }
14552
14553 var processPercent = isPercentage(n);
14554 n = mathMin(max, mathMax(0, parseFloat(n)));
14555
14556 // Automatically convert percentage into number
14557 if (processPercent) {
14558 n = parseInt(n * max, 10) / 100;
14559 }
14560
14561 // Handle floating point rounding errors
14562 if ((Math.abs(n - max) < 0.000001)) {
14563 return 1;
14564 }
14565
14566 // Convert into [0, 1] range if it isn't already
14567 return (n % max) / parseFloat(max);
14568}
14569
14570// Force a number between 0 and 1
14571function clamp01(val) {
14572 return mathMin(1, mathMax(0, val));
14573}
14574
14575// Parse a base-16 hex value into a base-10 integer
14576function parseIntFromHex(val) {
14577 return parseInt(val, 16);
14578}
14579
14580// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
14581// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
14582function isOnePointZero(n) {
14583 return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
14584}
14585
14586// Check to see if string passed in is a percentage
14587function isPercentage(n) {
14588 return typeof n === "string" && n.indexOf('%') != -1;
14589}
14590
14591// Force a hex value to have 2 characters
14592function pad2(c) {
14593 return c.length == 1 ? '0' + c : '' + c;
14594}
14595
14596// Replace a decimal with it's percentage value
14597function convertToPercentage(n) {
14598 if (n <= 1) {
14599 n = (n * 100) + "%";
14600 }
14601
14602 return n;
14603}
14604
14605// Converts a decimal to a hex value
14606function convertDecimalToHex(d) {
14607 return Math.round(parseFloat(d) * 255).toString(16);
14608}
14609// Converts a hex value to a decimal
14610function convertHexToDecimal(h) {
14611 return (parseIntFromHex(h) / 255);
14612}
14613
14614var matchers = (function() {
14615
14616 // <http://www.w3.org/TR/css3-values/#integers>
14617 var CSS_INTEGER = "[-\\+]?\\d+%?";
14618
14619 // <http://www.w3.org/TR/css3-values/#number-value>
14620 var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
14621
14622 // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
14623 var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
14624
14625 // Actual matching.
14626 // Parentheses and commas are optional, but not required.
14627 // Whitespace can take the place of commas or opening paren
14628 var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
14629 var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
14630
14631 return {
14632 CSS_UNIT: new RegExp(CSS_UNIT),
14633 rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
14634 rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
14635 hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
14636 hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
14637 hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
14638 hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
14639 hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
14640 hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
14641 hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
14642 hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
14643 };
14644})();
14645
14646// `isValidCSSUnit`
14647// Take in a single string / number and check to see if it looks like a CSS unit
14648// (see `matchers` above for definition).
14649function isValidCSSUnit(color) {
14650 return !!matchers.CSS_UNIT.exec(color);
14651}
14652
14653// `stringInputToObject`
14654// Permissive string parsing. Take in a number of formats, and output an object
14655// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
14656function stringInputToObject(color) {
14657
14658 color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
14659 var named = false;
14660 if (names[color]) {
14661 color = names[color];
14662 named = true;
14663 }
14664 else if (color == 'transparent') {
14665 return { r: 0, g: 0, b: 0, a: 0, format: "name" };
14666 }
14667
14668 // Try to match string input using regular expressions.
14669 // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
14670 // Just return an object and let the conversion functions handle that.
14671 // This way the result will be the same whether the tinycolor is initialized with string or object.
14672 var match;
14673 if ((match = matchers.rgb.exec(color))) {
14674 return { r: match[1], g: match[2], b: match[3] };
14675 }
14676 if ((match = matchers.rgba.exec(color))) {
14677 return { r: match[1], g: match[2], b: match[3], a: match[4] };
14678 }
14679 if ((match = matchers.hsl.exec(color))) {
14680 return { h: match[1], s: match[2], l: match[3] };
14681 }
14682 if ((match = matchers.hsla.exec(color))) {
14683 return { h: match[1], s: match[2], l: match[3], a: match[4] };
14684 }
14685 if ((match = matchers.hsv.exec(color))) {
14686 return { h: match[1], s: match[2], v: match[3] };
14687 }
14688 if ((match = matchers.hsva.exec(color))) {
14689 return { h: match[1], s: match[2], v: match[3], a: match[4] };
14690 }
14691 if ((match = matchers.hex8.exec(color))) {
14692 return {
14693 r: parseIntFromHex(match[1]),
14694 g: parseIntFromHex(match[2]),
14695 b: parseIntFromHex(match[3]),
14696 a: convertHexToDecimal(match[4]),
14697 format: named ? "name" : "hex8"
14698 };
14699 }
14700 if ((match = matchers.hex6.exec(color))) {
14701 return {
14702 r: parseIntFromHex(match[1]),
14703 g: parseIntFromHex(match[2]),
14704 b: parseIntFromHex(match[3]),
14705 format: named ? "name" : "hex"
14706 };
14707 }
14708 if ((match = matchers.hex4.exec(color))) {
14709 return {
14710 r: parseIntFromHex(match[1] + '' + match[1]),
14711 g: parseIntFromHex(match[2] + '' + match[2]),
14712 b: parseIntFromHex(match[3] + '' + match[3]),
14713 a: convertHexToDecimal(match[4] + '' + match[4]),
14714 format: named ? "name" : "hex8"
14715 };
14716 }
14717 if ((match = matchers.hex3.exec(color))) {
14718 return {
14719 r: parseIntFromHex(match[1] + '' + match[1]),
14720 g: parseIntFromHex(match[2] + '' + match[2]),
14721 b: parseIntFromHex(match[3] + '' + match[3]),
14722 format: named ? "name" : "hex"
14723 };
14724 }
14725
14726 return false;
14727}
14728
14729function validateWCAG2Parms(parms) {
14730 // return valid WCAG2 parms for isReadable.
14731 // If input parms are invalid, return {"level":"AA", "size":"small"}
14732 var level, size;
14733 parms = parms || {"level":"AA", "size":"small"};
14734 level = (parms.level || "AA").toUpperCase();
14735 size = (parms.size || "small").toLowerCase();
14736 if (level !== "AA" && level !== "AAA") {
14737 level = "AA";
14738 }
14739 if (size !== "small" && size !== "large") {
14740 size = "small";
14741 }
14742 return {"level":level, "size":size};
14743}
14744
14745// Node: Export function
14746if (typeof module !== "undefined" && module.exports) {
14747 module.exports = tinycolor;
14748}
14749// AMD/requirejs: Define the module
14750else if (typeof define === 'function' && define.amd) {
14751 define(function () {return tinycolor;});
14752}
14753// Browser: Expose to window
14754else {
14755 window.tinycolor = tinycolor;
14756}
14757
14758})(Math);
14759
14760},{}],36:[function(_dereq_,module,exports){
14761/**
14762* Copyright 2012-2020, Plotly, Inc.
14763* All rights reserved.
14764*
14765* This source code is licensed under the MIT license found in the
14766* LICENSE file in the root directory of this source tree.
14767*/
14768
14769'use strict';
14770
14771/**
14772 * All paths are tuned for maximum scalability of the arrowhead,
14773 * ie throughout arrowwidth=0.3..3 the head is joined smoothly
14774 * to the line, with the line coming from the left and ending at (0, 0).
14775 *
14776 * `backoff` is the distance to move the arrowhead and the end of the line,
14777 * in order that the arrowhead points to the desired place, either at
14778 * the tip of the arrow or (in the case of circle or square)
14779 * the center of the symbol.
14780 *
14781 * `noRotate`, if truthy, says that this arrowhead should not rotate with the
14782 * arrow. That's the case for squares, which should always be straight, and
14783 * circles, for which it's irrelevant.
14784 */
14785
14786module.exports = [
14787 // no arrow
14788 {
14789 path: '',
14790 backoff: 0
14791 },
14792 // wide with flat back
14793 {
14794 path: 'M-2.4,-3V3L0.6,0Z',
14795 backoff: 0.6
14796 },
14797 // narrower with flat back
14798 {
14799 path: 'M-3.7,-2.5V2.5L1.3,0Z',
14800 backoff: 1.3
14801 },
14802 // barbed
14803 {
14804 path: 'M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z',
14805 backoff: 1.55
14806 },
14807 // wide line-drawn
14808 {
14809 path: 'M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z',
14810 backoff: 1.6
14811 },
14812 // narrower line-drawn
14813 {
14814 path: 'M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z',
14815 backoff: 2
14816 },
14817 // circle
14818 {
14819 path: 'M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z',
14820 backoff: 0,
14821 noRotate: true
14822 },
14823 // square
14824 {
14825 path: 'M2,2V-2H-2V2Z',
14826 backoff: 0,
14827 noRotate: true
14828 }
14829];
14830
14831},{}],37:[function(_dereq_,module,exports){
14832/**
14833* Copyright 2012-2020, Plotly, Inc.
14834* All rights reserved.
14835*
14836* This source code is licensed under the MIT license found in the
14837* LICENSE file in the root directory of this source tree.
14838*/
14839
14840'use strict';
14841
14842var ARROWPATHS = _dereq_('./arrow_paths');
14843var fontAttrs = _dereq_('../../plots/font_attributes');
14844var cartesianConstants = _dereq_('../../plots/cartesian/constants');
14845var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
14846
14847
14848module.exports = templatedArray('annotation', {
14849 visible: {
14850 valType: 'boolean',
14851
14852 dflt: true,
14853 editType: 'calc+arraydraw',
14854
14855 },
14856
14857 text: {
14858 valType: 'string',
14859
14860 editType: 'calc+arraydraw',
14861
14862 },
14863 textangle: {
14864 valType: 'angle',
14865 dflt: 0,
14866
14867 editType: 'calc+arraydraw',
14868
14869 },
14870 font: fontAttrs({
14871 editType: 'calc+arraydraw',
14872 colorEditType: 'arraydraw',
14873
14874 }),
14875 width: {
14876 valType: 'number',
14877 min: 1,
14878 dflt: null,
14879
14880 editType: 'calc+arraydraw',
14881
14882 },
14883 height: {
14884 valType: 'number',
14885 min: 1,
14886 dflt: null,
14887
14888 editType: 'calc+arraydraw',
14889
14890 },
14891 opacity: {
14892 valType: 'number',
14893 min: 0,
14894 max: 1,
14895 dflt: 1,
14896
14897 editType: 'arraydraw',
14898
14899 },
14900 align: {
14901 valType: 'enumerated',
14902 values: ['left', 'center', 'right'],
14903 dflt: 'center',
14904
14905 editType: 'arraydraw',
14906
14907 },
14908 valign: {
14909 valType: 'enumerated',
14910 values: ['top', 'middle', 'bottom'],
14911 dflt: 'middle',
14912
14913 editType: 'arraydraw',
14914
14915 },
14916 bgcolor: {
14917 valType: 'color',
14918 dflt: 'rgba(0,0,0,0)',
14919
14920 editType: 'arraydraw',
14921
14922 },
14923 bordercolor: {
14924 valType: 'color',
14925 dflt: 'rgba(0,0,0,0)',
14926
14927 editType: 'arraydraw',
14928
14929 },
14930 borderpad: {
14931 valType: 'number',
14932 min: 0,
14933 dflt: 1,
14934
14935 editType: 'calc+arraydraw',
14936
14937 },
14938 borderwidth: {
14939 valType: 'number',
14940 min: 0,
14941 dflt: 1,
14942
14943 editType: 'calc+arraydraw',
14944
14945 },
14946 // arrow
14947 showarrow: {
14948 valType: 'boolean',
14949 dflt: true,
14950
14951 editType: 'calc+arraydraw',
14952
14953 },
14954 arrowcolor: {
14955 valType: 'color',
14956
14957 editType: 'arraydraw',
14958
14959 },
14960 arrowhead: {
14961 valType: 'integer',
14962 min: 0,
14963 max: ARROWPATHS.length,
14964 dflt: 1,
14965
14966 editType: 'arraydraw',
14967
14968 },
14969 startarrowhead: {
14970 valType: 'integer',
14971 min: 0,
14972 max: ARROWPATHS.length,
14973 dflt: 1,
14974
14975 editType: 'arraydraw',
14976
14977 },
14978 arrowside: {
14979 valType: 'flaglist',
14980 flags: ['end', 'start'],
14981 extras: ['none'],
14982 dflt: 'end',
14983
14984 editType: 'arraydraw',
14985
14986 },
14987 arrowsize: {
14988 valType: 'number',
14989 min: 0.3,
14990 dflt: 1,
14991
14992 editType: 'calc+arraydraw',
14993
14994 },
14995 startarrowsize: {
14996 valType: 'number',
14997 min: 0.3,
14998 dflt: 1,
14999
15000 editType: 'calc+arraydraw',
15001
15002 },
15003 arrowwidth: {
15004 valType: 'number',
15005 min: 0.1,
15006
15007 editType: 'calc+arraydraw',
15008
15009 },
15010 standoff: {
15011 valType: 'number',
15012 min: 0,
15013 dflt: 0,
15014
15015 editType: 'calc+arraydraw',
15016
15017 },
15018 startstandoff: {
15019 valType: 'number',
15020 min: 0,
15021 dflt: 0,
15022
15023 editType: 'calc+arraydraw',
15024
15025 },
15026 ax: {
15027 valType: 'any',
15028
15029 editType: 'calc+arraydraw',
15030
15031 },
15032 ay: {
15033 valType: 'any',
15034
15035 editType: 'calc+arraydraw',
15036
15037 },
15038 axref: {
15039 valType: 'enumerated',
15040 dflt: 'pixel',
15041 values: [
15042 'pixel',
15043 cartesianConstants.idRegex.x.toString()
15044 ],
15045
15046 editType: 'calc',
15047
15048 },
15049 ayref: {
15050 valType: 'enumerated',
15051 dflt: 'pixel',
15052 values: [
15053 'pixel',
15054 cartesianConstants.idRegex.y.toString()
15055 ],
15056
15057 editType: 'calc',
15058
15059 },
15060 // positioning
15061 xref: {
15062 valType: 'enumerated',
15063 values: [
15064 'paper',
15065 cartesianConstants.idRegex.x.toString()
15066 ],
15067
15068 editType: 'calc',
15069
15070 },
15071 x: {
15072 valType: 'any',
15073
15074 editType: 'calc+arraydraw',
15075
15076 },
15077 xanchor: {
15078 valType: 'enumerated',
15079 values: ['auto', 'left', 'center', 'right'],
15080 dflt: 'auto',
15081
15082 editType: 'calc+arraydraw',
15083
15084 },
15085 xshift: {
15086 valType: 'number',
15087 dflt: 0,
15088
15089 editType: 'calc+arraydraw',
15090
15091 },
15092 yref: {
15093 valType: 'enumerated',
15094 values: [
15095 'paper',
15096 cartesianConstants.idRegex.y.toString()
15097 ],
15098
15099 editType: 'calc',
15100
15101 },
15102 y: {
15103 valType: 'any',
15104
15105 editType: 'calc+arraydraw',
15106
15107 },
15108 yanchor: {
15109 valType: 'enumerated',
15110 values: ['auto', 'top', 'middle', 'bottom'],
15111 dflt: 'auto',
15112
15113 editType: 'calc+arraydraw',
15114
15115 },
15116 yshift: {
15117 valType: 'number',
15118 dflt: 0,
15119
15120 editType: 'calc+arraydraw',
15121
15122 },
15123 clicktoshow: {
15124 valType: 'enumerated',
15125 values: [false, 'onoff', 'onout'],
15126 dflt: false,
15127
15128 editType: 'arraydraw',
15129
15130 },
15131 xclick: {
15132 valType: 'any',
15133
15134 editType: 'arraydraw',
15135
15136 },
15137 yclick: {
15138 valType: 'any',
15139
15140 editType: 'arraydraw',
15141
15142 },
15143 hovertext: {
15144 valType: 'string',
15145
15146 editType: 'arraydraw',
15147
15148 },
15149 hoverlabel: {
15150 bgcolor: {
15151 valType: 'color',
15152
15153 editType: 'arraydraw',
15154
15155 },
15156 bordercolor: {
15157 valType: 'color',
15158
15159 editType: 'arraydraw',
15160
15161 },
15162 font: fontAttrs({
15163 editType: 'arraydraw',
15164
15165 }),
15166 editType: 'arraydraw'
15167 },
15168 captureevents: {
15169 valType: 'boolean',
15170
15171 editType: 'arraydraw',
15172
15173 },
15174 editType: 'calc',
15175
15176 _deprecated: {
15177 ref: {
15178 valType: 'string',
15179
15180 editType: 'calc',
15181
15182 }
15183 }
15184});
15185
15186},{"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228,"../../plots/font_attributes":250,"./arrow_paths":36}],38:[function(_dereq_,module,exports){
15187/**
15188* Copyright 2012-2020, Plotly, Inc.
15189* All rights reserved.
15190*
15191* This source code is licensed under the MIT license found in the
15192* LICENSE file in the root directory of this source tree.
15193*/
15194
15195
15196'use strict';
15197
15198var Lib = _dereq_('../../lib');
15199var Axes = _dereq_('../../plots/cartesian/axes');
15200
15201var draw = _dereq_('./draw').draw;
15202
15203
15204module.exports = function calcAutorange(gd) {
15205 var fullLayout = gd._fullLayout;
15206 var annotationList = Lib.filterVisible(fullLayout.annotations);
15207
15208 if(annotationList.length && gd._fullData.length) {
15209 return Lib.syncOrAsync([draw, annAutorange], gd);
15210 }
15211};
15212
15213function annAutorange(gd) {
15214 var fullLayout = gd._fullLayout;
15215
15216 // find the bounding boxes for each of these annotations'
15217 // relative to their anchor points
15218 // use the arrow and the text bg rectangle,
15219 // as the whole anno may include hidden text in its bbox
15220 Lib.filterVisible(fullLayout.annotations).forEach(function(ann) {
15221 var xa = Axes.getFromId(gd, ann.xref);
15222 var ya = Axes.getFromId(gd, ann.yref);
15223
15224 ann._extremes = {};
15225 if(xa) calcAxisExpansion(ann, xa);
15226 if(ya) calcAxisExpansion(ann, ya);
15227 });
15228}
15229
15230function calcAxisExpansion(ann, ax) {
15231 var axId = ax._id;
15232 var letter = axId.charAt(0);
15233 var pos = ann[letter];
15234 var apos = ann['a' + letter];
15235 var ref = ann[letter + 'ref'];
15236 var aref = ann['a' + letter + 'ref'];
15237 var padplus = ann['_' + letter + 'padplus'];
15238 var padminus = ann['_' + letter + 'padminus'];
15239 var shift = {x: 1, y: -1}[letter] * ann[letter + 'shift'];
15240 var headSize = 3 * ann.arrowsize * ann.arrowwidth || 0;
15241 var headPlus = headSize + shift;
15242 var headMinus = headSize - shift;
15243 var startHeadSize = 3 * ann.startarrowsize * ann.arrowwidth || 0;
15244 var startHeadPlus = startHeadSize + shift;
15245 var startHeadMinus = startHeadSize - shift;
15246 var extremes;
15247
15248 if(aref === ref) {
15249 // expand for the arrowhead (padded by arrowhead)
15250 var extremeArrowHead = Axes.findExtremes(ax, [ax.r2c(pos)], {
15251 ppadplus: headPlus,
15252 ppadminus: headMinus
15253 });
15254 // again for the textbox (padded by textbox)
15255 var extremeText = Axes.findExtremes(ax, [ax.r2c(apos)], {
15256 ppadplus: Math.max(padplus, startHeadPlus),
15257 ppadminus: Math.max(padminus, startHeadMinus)
15258 });
15259 extremes = {
15260 min: [extremeArrowHead.min[0], extremeText.min[0]],
15261 max: [extremeArrowHead.max[0], extremeText.max[0]]
15262 };
15263 } else {
15264 startHeadPlus = apos ? startHeadPlus + apos : startHeadPlus;
15265 startHeadMinus = apos ? startHeadMinus - apos : startHeadMinus;
15266 extremes = Axes.findExtremes(ax, [ax.r2c(pos)], {
15267 ppadplus: Math.max(padplus, headPlus, startHeadPlus),
15268 ppadminus: Math.max(padminus, headMinus, startHeadMinus)
15269 });
15270 }
15271
15272 ann._extremes[axId] = extremes;
15273}
15274
15275},{"../../lib":178,"../../plots/cartesian/axes":222,"./draw":43}],39:[function(_dereq_,module,exports){
15276/**
15277* Copyright 2012-2020, Plotly, Inc.
15278* All rights reserved.
15279*
15280* This source code is licensed under the MIT license found in the
15281* LICENSE file in the root directory of this source tree.
15282*/
15283
15284'use strict';
15285
15286var Lib = _dereq_('../../lib');
15287var Registry = _dereq_('../../registry');
15288var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
15289
15290module.exports = {
15291 hasClickToShow: hasClickToShow,
15292 onClick: onClick
15293};
15294
15295/*
15296 * hasClickToShow: does the given hoverData have ANY annotations which will
15297 * turn ON if we click here? (used by hover events to set cursor)
15298 *
15299 * gd: graphDiv
15300 * hoverData: a hoverData array, as included with the *plotly_hover* or
15301 * *plotly_click* events in the `points` attribute
15302 *
15303 * returns: boolean
15304 */
15305function hasClickToShow(gd, hoverData) {
15306 var sets = getToggleSets(gd, hoverData);
15307 return sets.on.length > 0 || sets.explicitOff.length > 0;
15308}
15309
15310/*
15311 * onClick: perform the toggling (via Plotly.update) implied by clicking
15312 * at this hoverData
15313 *
15314 * gd: graphDiv
15315 * hoverData: a hoverData array, as included with the *plotly_hover* or
15316 * *plotly_click* events in the `points` attribute
15317 *
15318 * returns: Promise that the update is complete
15319 */
15320function onClick(gd, hoverData) {
15321 var toggleSets = getToggleSets(gd, hoverData);
15322 var onSet = toggleSets.on;
15323 var offSet = toggleSets.off.concat(toggleSets.explicitOff);
15324 var update = {};
15325 var annotationsOut = gd._fullLayout.annotations;
15326 var i, editHelpers;
15327
15328 if(!(onSet.length || offSet.length)) return;
15329
15330 for(i = 0; i < onSet.length; i++) {
15331 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[onSet[i]]);
15332 editHelpers.modifyItem('visible', true);
15333 Lib.extendFlat(update, editHelpers.getUpdateObj());
15334 }
15335
15336 for(i = 0; i < offSet.length; i++) {
15337 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[offSet[i]]);
15338 editHelpers.modifyItem('visible', false);
15339 Lib.extendFlat(update, editHelpers.getUpdateObj());
15340 }
15341
15342 return Registry.call('update', gd, {}, update);
15343}
15344
15345/*
15346 * getToggleSets: find the annotations which will turn on or off at this
15347 * hoverData
15348 *
15349 * gd: graphDiv
15350 * hoverData: a hoverData array, as included with the *plotly_hover* or
15351 * *plotly_click* events in the `points` attribute
15352 *
15353 * returns: {
15354 * on: Array (indices of annotations to turn on),
15355 * off: Array (indices to turn off because you're not hovering on them),
15356 * explicitOff: Array (indices to turn off because you *are* hovering on them)
15357 * }
15358 */
15359function getToggleSets(gd, hoverData) {
15360 var annotations = gd._fullLayout.annotations;
15361 var onSet = [];
15362 var offSet = [];
15363 var explicitOffSet = [];
15364 var hoverLen = (hoverData || []).length;
15365
15366 var i, j, anni, showMode, pointj, xa, ya, toggleType;
15367
15368 for(i = 0; i < annotations.length; i++) {
15369 anni = annotations[i];
15370 showMode = anni.clicktoshow;
15371
15372 if(showMode) {
15373 for(j = 0; j < hoverLen; j++) {
15374 pointj = hoverData[j];
15375 xa = pointj.xaxis;
15376 ya = pointj.yaxis;
15377
15378 if(xa._id === anni.xref &&
15379 ya._id === anni.yref &&
15380 xa.d2r(pointj.x) === clickData2r(anni._xclick, xa) &&
15381 ya.d2r(pointj.y) === clickData2r(anni._yclick, ya)
15382 ) {
15383 // match! toggle this annotation
15384 // regardless of its clicktoshow mode
15385 // but if it's onout mode, off is implicit
15386 if(anni.visible) {
15387 if(showMode === 'onout') toggleType = offSet;
15388 else toggleType = explicitOffSet;
15389 } else {
15390 toggleType = onSet;
15391 }
15392 toggleType.push(i);
15393 break;
15394 }
15395 }
15396
15397 if(j === hoverLen) {
15398 // no match - only turn this annotation OFF, and only if
15399 // showmode is 'onout'
15400 if(anni.visible && showMode === 'onout') offSet.push(i);
15401 }
15402 }
15403 }
15404
15405 return {on: onSet, off: offSet, explicitOff: explicitOffSet};
15406}
15407
15408// to handle log axes until v2
15409function clickData2r(d, ax) {
15410 return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d);
15411}
15412
15413},{"../../lib":178,"../../plot_api/plot_template":212,"../../registry":269}],40:[function(_dereq_,module,exports){
15414/**
15415* Copyright 2012-2020, Plotly, Inc.
15416* All rights reserved.
15417*
15418* This source code is licensed under the MIT license found in the
15419* LICENSE file in the root directory of this source tree.
15420*/
15421
15422'use strict';
15423
15424var Lib = _dereq_('../../lib');
15425var Color = _dereq_('../color');
15426
15427// defaults common to 'annotations' and 'annotations3d'
15428module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce) {
15429 coerce('opacity');
15430 var bgColor = coerce('bgcolor');
15431
15432 var borderColor = coerce('bordercolor');
15433 var borderOpacity = Color.opacity(borderColor);
15434
15435 coerce('borderpad');
15436
15437 var borderWidth = coerce('borderwidth');
15438 var showArrow = coerce('showarrow');
15439
15440 coerce('text', showArrow ? ' ' : fullLayout._dfltTitle.annotation);
15441 coerce('textangle');
15442 Lib.coerceFont(coerce, 'font', fullLayout.font);
15443
15444 coerce('width');
15445 coerce('align');
15446
15447 var h = coerce('height');
15448 if(h) coerce('valign');
15449
15450 if(showArrow) {
15451 var arrowside = coerce('arrowside');
15452 var arrowhead;
15453 var arrowsize;
15454
15455 if(arrowside.indexOf('end') !== -1) {
15456 arrowhead = coerce('arrowhead');
15457 arrowsize = coerce('arrowsize');
15458 }
15459
15460 if(arrowside.indexOf('start') !== -1) {
15461 coerce('startarrowhead', arrowhead);
15462 coerce('startarrowsize', arrowsize);
15463 }
15464 coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
15465 coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
15466 coerce('standoff');
15467 coerce('startstandoff');
15468 }
15469
15470 var hoverText = coerce('hovertext');
15471 var globalHoverLabel = fullLayout.hoverlabel || {};
15472
15473 if(hoverText) {
15474 var hoverBG = coerce('hoverlabel.bgcolor', globalHoverLabel.bgcolor ||
15475 (Color.opacity(bgColor) ? Color.rgb(bgColor) : Color.defaultLine)
15476 );
15477
15478 var hoverBorder = coerce('hoverlabel.bordercolor', globalHoverLabel.bordercolor ||
15479 Color.contrast(hoverBG)
15480 );
15481
15482 Lib.coerceFont(coerce, 'hoverlabel.font', {
15483 family: globalHoverLabel.font.family,
15484 size: globalHoverLabel.font.size,
15485 color: globalHoverLabel.font.color || hoverBorder
15486 });
15487 }
15488
15489 coerce('captureevents', !!hoverText);
15490};
15491
15492},{"../../lib":178,"../color":52}],41:[function(_dereq_,module,exports){
15493/**
15494* Copyright 2012-2020, Plotly, Inc.
15495* All rights reserved.
15496*
15497* This source code is licensed under the MIT license found in the
15498* LICENSE file in the root directory of this source tree.
15499*/
15500
15501
15502'use strict';
15503
15504var isNumeric = _dereq_('fast-isnumeric');
15505var toLogRange = _dereq_('../../lib/to_log_range');
15506
15507/*
15508 * convertCoords: when converting an axis between log and linear
15509 * you need to alter any annotations on that axis to keep them
15510 * pointing at the same data point.
15511 * In v2.0 this will become obsolete
15512 *
15513 * gd: the plot div
15514 * ax: the axis being changed
15515 * newType: the type it's getting
15516 * doExtra: function(attr, val) from inside relayout that sets the attribute.
15517 * Use this to make the changes as it's aware if any other changes in the
15518 * same relayout call should override this conversion.
15519 */
15520module.exports = function convertCoords(gd, ax, newType, doExtra) {
15521 ax = ax || {};
15522
15523 var toLog = (newType === 'log') && (ax.type === 'linear');
15524 var fromLog = (newType === 'linear') && (ax.type === 'log');
15525
15526 if(!(toLog || fromLog)) return;
15527
15528 var annotations = gd._fullLayout.annotations;
15529 var axLetter = ax._id.charAt(0);
15530 var ann;
15531 var attrPrefix;
15532
15533 function convert(attr) {
15534 var currentVal = ann[attr];
15535 var newVal = null;
15536
15537 if(toLog) newVal = toLogRange(currentVal, ax.range);
15538 else newVal = Math.pow(10, currentVal);
15539
15540 // if conversion failed, delete the value so it gets a default value
15541 if(!isNumeric(newVal)) newVal = null;
15542
15543 doExtra(attrPrefix + attr, newVal);
15544 }
15545
15546 for(var i = 0; i < annotations.length; i++) {
15547 ann = annotations[i];
15548 attrPrefix = 'annotations[' + i + '].';
15549
15550 if(ann[axLetter + 'ref'] === ax._id) convert(axLetter);
15551 if(ann['a' + axLetter + 'ref'] === ax._id) convert('a' + axLetter);
15552 }
15553};
15554
15555},{"../../lib/to_log_range":201,"fast-isnumeric":18}],42:[function(_dereq_,module,exports){
15556/**
15557* Copyright 2012-2020, Plotly, Inc.
15558* All rights reserved.
15559*
15560* This source code is licensed under the MIT license found in the
15561* LICENSE file in the root directory of this source tree.
15562*/
15563
15564
15565'use strict';
15566
15567var Lib = _dereq_('../../lib');
15568var Axes = _dereq_('../../plots/cartesian/axes');
15569var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
15570
15571var handleAnnotationCommonDefaults = _dereq_('./common_defaults');
15572var attributes = _dereq_('./attributes');
15573
15574
15575module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
15576 handleArrayContainerDefaults(layoutIn, layoutOut, {
15577 name: 'annotations',
15578 handleItemDefaults: handleAnnotationDefaults
15579 });
15580};
15581
15582function handleAnnotationDefaults(annIn, annOut, fullLayout) {
15583 function coerce(attr, dflt) {
15584 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
15585 }
15586
15587 var visible = coerce('visible');
15588 var clickToShow = coerce('clicktoshow');
15589
15590 if(!(visible || clickToShow)) return;
15591
15592 handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce);
15593
15594 var showArrow = annOut.showarrow;
15595
15596 // positioning
15597 var axLetters = ['x', 'y'];
15598 var arrowPosDflt = [-10, -30];
15599 var gdMock = {_fullLayout: fullLayout};
15600
15601 for(var i = 0; i < 2; i++) {
15602 var axLetter = axLetters[i];
15603
15604 // xref, yref
15605 var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
15606
15607 if(axRef !== 'paper') {
15608 var ax = Axes.getFromId(gdMock, axRef);
15609 ax._annIndices.push(annOut._index);
15610 }
15611
15612 // x, y
15613 Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
15614
15615 if(showArrow) {
15616 var arrowPosAttr = 'a' + axLetter;
15617 // axref, ayref
15618 var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
15619
15620 // for now the arrow can only be on the same axis or specified as pixels
15621 // TODO: sometime it might be interesting to allow it to be on *any* axis
15622 // but that would require updates to drawing & autorange code and maybe more
15623 if(aaxRef !== 'pixel' && aaxRef !== axRef) {
15624 aaxRef = annOut[arrowPosAttr] = 'pixel';
15625 }
15626
15627 // ax, ay
15628 var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4;
15629 Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt);
15630 }
15631
15632 // xanchor, yanchor
15633 coerce(axLetter + 'anchor');
15634
15635 // xshift, yshift
15636 coerce(axLetter + 'shift');
15637 }
15638
15639 // if you have one coordinate you should have both
15640 Lib.noneOrAll(annIn, annOut, ['x', 'y']);
15641
15642 // if you have one part of arrow length you should have both
15643 if(showArrow) {
15644 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
15645 }
15646
15647 if(clickToShow) {
15648 var xClick = coerce('xclick');
15649 var yClick = coerce('yclick');
15650
15651 // put the actual click data to bind to into private attributes
15652 // so we don't have to do this little bit of logic on every hover event
15653 annOut._xclick = (xClick === undefined) ?
15654 annOut.x :
15655 Axes.cleanPosition(xClick, gdMock, annOut.xref);
15656 annOut._yclick = (yClick === undefined) ?
15657 annOut.y :
15658 Axes.cleanPosition(yClick, gdMock, annOut.yref);
15659 }
15660}
15661
15662},{"../../lib":178,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":37,"./common_defaults":40}],43:[function(_dereq_,module,exports){
15663/**
15664* Copyright 2012-2020, Plotly, Inc.
15665* All rights reserved.
15666*
15667* This source code is licensed under the MIT license found in the
15668* LICENSE file in the root directory of this source tree.
15669*/
15670
15671'use strict';
15672
15673var d3 = _dereq_('d3');
15674
15675var Registry = _dereq_('../../registry');
15676var Plots = _dereq_('../../plots/plots');
15677var Lib = _dereq_('../../lib');
15678var Axes = _dereq_('../../plots/cartesian/axes');
15679var Color = _dereq_('../color');
15680var Drawing = _dereq_('../drawing');
15681var Fx = _dereq_('../fx');
15682var svgTextUtils = _dereq_('../../lib/svg_text_utils');
15683var setCursor = _dereq_('../../lib/setcursor');
15684var dragElement = _dereq_('../dragelement');
15685var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
15686
15687var drawArrowHead = _dereq_('./draw_arrow_head');
15688
15689// Annotations are stored in gd.layout.annotations, an array of objects
15690// index can point to one item in this array,
15691// or non-numeric to simply add a new one
15692// or -1 to modify all existing
15693// opt can be the full options object, or one key (to be set to value)
15694// or undefined to simply redraw
15695// if opt is blank, val can be 'add' or a full options object to add a new
15696// annotation at that point in the array, or 'remove' to delete this one
15697
15698module.exports = {
15699 draw: draw,
15700 drawOne: drawOne,
15701 drawRaw: drawRaw
15702};
15703
15704/*
15705 * draw: draw all annotations without any new modifications
15706 */
15707function draw(gd) {
15708 var fullLayout = gd._fullLayout;
15709
15710 fullLayout._infolayer.selectAll('.annotation').remove();
15711
15712 for(var i = 0; i < fullLayout.annotations.length; i++) {
15713 if(fullLayout.annotations[i].visible) {
15714 drawOne(gd, i);
15715 }
15716 }
15717
15718 return Plots.previousPromises(gd);
15719}
15720
15721/*
15722 * drawOne: draw a single cartesian or paper-ref annotation, potentially with modifications
15723 *
15724 * index (int): the annotation to draw
15725 */
15726function drawOne(gd, index) {
15727 var fullLayout = gd._fullLayout;
15728 var options = fullLayout.annotations[index] || {};
15729 var xa = Axes.getFromId(gd, options.xref);
15730 var ya = Axes.getFromId(gd, options.yref);
15731
15732 if(xa) xa.setScale();
15733 if(ya) ya.setScale();
15734
15735 drawRaw(gd, options, index, false, xa, ya);
15736}
15737
15738/**
15739 * drawRaw: draw a single annotation, potentially with modifications
15740 *
15741 * @param {DOM element} gd
15742 * @param {object} options : this annotation's fullLayout options
15743 * @param {integer} index : index in 'annotations' container of the annotation to draw
15744 * @param {string} subplotId : id of the annotation's subplot
15745 * - use false for 2d (i.e. cartesian or paper-ref) annotations
15746 * @param {object | undefined} xa : full x-axis object to compute subplot pos-to-px
15747 * @param {object | undefined} ya : ... y-axis
15748 */
15749function drawRaw(gd, options, index, subplotId, xa, ya) {
15750 var fullLayout = gd._fullLayout;
15751 var gs = gd._fullLayout._size;
15752 var edits = gd._context.edits;
15753
15754 var className, containerStr;
15755
15756 if(subplotId) {
15757 className = 'annotation-' + subplotId;
15758 containerStr = subplotId + '.annotations';
15759 } else {
15760 className = 'annotation';
15761 containerStr = 'annotations';
15762 }
15763
15764 var editHelpers = arrayEditor(gd.layout, containerStr, options);
15765 var modifyBase = editHelpers.modifyBase;
15766 var modifyItem = editHelpers.modifyItem;
15767 var getUpdateObj = editHelpers.getUpdateObj;
15768
15769 // remove the existing annotation if there is one
15770 fullLayout._infolayer
15771 .selectAll('.' + className + '[data-index="' + index + '"]')
15772 .remove();
15773
15774 var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
15775
15776 // this annotation is gone - quit now after deleting it
15777 // TODO: use d3 idioms instead of deleting and redrawing every time
15778 if(!options._input || options.visible === false) {
15779 d3.selectAll('#' + annClipID).remove();
15780 return;
15781 }
15782
15783 // calculated pixel positions
15784 // x & y each will get text, head, and tail as appropriate
15785 var annPosPx = {x: {}, y: {}};
15786 var textangle = +options.textangle || 0;
15787
15788 // create the components
15789 // made a single group to contain all, so opacity can work right
15790 // with border/arrow together this could handle a whole bunch of
15791 // cleanup at this point, but works for now
15792 var annGroup = fullLayout._infolayer.append('g')
15793 .classed(className, true)
15794 .attr('data-index', String(index))
15795 .style('opacity', options.opacity);
15796
15797 // another group for text+background so that they can rotate together
15798 var annTextGroup = annGroup.append('g')
15799 .classed('annotation-text-g', true);
15800
15801 var editTextPosition = edits[options.showarrow ? 'annotationTail' : 'annotationPosition'];
15802 var textEvents = options.captureevents || edits.annotationText || editTextPosition;
15803
15804 function makeEventData(initialEvent) {
15805 var eventData = {
15806 index: index,
15807 annotation: options._input,
15808 fullAnnotation: options,
15809 event: initialEvent
15810 };
15811 if(subplotId) {
15812 eventData.subplotId = subplotId;
15813 }
15814 return eventData;
15815 }
15816
15817 var annTextGroupInner = annTextGroup.append('g')
15818 .style('pointer-events', textEvents ? 'all' : null)
15819 .call(setCursor, 'pointer')
15820 .on('click', function() {
15821 gd._dragging = false;
15822 gd.emit('plotly_clickannotation', makeEventData(d3.event));
15823 });
15824
15825 if(options.hovertext) {
15826 annTextGroupInner
15827 .on('mouseover', function() {
15828 var hoverOptions = options.hoverlabel;
15829 var hoverFont = hoverOptions.font;
15830 var bBox = this.getBoundingClientRect();
15831 var bBoxRef = gd.getBoundingClientRect();
15832
15833 Fx.loneHover({
15834 x0: bBox.left - bBoxRef.left,
15835 x1: bBox.right - bBoxRef.left,
15836 y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
15837 text: options.hovertext,
15838 color: hoverOptions.bgcolor,
15839 borderColor: hoverOptions.bordercolor,
15840 fontFamily: hoverFont.family,
15841 fontSize: hoverFont.size,
15842 fontColor: hoverFont.color
15843 }, {
15844 container: fullLayout._hoverlayer.node(),
15845 outerContainer: fullLayout._paper.node(),
15846 gd: gd
15847 });
15848 })
15849 .on('mouseout', function() {
15850 Fx.loneUnhover(fullLayout._hoverlayer.node());
15851 });
15852 }
15853
15854 var borderwidth = options.borderwidth;
15855 var borderpad = options.borderpad;
15856 var borderfull = borderwidth + borderpad;
15857
15858 var annTextBG = annTextGroupInner.append('rect')
15859 .attr('class', 'bg')
15860 .style('stroke-width', borderwidth + 'px')
15861 .call(Color.stroke, options.bordercolor)
15862 .call(Color.fill, options.bgcolor);
15863
15864 var isSizeConstrained = options.width || options.height;
15865
15866 var annTextClip = fullLayout._topclips
15867 .selectAll('#' + annClipID)
15868 .data(isSizeConstrained ? [0] : []);
15869
15870 annTextClip.enter().append('clipPath')
15871 .classed('annclip', true)
15872 .attr('id', annClipID)
15873 .append('rect');
15874 annTextClip.exit().remove();
15875
15876 var font = options.font;
15877
15878 var text = fullLayout._meta ?
15879 Lib.templateString(options.text, fullLayout._meta) :
15880 options.text;
15881
15882 var annText = annTextGroupInner.append('text')
15883 .classed('annotation-text', true)
15884 .text(text);
15885
15886 function textLayout(s) {
15887 s.call(Drawing.font, font)
15888 .attr({
15889 'text-anchor': {
15890 left: 'start',
15891 right: 'end'
15892 }[options.align] || 'middle'
15893 });
15894
15895 svgTextUtils.convertToTspans(s, gd, drawGraphicalElements);
15896 return s;
15897 }
15898
15899 function drawGraphicalElements() {
15900 // if the text has *only* a link, make the whole box into a link
15901 var anchor3 = annText.selectAll('a');
15902 if(anchor3.size() === 1 && anchor3.text() === annText.text()) {
15903 var wholeLink = annTextGroupInner.insert('a', ':first-child').attr({
15904 'xlink:xlink:href': anchor3.attr('xlink:href'),
15905 'xlink:xlink:show': anchor3.attr('xlink:show')
15906 })
15907 .style({cursor: 'pointer'});
15908
15909 wholeLink.node().appendChild(annTextBG.node());
15910 }
15911
15912 var mathjaxGroup = annTextGroupInner.select('.annotation-text-math-group');
15913 var hasMathjax = !mathjaxGroup.empty();
15914 var anntextBB = Drawing.bBox(
15915 (hasMathjax ? mathjaxGroup : annText).node());
15916 var textWidth = anntextBB.width;
15917 var textHeight = anntextBB.height;
15918 var annWidth = options.width || textWidth;
15919 var annHeight = options.height || textHeight;
15920 var outerWidth = Math.round(annWidth + 2 * borderfull);
15921 var outerHeight = Math.round(annHeight + 2 * borderfull);
15922
15923 function shiftFraction(v, anchor) {
15924 if(anchor === 'auto') {
15925 if(v < 1 / 3) anchor = 'left';
15926 else if(v > 2 / 3) anchor = 'right';
15927 else anchor = 'center';
15928 }
15929 return {
15930 center: 0,
15931 middle: 0,
15932 left: 0.5,
15933 bottom: -0.5,
15934 right: -0.5,
15935 top: 0.5
15936 }[anchor];
15937 }
15938
15939 var annotationIsOffscreen = false;
15940 var letters = ['x', 'y'];
15941
15942 for(var i = 0; i < letters.length; i++) {
15943 var axLetter = letters[i];
15944 var axRef = options[axLetter + 'ref'] || axLetter;
15945 var tailRef = options['a' + axLetter + 'ref'];
15946 var ax = {x: xa, y: ya}[axLetter];
15947 var dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180;
15948 // note that these two can be either positive or negative
15949 var annSizeFromWidth = outerWidth * Math.cos(dimAngle);
15950 var annSizeFromHeight = outerHeight * Math.sin(dimAngle);
15951 // but this one is the positive total size
15952 var annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight);
15953 var anchor = options[axLetter + 'anchor'];
15954 var overallShift = options[axLetter + 'shift'] * (axLetter === 'x' ? 1 : -1);
15955 var posPx = annPosPx[axLetter];
15956 var basePx;
15957 var textPadShift;
15958 var alignPosition;
15959 var autoAlignFraction;
15960 var textShift;
15961
15962 /*
15963 * calculate the *primary* pixel position
15964 * which is the arrowhead if there is one,
15965 * otherwise the text anchor point
15966 */
15967 if(ax) {
15968 // check if annotation is off screen, to bypass DOM manipulations
15969 var posFraction = ax.r2fraction(options[axLetter]);
15970 if(posFraction < 0 || posFraction > 1) {
15971 if(tailRef === axRef) {
15972 posFraction = ax.r2fraction(options['a' + axLetter]);
15973 if(posFraction < 0 || posFraction > 1) {
15974 annotationIsOffscreen = true;
15975 }
15976 } else {
15977 annotationIsOffscreen = true;
15978 }
15979 }
15980 basePx = ax._offset + ax.r2p(options[axLetter]);
15981 autoAlignFraction = 0.5;
15982 } else {
15983 if(axLetter === 'x') {
15984 alignPosition = options[axLetter];
15985 basePx = gs.l + gs.w * alignPosition;
15986 } else {
15987 alignPosition = 1 - options[axLetter];
15988 basePx = gs.t + gs.h * alignPosition;
15989 }
15990 autoAlignFraction = options.showarrow ? 0.5 : alignPosition;
15991 }
15992
15993 // now translate this into pixel positions of head, tail, and text
15994 // as well as paddings for autorange
15995 if(options.showarrow) {
15996 posPx.head = basePx;
15997
15998 var arrowLength = options['a' + axLetter];
15999
16000 // with an arrow, the text rotates around the anchor point
16001 textShift = annSizeFromWidth * shiftFraction(0.5, options.xanchor) -
16002 annSizeFromHeight * shiftFraction(0.5, options.yanchor);
16003
16004 if(tailRef === axRef) {
16005 posPx.tail = ax._offset + ax.r2p(arrowLength);
16006 // tail is data-referenced: autorange pads the text in px from the tail
16007 textPadShift = textShift;
16008 } else {
16009 posPx.tail = basePx + arrowLength;
16010 // tail is specified in px from head, so autorange also pads vs head
16011 textPadShift = textShift + arrowLength;
16012 }
16013
16014 posPx.text = posPx.tail + textShift;
16015
16016 // constrain pixel/paper referenced so the draggers are at least
16017 // partially visible
16018 var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height'];
16019 if(axRef === 'paper') {
16020 posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1);
16021 }
16022 if(tailRef === 'pixel') {
16023 var shiftPlus = -Math.max(posPx.tail - 3, posPx.text);
16024 var shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx;
16025 if(shiftPlus > 0) {
16026 posPx.tail += shiftPlus;
16027 posPx.text += shiftPlus;
16028 } else if(shiftMinus > 0) {
16029 posPx.tail -= shiftMinus;
16030 posPx.text -= shiftMinus;
16031 }
16032 }
16033
16034 posPx.tail += overallShift;
16035 posPx.head += overallShift;
16036 } else {
16037 // with no arrow, the text rotates and *then* we put the anchor
16038 // relative to the new bounding box
16039 textShift = annSize * shiftFraction(autoAlignFraction, anchor);
16040 textPadShift = textShift;
16041 posPx.text = basePx + textShift;
16042 }
16043
16044 posPx.text += overallShift;
16045 textShift += overallShift;
16046 textPadShift += overallShift;
16047
16048 // padplus/minus are used by autorange
16049 options['_' + axLetter + 'padplus'] = (annSize / 2) + textPadShift;
16050 options['_' + axLetter + 'padminus'] = (annSize / 2) - textPadShift;
16051
16052 // size/shift are used during dragging
16053 options['_' + axLetter + 'size'] = annSize;
16054 options['_' + axLetter + 'shift'] = textShift;
16055 }
16056
16057 if(annotationIsOffscreen) {
16058 annTextGroupInner.remove();
16059 return;
16060 }
16061
16062 var xShift = 0;
16063 var yShift = 0;
16064
16065 if(options.align !== 'left') {
16066 xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
16067 }
16068 if(options.valign !== 'top') {
16069 yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
16070 }
16071
16072 if(hasMathjax) {
16073 mathjaxGroup.select('svg').attr({
16074 x: borderfull + xShift - 1,
16075 y: borderfull + yShift
16076 })
16077 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
16078 } else {
16079 var texty = borderfull + yShift - anntextBB.top;
16080 var textx = borderfull + xShift - anntextBB.left;
16081
16082 annText.call(svgTextUtils.positionText, textx, texty)
16083 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
16084 }
16085
16086 annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
16087 annWidth, annHeight);
16088
16089 annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
16090 outerWidth - borderwidth, outerHeight - borderwidth);
16091
16092 annTextGroupInner.call(Drawing.setTranslate,
16093 Math.round(annPosPx.x.text - outerWidth / 2),
16094 Math.round(annPosPx.y.text - outerHeight / 2));
16095
16096 /*
16097 * rotate text and background
16098 * we already calculated the text center position *as rotated*
16099 * because we needed that for autoranging anyway, so now whether
16100 * we have an arrow or not, we rotate about the text center.
16101 */
16102 annTextGroup.attr({transform: 'rotate(' + textangle + ',' +
16103 annPosPx.x.text + ',' + annPosPx.y.text + ')'});
16104
16105 /*
16106 * add the arrow
16107 * uses options[arrowwidth,arrowcolor,arrowhead] for styling
16108 * dx and dy are normally zero, but when you are dragging the textbox
16109 * while the head stays put, dx and dy are the pixel offsets
16110 */
16111 var drawArrow = function(dx, dy) {
16112 annGroup
16113 .selectAll('.annotation-arrow-g')
16114 .remove();
16115
16116 var headX = annPosPx.x.head;
16117 var headY = annPosPx.y.head;
16118 var tailX = annPosPx.x.tail + dx;
16119 var tailY = annPosPx.y.tail + dy;
16120 var textX = annPosPx.x.text + dx;
16121 var textY = annPosPx.y.text + dy;
16122
16123 // find the edge of the text box, where we'll start the arrow:
16124 // create transform matrix to rotate the text box corners
16125 var transform = Lib.rotationXYMatrix(textangle, textX, textY);
16126 var applyTransform = Lib.apply2DTransform(transform);
16127 var applyTransform2 = Lib.apply2DTransform2(transform);
16128
16129 // calculate and transform bounding box
16130 var width = +annTextBG.attr('width');
16131 var height = +annTextBG.attr('height');
16132 var xLeft = textX - 0.5 * width;
16133 var xRight = xLeft + width;
16134 var yTop = textY - 0.5 * height;
16135 var yBottom = yTop + height;
16136 var edges = [
16137 [xLeft, yTop, xLeft, yBottom],
16138 [xLeft, yBottom, xRight, yBottom],
16139 [xRight, yBottom, xRight, yTop],
16140 [xRight, yTop, xLeft, yTop]
16141 ].map(applyTransform2);
16142
16143 // Remove the line if it ends inside the box. Use ray
16144 // casting for rotated boxes: see which edges intersect a
16145 // line from the arrowhead to far away and reduce with xor
16146 // to get the parity of the number of intersections.
16147 if(edges.reduce(function(a, x) {
16148 return a ^
16149 !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6,
16150 x[0], x[1], x[2], x[3]);
16151 }, false)) {
16152 // no line or arrow - so quit drawArrow now
16153 return;
16154 }
16155
16156 edges.forEach(function(x) {
16157 var p = Lib.segmentsIntersect(tailX, tailY, headX, headY,
16158 x[0], x[1], x[2], x[3]);
16159 if(p) {
16160 tailX = p.x;
16161 tailY = p.y;
16162 }
16163 });
16164
16165 var strokewidth = options.arrowwidth;
16166 var arrowColor = options.arrowcolor;
16167 var arrowSide = options.arrowside;
16168
16169 var arrowGroup = annGroup.append('g')
16170 .style({opacity: Color.opacity(arrowColor)})
16171 .classed('annotation-arrow-g', true);
16172
16173 var arrow = arrowGroup.append('path')
16174 .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY)
16175 .style('stroke-width', strokewidth + 'px')
16176 .call(Color.stroke, Color.rgb(arrowColor));
16177
16178 drawArrowHead(arrow, arrowSide, options);
16179
16180 // the arrow dragger is a small square right at the head, then a line to the tail,
16181 // all expanded by a stroke width of 6px plus the arrow line width
16182 if(edits.annotationPosition && arrow.node().parentNode && !subplotId) {
16183 var arrowDragHeadX = headX;
16184 var arrowDragHeadY = headY;
16185 if(options.standoff) {
16186 var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2));
16187 arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength;
16188 arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength;
16189 }
16190 var arrowDrag = arrowGroup.append('path')
16191 .classed('annotation-arrow', true)
16192 .classed('anndrag', true)
16193 .classed('cursor-move', true)
16194 .attr({
16195 d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY),
16196 transform: 'translate(' + arrowDragHeadX + ',' + arrowDragHeadY + ')'
16197 })
16198 .style('stroke-width', (strokewidth + 6) + 'px')
16199 .call(Color.stroke, 'rgba(0,0,0,0)')
16200 .call(Color.fill, 'rgba(0,0,0,0)');
16201
16202 var annx0, anny0;
16203
16204 // dragger for the arrow & head: translates the whole thing
16205 // (head/tail/text) all together
16206 dragElement.init({
16207 element: arrowDrag.node(),
16208 gd: gd,
16209 prepFn: function() {
16210 var pos = Drawing.getTranslate(annTextGroupInner);
16211
16212 annx0 = pos.x;
16213 anny0 = pos.y;
16214 if(xa && xa.autorange) {
16215 modifyBase(xa._name + '.autorange', true);
16216 }
16217 if(ya && ya.autorange) {
16218 modifyBase(ya._name + '.autorange', true);
16219 }
16220 },
16221 moveFn: function(dx, dy) {
16222 var annxy0 = applyTransform(annx0, anny0);
16223 var xcenter = annxy0[0] + dx;
16224 var ycenter = annxy0[1] + dy;
16225 annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter);
16226
16227 modifyItem('x', xa ?
16228 xa.p2r(xa.r2p(options.x) + dx) :
16229 (options.x + (dx / gs.w)));
16230 modifyItem('y', ya ?
16231 ya.p2r(ya.r2p(options.y) + dy) :
16232 (options.y - (dy / gs.h)));
16233
16234 if(options.axref === options.xref) {
16235 modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
16236 }
16237
16238 if(options.ayref === options.yref) {
16239 modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
16240 }
16241
16242 arrowGroup.attr('transform', 'translate(' + dx + ',' + dy + ')');
16243 annTextGroup.attr({
16244 transform: 'rotate(' + textangle + ',' +
16245 xcenter + ',' + ycenter + ')'
16246 });
16247 },
16248 doneFn: function() {
16249 Registry.call('_guiRelayout', gd, getUpdateObj());
16250 var notesBox = document.querySelector('.js-notes-box-panel');
16251 if(notesBox) notesBox.redraw(notesBox.selectedObj);
16252 }
16253 });
16254 }
16255 };
16256
16257 if(options.showarrow) drawArrow(0, 0);
16258
16259 // user dragging the annotation (text, not arrow)
16260 if(editTextPosition) {
16261 var baseTextTransform;
16262
16263 // dragger for the textbox: if there's an arrow, just drag the
16264 // textbox and tail, leave the head untouched
16265 dragElement.init({
16266 element: annTextGroupInner.node(),
16267 gd: gd,
16268 prepFn: function() {
16269 baseTextTransform = annTextGroup.attr('transform');
16270 },
16271 moveFn: function(dx, dy) {
16272 var csr = 'pointer';
16273 if(options.showarrow) {
16274 if(options.axref === options.xref) {
16275 modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
16276 } else {
16277 modifyItem('ax', options.ax + dx);
16278 }
16279
16280 if(options.ayref === options.yref) {
16281 modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
16282 } else {
16283 modifyItem('ay', options.ay + dy);
16284 }
16285
16286 drawArrow(dx, dy);
16287 } else if(!subplotId) {
16288 var xUpdate, yUpdate;
16289 if(xa) {
16290 xUpdate = xa.p2r(xa.r2p(options.x) + dx);
16291 } else {
16292 var widthFraction = options._xsize / gs.w;
16293 var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2;
16294
16295 xUpdate = dragElement.align(xLeft + dx / gs.w,
16296 widthFraction, 0, 1, options.xanchor);
16297 }
16298
16299 if(ya) {
16300 yUpdate = ya.p2r(ya.r2p(options.y) + dy);
16301 } else {
16302 var heightFraction = options._ysize / gs.h;
16303 var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2;
16304
16305 yUpdate = dragElement.align(yBottom - dy / gs.h,
16306 heightFraction, 0, 1, options.yanchor);
16307 }
16308 modifyItem('x', xUpdate);
16309 modifyItem('y', yUpdate);
16310 if(!xa || !ya) {
16311 csr = dragElement.getCursor(
16312 xa ? 0.5 : xUpdate,
16313 ya ? 0.5 : yUpdate,
16314 options.xanchor, options.yanchor
16315 );
16316 }
16317 } else return;
16318
16319 annTextGroup.attr({
16320 transform: 'translate(' + dx + ',' + dy + ')' + baseTextTransform
16321 });
16322
16323 setCursor(annTextGroupInner, csr);
16324 },
16325 clickFn: function(_, initialEvent) {
16326 if(options.captureevents) {
16327 gd.emit('plotly_clickannotation', makeEventData(initialEvent));
16328 }
16329 },
16330 doneFn: function() {
16331 setCursor(annTextGroupInner);
16332 Registry.call('_guiRelayout', gd, getUpdateObj());
16333 var notesBox = document.querySelector('.js-notes-box-panel');
16334 if(notesBox) notesBox.redraw(notesBox.selectedObj);
16335 }
16336 });
16337 }
16338 }
16339
16340 if(edits.annotationText) {
16341 annText.call(svgTextUtils.makeEditable, {delegate: annTextGroupInner, gd: gd})
16342 .call(textLayout)
16343 .on('edit', function(_text) {
16344 options.text = _text;
16345
16346 this.call(textLayout);
16347
16348 modifyItem('text', _text);
16349
16350 if(xa && xa.autorange) {
16351 modifyBase(xa._name + '.autorange', true);
16352 }
16353 if(ya && ya.autorange) {
16354 modifyBase(ya._name + '.autorange', true);
16355 }
16356
16357 Registry.call('_guiRelayout', gd, getUpdateObj());
16358 });
16359 } else annText.call(textLayout);
16360}
16361
16362},{"../../lib":178,"../../lib/setcursor":197,"../../lib/svg_text_utils":199,"../../plot_api/plot_template":212,"../../plots/cartesian/axes":222,"../../plots/plots":256,"../../registry":269,"../color":52,"../dragelement":71,"../drawing":74,"../fx":92,"./draw_arrow_head":44,"d3":16}],44:[function(_dereq_,module,exports){
16363/**
16364* Copyright 2012-2020, Plotly, Inc.
16365* All rights reserved.
16366*
16367* This source code is licensed under the MIT license found in the
16368* LICENSE file in the root directory of this source tree.
16369*/
16370
16371
16372'use strict';
16373
16374var d3 = _dereq_('d3');
16375
16376var Color = _dereq_('../color');
16377
16378var ARROWPATHS = _dereq_('./arrow_paths');
16379
16380/**
16381 * Add arrowhead(s) to a path or line element
16382 *
16383 * @param {d3.selection} el3: a d3-selected line or path element
16384 *
16385 * @param {string} ends: 'none', 'start', 'end', or 'start+end' for which ends get arrowheads
16386 *
16387 * @param {object} options: style information. Must have all the following:
16388 * @param {number} options.arrowhead: end head style - see ./arrow_paths
16389 * @param {number} options.startarrowhead: start head style - see ./arrow_paths
16390 * @param {number} options.arrowsize: relative size of the end head vs line width
16391 * @param {number} options.startarrowsize: relative size of the start head vs line width
16392 * @param {number} options.standoff: distance in px to move the end arrow point from its target
16393 * @param {number} options.startstandoff: distance in px to move the start arrow point from its target
16394 * @param {number} options.arrowwidth: width of the arrow line
16395 * @param {string} options.arrowcolor: color of the arrow line, for the head to match
16396 * Note that the opacity of this color is ignored, as it's assumed the container
16397 * of both the line and head has opacity applied to it so there isn't greater opacity
16398 * where they overlap.
16399 */
16400module.exports = function drawArrowHead(el3, ends, options) {
16401 var el = el3.node();
16402 var headStyle = ARROWPATHS[options.arrowhead || 0];
16403 var startHeadStyle = ARROWPATHS[options.startarrowhead || 0];
16404 var scale = (options.arrowwidth || 1) * (options.arrowsize || 1);
16405 var startScale = (options.arrowwidth || 1) * (options.startarrowsize || 1);
16406 var doStart = ends.indexOf('start') >= 0;
16407 var doEnd = ends.indexOf('end') >= 0;
16408 var backOff = headStyle.backoff * scale + options.standoff;
16409 var startBackOff = startHeadStyle.backoff * startScale + options.startstandoff;
16410
16411 var start, end, startRot, endRot;
16412
16413 if(el.nodeName === 'line') {
16414 start = {x: +el3.attr('x1'), y: +el3.attr('y1')};
16415 end = {x: +el3.attr('x2'), y: +el3.attr('y2')};
16416
16417 var dx = start.x - end.x;
16418 var dy = start.y - end.y;
16419
16420 startRot = Math.atan2(dy, dx);
16421 endRot = startRot + Math.PI;
16422 if(backOff && startBackOff) {
16423 if(backOff + startBackOff > Math.sqrt(dx * dx + dy * dy)) {
16424 hideLine();
16425 return;
16426 }
16427 }
16428
16429 if(backOff) {
16430 if(backOff * backOff > dx * dx + dy * dy) {
16431 hideLine();
16432 return;
16433 }
16434 var backOffX = backOff * Math.cos(startRot);
16435 var backOffY = backOff * Math.sin(startRot);
16436
16437 end.x += backOffX;
16438 end.y += backOffY;
16439 el3.attr({x2: end.x, y2: end.y});
16440 }
16441
16442 if(startBackOff) {
16443 if(startBackOff * startBackOff > dx * dx + dy * dy) {
16444 hideLine();
16445 return;
16446 }
16447 var startBackOffX = startBackOff * Math.cos(startRot);
16448 var startbackOffY = startBackOff * Math.sin(startRot);
16449
16450 start.x -= startBackOffX;
16451 start.y -= startbackOffY;
16452 el3.attr({x1: start.x, y1: start.y});
16453 }
16454 } else if(el.nodeName === 'path') {
16455 var pathlen = el.getTotalLength();
16456 // using dash to hide the backOff region of the path.
16457 // if we ever allow dash for the arrow we'll have to
16458 // do better than this hack... maybe just manually
16459 // combine the two
16460 var dashArray = '';
16461
16462 if(pathlen < backOff + startBackOff) {
16463 hideLine();
16464 return;
16465 }
16466
16467
16468 var start0 = el.getPointAtLength(0);
16469 var dstart = el.getPointAtLength(0.1);
16470
16471 startRot = Math.atan2(start0.y - dstart.y, start0.x - dstart.x);
16472 start = el.getPointAtLength(Math.min(startBackOff, pathlen));
16473
16474 dashArray = '0px,' + startBackOff + 'px,';
16475
16476 var end0 = el.getPointAtLength(pathlen);
16477 var dend = el.getPointAtLength(pathlen - 0.1);
16478
16479 endRot = Math.atan2(end0.y - dend.y, end0.x - dend.x);
16480 end = el.getPointAtLength(Math.max(0, pathlen - backOff));
16481
16482 var shortening = dashArray ? startBackOff + backOff : backOff;
16483 dashArray += (pathlen - shortening) + 'px,' + pathlen + 'px';
16484
16485 el3.style('stroke-dasharray', dashArray);
16486 }
16487
16488 function hideLine() { el3.style('stroke-dasharray', '0px,100px'); }
16489
16490 function drawhead(arrowHeadStyle, p, rot, arrowScale) {
16491 if(!arrowHeadStyle.path) return;
16492 if(arrowHeadStyle.noRotate) rot = 0;
16493
16494 d3.select(el.parentNode).append('path')
16495 .attr({
16496 'class': el3.attr('class'),
16497 d: arrowHeadStyle.path,
16498 transform:
16499 'translate(' + p.x + ',' + p.y + ')' +
16500 (rot ? 'rotate(' + (rot * 180 / Math.PI) + ')' : '') +
16501 'scale(' + arrowScale + ')'
16502 })
16503 .style({
16504 fill: Color.rgb(options.arrowcolor),
16505 'stroke-width': 0
16506 });
16507 }
16508
16509 if(doStart) drawhead(startHeadStyle, start, startRot, startScale);
16510 if(doEnd) drawhead(headStyle, end, endRot, scale);
16511};
16512
16513},{"../color":52,"./arrow_paths":36,"d3":16}],45:[function(_dereq_,module,exports){
16514/**
16515* Copyright 2012-2020, Plotly, Inc.
16516* All rights reserved.
16517*
16518* This source code is licensed under the MIT license found in the
16519* LICENSE file in the root directory of this source tree.
16520*/
16521
16522
16523'use strict';
16524
16525var drawModule = _dereq_('./draw');
16526var clickModule = _dereq_('./click');
16527
16528module.exports = {
16529 moduleType: 'component',
16530 name: 'annotations',
16531
16532 layoutAttributes: _dereq_('./attributes'),
16533 supplyLayoutDefaults: _dereq_('./defaults'),
16534 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('annotations'),
16535
16536 calcAutorange: _dereq_('./calc_autorange'),
16537 draw: drawModule.draw,
16538 drawOne: drawModule.drawOne,
16539 drawRaw: drawModule.drawRaw,
16540
16541 hasClickToShow: clickModule.hasClickToShow,
16542 onClick: clickModule.onClick,
16543
16544 convertCoords: _dereq_('./convert_coords')
16545};
16546
16547},{"../../plots/cartesian/include_components":234,"./attributes":37,"./calc_autorange":38,"./click":39,"./convert_coords":41,"./defaults":42,"./draw":43}],46:[function(_dereq_,module,exports){
16548/**
16549* Copyright 2012-2020, Plotly, Inc.
16550* All rights reserved.
16551*
16552* This source code is licensed under the MIT license found in the
16553* LICENSE file in the root directory of this source tree.
16554*/
16555
16556
16557'use strict';
16558
16559var annAttrs = _dereq_('../annotations/attributes');
16560var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
16561var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
16562
16563module.exports = overrideAll(templatedArray('annotation', {
16564 visible: annAttrs.visible,
16565 x: {
16566 valType: 'any',
16567
16568
16569 },
16570 y: {
16571 valType: 'any',
16572
16573
16574 },
16575 z: {
16576 valType: 'any',
16577
16578
16579 },
16580 ax: {
16581 valType: 'number',
16582
16583
16584 },
16585 ay: {
16586 valType: 'number',
16587
16588
16589 },
16590
16591 xanchor: annAttrs.xanchor,
16592 xshift: annAttrs.xshift,
16593 yanchor: annAttrs.yanchor,
16594 yshift: annAttrs.yshift,
16595
16596 text: annAttrs.text,
16597 textangle: annAttrs.textangle,
16598 font: annAttrs.font,
16599 width: annAttrs.width,
16600 height: annAttrs.height,
16601 opacity: annAttrs.opacity,
16602 align: annAttrs.align,
16603 valign: annAttrs.valign,
16604 bgcolor: annAttrs.bgcolor,
16605 bordercolor: annAttrs.bordercolor,
16606 borderpad: annAttrs.borderpad,
16607 borderwidth: annAttrs.borderwidth,
16608 showarrow: annAttrs.showarrow,
16609 arrowcolor: annAttrs.arrowcolor,
16610 arrowhead: annAttrs.arrowhead,
16611 startarrowhead: annAttrs.startarrowhead,
16612 arrowside: annAttrs.arrowside,
16613 arrowsize: annAttrs.arrowsize,
16614 startarrowsize: annAttrs.startarrowsize,
16615 arrowwidth: annAttrs.arrowwidth,
16616 standoff: annAttrs.standoff,
16617 startstandoff: annAttrs.startstandoff,
16618 hovertext: annAttrs.hovertext,
16619 hoverlabel: annAttrs.hoverlabel,
16620 captureevents: annAttrs.captureevents,
16621
16622 // maybes later?
16623 // clicktoshow: annAttrs.clicktoshow,
16624 // xclick: annAttrs.xclick,
16625 // yclick: annAttrs.yclick,
16626
16627 // not needed!
16628 // axref: 'pixel'
16629 // ayref: 'pixel'
16630 // xref: 'x'
16631 // yref: 'y
16632 // zref: 'z'
16633}), 'calc', 'from-root');
16634
16635},{"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../annotations/attributes":37}],47:[function(_dereq_,module,exports){
16636/**
16637* Copyright 2012-2020, Plotly, Inc.
16638* All rights reserved.
16639*
16640* This source code is licensed under the MIT license found in the
16641* LICENSE file in the root directory of this source tree.
16642*/
16643
16644'use strict';
16645
16646var Lib = _dereq_('../../lib');
16647var Axes = _dereq_('../../plots/cartesian/axes');
16648
16649module.exports = function convert(scene) {
16650 var fullSceneLayout = scene.fullSceneLayout;
16651 var anns = fullSceneLayout.annotations;
16652
16653 for(var i = 0; i < anns.length; i++) {
16654 mockAnnAxes(anns[i], scene);
16655 }
16656
16657 scene.fullLayout._infolayer
16658 .selectAll('.annotation-' + scene.id)
16659 .remove();
16660};
16661
16662function mockAnnAxes(ann, scene) {
16663 var fullSceneLayout = scene.fullSceneLayout;
16664 var domain = fullSceneLayout.domain;
16665 var size = scene.fullLayout._size;
16666
16667 var base = {
16668 // this gets fill in on render
16669 pdata: null,
16670
16671 // to get setConvert to not execute cleanly
16672 type: 'linear',
16673
16674 // don't try to update them on `editable: true`
16675 autorange: false,
16676
16677 // set infinite range so that annotation draw routine
16678 // does not try to remove 'outside-range' annotations,
16679 // this case is handled in the render loop
16680 range: [-Infinity, Infinity]
16681 };
16682
16683 ann._xa = {};
16684 Lib.extendFlat(ann._xa, base);
16685 Axes.setConvert(ann._xa);
16686 ann._xa._offset = size.l + domain.x[0] * size.w;
16687 ann._xa.l2p = function() {
16688 return 0.5 * (1 + ann._pdata[0] / ann._pdata[3]) * size.w * (domain.x[1] - domain.x[0]);
16689 };
16690
16691 ann._ya = {};
16692 Lib.extendFlat(ann._ya, base);
16693 Axes.setConvert(ann._ya);
16694 ann._ya._offset = size.t + (1 - domain.y[1]) * size.h;
16695 ann._ya.l2p = function() {
16696 return 0.5 * (1 - ann._pdata[1] / ann._pdata[3]) * size.h * (domain.y[1] - domain.y[0]);
16697 };
16698}
16699
16700},{"../../lib":178,"../../plots/cartesian/axes":222}],48:[function(_dereq_,module,exports){
16701/**
16702* Copyright 2012-2020, Plotly, Inc.
16703* All rights reserved.
16704*
16705* This source code is licensed under the MIT license found in the
16706* LICENSE file in the root directory of this source tree.
16707*/
16708
16709'use strict';
16710
16711var Lib = _dereq_('../../lib');
16712var Axes = _dereq_('../../plots/cartesian/axes');
16713var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
16714var handleAnnotationCommonDefaults = _dereq_('../annotations/common_defaults');
16715var attributes = _dereq_('./attributes');
16716
16717module.exports = function handleDefaults(sceneLayoutIn, sceneLayoutOut, opts) {
16718 handleArrayContainerDefaults(sceneLayoutIn, sceneLayoutOut, {
16719 name: 'annotations',
16720 handleItemDefaults: handleAnnotationDefaults,
16721 fullLayout: opts.fullLayout
16722 });
16723};
16724
16725function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) {
16726 function coerce(attr, dflt) {
16727 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
16728 }
16729
16730 function coercePosition(axLetter) {
16731 var axName = axLetter + 'axis';
16732
16733 // mock in such way that getFromId grabs correct 3D axis
16734 var gdMock = { _fullLayout: {} };
16735 gdMock._fullLayout[axName] = sceneLayout[axName];
16736
16737 return Axes.coercePosition(annOut, gdMock, coerce, axLetter, axLetter, 0.5);
16738 }
16739
16740
16741 var visible = coerce('visible');
16742 if(!visible) return;
16743
16744 handleAnnotationCommonDefaults(annIn, annOut, opts.fullLayout, coerce);
16745
16746 coercePosition('x');
16747 coercePosition('y');
16748 coercePosition('z');
16749
16750 // if you have one coordinate you should all three
16751 Lib.noneOrAll(annIn, annOut, ['x', 'y', 'z']);
16752
16753 // hard-set here for completeness
16754 annOut.xref = 'x';
16755 annOut.yref = 'y';
16756 annOut.zref = 'z';
16757
16758 coerce('xanchor');
16759 coerce('yanchor');
16760 coerce('xshift');
16761 coerce('yshift');
16762
16763 if(annOut.showarrow) {
16764 annOut.axref = 'pixel';
16765 annOut.ayref = 'pixel';
16766
16767 // TODO maybe default values should be bigger than the 2D case?
16768 coerce('ax', -10);
16769 coerce('ay', -30);
16770
16771 // if you have one part of arrow length you should have both
16772 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
16773 }
16774}
16775
16776},{"../../lib":178,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"../annotations/common_defaults":40,"./attributes":46}],49:[function(_dereq_,module,exports){
16777/**
16778* Copyright 2012-2020, Plotly, Inc.
16779* All rights reserved.
16780*
16781* This source code is licensed under the MIT license found in the
16782* LICENSE file in the root directory of this source tree.
16783*/
16784
16785'use strict';
16786
16787var drawRaw = _dereq_('../annotations/draw').drawRaw;
16788var project = _dereq_('../../plots/gl3d/project');
16789var axLetters = ['x', 'y', 'z'];
16790
16791module.exports = function draw(scene) {
16792 var fullSceneLayout = scene.fullSceneLayout;
16793 var dataScale = scene.dataScale;
16794 var anns = fullSceneLayout.annotations;
16795
16796 for(var i = 0; i < anns.length; i++) {
16797 var ann = anns[i];
16798 var annotationIsOffscreen = false;
16799
16800 for(var j = 0; j < 3; j++) {
16801 var axLetter = axLetters[j];
16802 var pos = ann[axLetter];
16803 var ax = fullSceneLayout[axLetter + 'axis'];
16804 var posFraction = ax.r2fraction(pos);
16805
16806 if(posFraction < 0 || posFraction > 1) {
16807 annotationIsOffscreen = true;
16808 break;
16809 }
16810 }
16811
16812 if(annotationIsOffscreen) {
16813 scene.fullLayout._infolayer
16814 .select('.annotation-' + scene.id + '[data-index="' + i + '"]')
16815 .remove();
16816 } else {
16817 ann._pdata = project(scene.glplot.cameraParams, [
16818 fullSceneLayout.xaxis.r2l(ann.x) * dataScale[0],
16819 fullSceneLayout.yaxis.r2l(ann.y) * dataScale[1],
16820 fullSceneLayout.zaxis.r2l(ann.z) * dataScale[2]
16821 ]);
16822
16823 drawRaw(scene.graphDiv, ann, i, scene.id, ann._xa, ann._ya);
16824 }
16825 }
16826};
16827
16828},{"../../plots/gl3d/project":253,"../annotations/draw":43}],50:[function(_dereq_,module,exports){
16829/**
16830* Copyright 2012-2020, Plotly, Inc.
16831* All rights reserved.
16832*
16833* This source code is licensed under the MIT license found in the
16834* LICENSE file in the root directory of this source tree.
16835*/
16836
16837'use strict';
16838
16839var Registry = _dereq_('../../registry');
16840var Lib = _dereq_('../../lib');
16841
16842module.exports = {
16843 moduleType: 'component',
16844 name: 'annotations3d',
16845
16846 schema: {
16847 subplots: {
16848 scene: {annotations: _dereq_('./attributes')}
16849 }
16850 },
16851
16852 layoutAttributes: _dereq_('./attributes'),
16853 handleDefaults: _dereq_('./defaults'),
16854 includeBasePlot: includeGL3D,
16855
16856 convert: _dereq_('./convert'),
16857 draw: _dereq_('./draw')
16858};
16859
16860function includeGL3D(layoutIn, layoutOut) {
16861 var GL3D = Registry.subplotsRegistry.gl3d;
16862 if(!GL3D) return;
16863
16864 var attrRegex = GL3D.attrRegex;
16865
16866 var keys = Object.keys(layoutIn);
16867 for(var i = 0; i < keys.length; i++) {
16868 var k = keys[i];
16869 if(attrRegex.test(k) && (layoutIn[k].annotations || []).length) {
16870 Lib.pushUnique(layoutOut._basePlotModules, GL3D);
16871 Lib.pushUnique(layoutOut._subplots.gl3d, k);
16872 }
16873 }
16874}
16875
16876},{"../../lib":178,"../../registry":269,"./attributes":46,"./convert":47,"./defaults":48,"./draw":49}],51:[function(_dereq_,module,exports){
16877/**
16878* Copyright 2012-2020, Plotly, Inc.
16879* All rights reserved.
16880*
16881* This source code is licensed under the MIT license found in the
16882* LICENSE file in the root directory of this source tree.
16883*/
16884
16885'use strict';
16886
16887
16888// IMPORTANT - default colors should be in hex for compatibility
16889exports.defaults = [
16890 '#1f77b4', // muted blue
16891 '#ff7f0e', // safety orange
16892 '#2ca02c', // cooked asparagus green
16893 '#d62728', // brick red
16894 '#9467bd', // muted purple
16895 '#8c564b', // chestnut brown
16896 '#e377c2', // raspberry yogurt pink
16897 '#7f7f7f', // middle gray
16898 '#bcbd22', // curry yellow-green
16899 '#17becf' // blue-teal
16900];
16901
16902exports.defaultLine = '#444';
16903
16904exports.lightLine = '#eee';
16905
16906exports.background = '#fff';
16907
16908exports.borderLine = '#BEC8D9';
16909
16910// with axis.color and Color.interp we aren't using lightLine
16911// itself anymore, instead interpolating between axis.color
16912// and the background color using tinycolor.mix. lightFraction
16913// gives back exactly lightLine if the other colors are defaults.
16914exports.lightFraction = 100 * (0xe - 0x4) / (0xf - 0x4);
16915
16916},{}],52:[function(_dereq_,module,exports){
16917/**
16918* Copyright 2012-2020, Plotly, Inc.
16919* All rights reserved.
16920*
16921* This source code is licensed under the MIT license found in the
16922* LICENSE file in the root directory of this source tree.
16923*/
16924
16925
16926'use strict';
16927
16928var tinycolor = _dereq_('tinycolor2');
16929var isNumeric = _dereq_('fast-isnumeric');
16930
16931var color = module.exports = {};
16932
16933var colorAttrs = _dereq_('./attributes');
16934color.defaults = colorAttrs.defaults;
16935var defaultLine = color.defaultLine = colorAttrs.defaultLine;
16936color.lightLine = colorAttrs.lightLine;
16937var background = color.background = colorAttrs.background;
16938
16939/*
16940 * tinyRGB: turn a tinycolor into an rgb string, but
16941 * unlike the built-in tinycolor.toRgbString this never includes alpha
16942 */
16943color.tinyRGB = function(tc) {
16944 var c = tc.toRgb();
16945 return 'rgb(' + Math.round(c.r) + ', ' +
16946 Math.round(c.g) + ', ' + Math.round(c.b) + ')';
16947};
16948
16949color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); };
16950
16951color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; };
16952
16953color.addOpacity = function(cstr, op) {
16954 var c = tinycolor(cstr).toRgb();
16955 return 'rgba(' + Math.round(c.r) + ', ' +
16956 Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')';
16957};
16958
16959// combine two colors into one apparent color
16960// if back has transparency or is missing,
16961// color.background is assumed behind it
16962color.combine = function(front, back) {
16963 var fc = tinycolor(front).toRgb();
16964 if(fc.a === 1) return tinycolor(front).toRgbString();
16965
16966 var bc = tinycolor(back || background).toRgb();
16967 var bcflat = bc.a === 1 ? bc : {
16968 r: 255 * (1 - bc.a) + bc.r * bc.a,
16969 g: 255 * (1 - bc.a) + bc.g * bc.a,
16970 b: 255 * (1 - bc.a) + bc.b * bc.a
16971 };
16972 var fcflat = {
16973 r: bcflat.r * (1 - fc.a) + fc.r * fc.a,
16974 g: bcflat.g * (1 - fc.a) + fc.g * fc.a,
16975 b: bcflat.b * (1 - fc.a) + fc.b * fc.a
16976 };
16977 return tinycolor(fcflat).toRgbString();
16978};
16979
16980/*
16981 * Create a color that contrasts with cstr.
16982 *
16983 * If cstr is a dark color, we lighten it; if it's light, we darken.
16984 *
16985 * If lightAmount / darkAmount are used, we adjust by these percentages,
16986 * otherwise we go all the way to white or black.
16987 */
16988color.contrast = function(cstr, lightAmount, darkAmount) {
16989 var tc = tinycolor(cstr);
16990
16991 if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background));
16992
16993 var newColor = tc.isDark() ?
16994 (lightAmount ? tc.lighten(lightAmount) : background) :
16995 (darkAmount ? tc.darken(darkAmount) : defaultLine);
16996
16997 return newColor.toString();
16998};
16999
17000color.stroke = function(s, c) {
17001 var tc = tinycolor(c);
17002 s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()});
17003};
17004
17005color.fill = function(s, c) {
17006 var tc = tinycolor(c);
17007 s.style({
17008 'fill': color.tinyRGB(tc),
17009 'fill-opacity': tc.getAlpha()
17010 });
17011};
17012
17013// search container for colors with the deprecated rgb(fractions) format
17014// and convert them to rgb(0-255 values)
17015color.clean = function(container) {
17016 if(!container || typeof container !== 'object') return;
17017
17018 var keys = Object.keys(container);
17019 var i, j, key, val;
17020
17021 for(i = 0; i < keys.length; i++) {
17022 key = keys[i];
17023 val = container[key];
17024
17025 if(key.substr(key.length - 5) === 'color') {
17026 // only sanitize keys that end in "color" or "colorscale"
17027
17028 if(Array.isArray(val)) {
17029 for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]);
17030 } else container[key] = cleanOne(val);
17031 } else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) {
17032 // colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]]
17033
17034 for(j = 0; j < val.length; j++) {
17035 if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]);
17036 }
17037 } else if(Array.isArray(val)) {
17038 // recurse into arrays of objects, and plain objects
17039
17040 var el0 = val[0];
17041 if(!Array.isArray(el0) && el0 && typeof el0 === 'object') {
17042 for(j = 0; j < val.length; j++) color.clean(val[j]);
17043 }
17044 } else if(val && typeof val === 'object') color.clean(val);
17045 }
17046};
17047
17048function cleanOne(val) {
17049 if(isNumeric(val) || typeof val !== 'string') return val;
17050
17051 var valTrim = val.trim();
17052 if(valTrim.substr(0, 3) !== 'rgb') return val;
17053
17054 var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/);
17055 if(!match) return val;
17056
17057 var parts = match[1].trim().split(/\s*[\s,]\s*/);
17058 var rgba = valTrim.charAt(3) === 'a' && parts.length === 4;
17059 if(!rgba && parts.length !== 3) return val;
17060
17061 for(var i = 0; i < parts.length; i++) {
17062 if(!parts[i].length) return val;
17063 parts[i] = Number(parts[i]);
17064
17065 if(!(parts[i] >= 0)) {
17066 // all parts must be non-negative numbers
17067
17068 return val;
17069 }
17070
17071 if(i === 3) {
17072 // alpha>1 gets clipped to 1
17073
17074 if(parts[i] > 1) parts[i] = 1;
17075 } else if(parts[i] >= 1) {
17076 // r, g, b must be < 1 (ie 1 itself is not allowed)
17077
17078 return val;
17079 }
17080 }
17081
17082 var rgbStr = Math.round(parts[0] * 255) + ', ' +
17083 Math.round(parts[1] * 255) + ', ' +
17084 Math.round(parts[2] * 255);
17085
17086 if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')';
17087 return 'rgb(' + rgbStr + ')';
17088}
17089
17090},{"./attributes":51,"fast-isnumeric":18,"tinycolor2":35}],53:[function(_dereq_,module,exports){
17091/**
17092* Copyright 2012-2020, Plotly, Inc.
17093* All rights reserved.
17094*
17095* This source code is licensed under the MIT license found in the
17096* LICENSE file in the root directory of this source tree.
17097*/
17098
17099'use strict';
17100
17101var axesAttrs = _dereq_('../../plots/cartesian/layout_attributes');
17102var fontAttrs = _dereq_('../../plots/font_attributes');
17103var extendFlat = _dereq_('../../lib/extend').extendFlat;
17104var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
17105
17106
17107module.exports = overrideAll({
17108// TODO: only right is supported currently
17109// orient: {
17110// valType: 'enumerated',
17111//
17112// values: ['left', 'right', 'top', 'bottom'],
17113// dflt: 'right',
17114//
17115// },
17116 thicknessmode: {
17117 valType: 'enumerated',
17118 values: ['fraction', 'pixels'],
17119
17120 dflt: 'pixels',
17121
17122 },
17123 thickness: {
17124 valType: 'number',
17125
17126 min: 0,
17127 dflt: 30,
17128
17129 },
17130 lenmode: {
17131 valType: 'enumerated',
17132 values: ['fraction', 'pixels'],
17133
17134 dflt: 'fraction',
17135
17136 },
17137 len: {
17138 valType: 'number',
17139 min: 0,
17140 dflt: 1,
17141
17142
17143 },
17144 x: {
17145 valType: 'number',
17146 dflt: 1.02,
17147 min: -2,
17148 max: 3,
17149
17150
17151 },
17152 xanchor: {
17153 valType: 'enumerated',
17154 values: ['left', 'center', 'right'],
17155 dflt: 'left',
17156
17157
17158 },
17159 xpad: {
17160 valType: 'number',
17161
17162 min: 0,
17163 dflt: 10,
17164
17165 },
17166 y: {
17167 valType: 'number',
17168
17169 dflt: 0.5,
17170 min: -2,
17171 max: 3,
17172
17173 },
17174 yanchor: {
17175 valType: 'enumerated',
17176 values: ['top', 'middle', 'bottom'],
17177
17178 dflt: 'middle',
17179
17180 },
17181 ypad: {
17182 valType: 'number',
17183
17184 min: 0,
17185 dflt: 10,
17186
17187 },
17188 // a possible line around the bar itself
17189 outlinecolor: axesAttrs.linecolor,
17190 outlinewidth: axesAttrs.linewidth,
17191 // Should outlinewidth have {dflt: 0} ?
17192 // another possible line outside the padding and tick labels
17193 bordercolor: axesAttrs.linecolor,
17194 borderwidth: {
17195 valType: 'number',
17196
17197 min: 0,
17198 dflt: 0,
17199
17200 },
17201 bgcolor: {
17202 valType: 'color',
17203
17204 dflt: 'rgba(0,0,0,0)',
17205
17206 },
17207 // tick and title properties named and function exactly as in axes
17208 tickmode: axesAttrs.tickmode,
17209 nticks: axesAttrs.nticks,
17210 tick0: axesAttrs.tick0,
17211 dtick: axesAttrs.dtick,
17212 tickvals: axesAttrs.tickvals,
17213 ticktext: axesAttrs.ticktext,
17214 ticks: extendFlat({}, axesAttrs.ticks, {dflt: ''}),
17215 ticklen: axesAttrs.ticklen,
17216 tickwidth: axesAttrs.tickwidth,
17217 tickcolor: axesAttrs.tickcolor,
17218 showticklabels: axesAttrs.showticklabels,
17219 tickfont: fontAttrs({
17220
17221 }),
17222 tickangle: axesAttrs.tickangle,
17223 tickformat: axesAttrs.tickformat,
17224 tickformatstops: axesAttrs.tickformatstops,
17225 tickprefix: axesAttrs.tickprefix,
17226 showtickprefix: axesAttrs.showtickprefix,
17227 ticksuffix: axesAttrs.ticksuffix,
17228 showticksuffix: axesAttrs.showticksuffix,
17229 separatethousands: axesAttrs.separatethousands,
17230 exponentformat: axesAttrs.exponentformat,
17231 showexponent: axesAttrs.showexponent,
17232 title: {
17233 text: {
17234 valType: 'string',
17235
17236
17237 },
17238 font: fontAttrs({
17239
17240 }),
17241 side: {
17242 valType: 'enumerated',
17243 values: ['right', 'top', 'bottom'],
17244
17245 dflt: 'top',
17246
17247 }
17248 },
17249
17250 _deprecated: {
17251 title: {
17252 valType: 'string',
17253
17254
17255 },
17256 titlefont: fontAttrs({
17257
17258 }),
17259 titleside: {
17260 valType: 'enumerated',
17261 values: ['right', 'top', 'bottom'],
17262
17263 dflt: 'top',
17264
17265 }
17266 }
17267}, 'colorbars', 'from-root');
17268
17269},{"../../lib/extend":173,"../../plot_api/edit_types":205,"../../plots/cartesian/layout_attributes":236,"../../plots/font_attributes":250}],54:[function(_dereq_,module,exports){
17270/**
17271* Copyright 2012-2020, Plotly, Inc.
17272* All rights reserved.
17273*
17274* This source code is licensed under the MIT license found in the
17275* LICENSE file in the root directory of this source tree.
17276*/
17277
17278'use strict';
17279
17280module.exports = {
17281 cn: {
17282 colorbar: 'colorbar',
17283 cbbg: 'cbbg',
17284 cbfill: 'cbfill',
17285 cbfills: 'cbfills',
17286 cbline: 'cbline',
17287 cblines: 'cblines',
17288 cbaxis: 'cbaxis',
17289 cbtitleunshift: 'cbtitleunshift',
17290 cbtitle: 'cbtitle',
17291 cboutline: 'cboutline',
17292 crisp: 'crisp',
17293 jsPlaceholder: 'js-placeholder'
17294 }
17295};
17296
17297},{}],55:[function(_dereq_,module,exports){
17298/**
17299* Copyright 2012-2020, Plotly, Inc.
17300* All rights reserved.
17301*
17302* This source code is licensed under the MIT license found in the
17303* LICENSE file in the root directory of this source tree.
17304*/
17305
17306
17307'use strict';
17308
17309var Lib = _dereq_('../../lib');
17310var Template = _dereq_('../../plot_api/plot_template');
17311
17312var handleTickValueDefaults = _dereq_('../../plots/cartesian/tick_value_defaults');
17313var handleTickMarkDefaults = _dereq_('../../plots/cartesian/tick_mark_defaults');
17314var handleTickLabelDefaults = _dereq_('../../plots/cartesian/tick_label_defaults');
17315
17316var attributes = _dereq_('./attributes');
17317
17318module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
17319 var colorbarOut = Template.newContainer(containerOut, 'colorbar');
17320 var colorbarIn = containerIn.colorbar || {};
17321
17322 function coerce(attr, dflt) {
17323 return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt);
17324 }
17325
17326 var thicknessmode = coerce('thicknessmode');
17327 coerce('thickness', (thicknessmode === 'fraction') ?
17328 30 / (layout.width - layout.margin.l - layout.margin.r) :
17329 30
17330 );
17331
17332 var lenmode = coerce('lenmode');
17333 coerce('len', (lenmode === 'fraction') ?
17334 1 :
17335 layout.height - layout.margin.t - layout.margin.b
17336 );
17337
17338 coerce('x');
17339 coerce('xanchor');
17340 coerce('xpad');
17341 coerce('y');
17342 coerce('yanchor');
17343 coerce('ypad');
17344 Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']);
17345
17346 coerce('outlinecolor');
17347 coerce('outlinewidth');
17348 coerce('bordercolor');
17349 coerce('borderwidth');
17350 coerce('bgcolor');
17351
17352 handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');
17353
17354 var opts = {outerTicks: false, font: layout.font};
17355 handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
17356 handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
17357
17358 coerce('title.text', layout._dfltTitle.colorbar);
17359 Lib.coerceFont(coerce, 'title.font', layout.font);
17360 coerce('title.side');
17361};
17362
17363},{"../../lib":178,"../../plot_api/plot_template":212,"../../plots/cartesian/tick_label_defaults":243,"../../plots/cartesian/tick_mark_defaults":244,"../../plots/cartesian/tick_value_defaults":245,"./attributes":53}],56:[function(_dereq_,module,exports){
17364/**
17365* Copyright 2012-2020, Plotly, Inc.
17366* All rights reserved.
17367*
17368* This source code is licensed under the MIT license found in the
17369* LICENSE file in the root directory of this source tree.
17370*/
17371
17372'use strict';
17373
17374var d3 = _dereq_('d3');
17375var tinycolor = _dereq_('tinycolor2');
17376
17377var Plots = _dereq_('../../plots/plots');
17378var Registry = _dereq_('../../registry');
17379var Axes = _dereq_('../../plots/cartesian/axes');
17380var dragElement = _dereq_('../dragelement');
17381var Lib = _dereq_('../../lib');
17382var extendFlat = _dereq_('../../lib/extend').extendFlat;
17383var setCursor = _dereq_('../../lib/setcursor');
17384var Drawing = _dereq_('../drawing');
17385var Color = _dereq_('../color');
17386var Titles = _dereq_('../titles');
17387var svgTextUtils = _dereq_('../../lib/svg_text_utils');
17388var flipScale = _dereq_('../colorscale/helpers').flipScale;
17389
17390var handleAxisDefaults = _dereq_('../../plots/cartesian/axis_defaults');
17391var handleAxisPositionDefaults = _dereq_('../../plots/cartesian/position_defaults');
17392var axisLayoutAttrs = _dereq_('../../plots/cartesian/layout_attributes');
17393
17394var alignmentConstants = _dereq_('../../constants/alignment');
17395var LINE_SPACING = alignmentConstants.LINE_SPACING;
17396var FROM_TL = alignmentConstants.FROM_TL;
17397var FROM_BR = alignmentConstants.FROM_BR;
17398
17399var cn = _dereq_('./constants').cn;
17400
17401function draw(gd) {
17402 var fullLayout = gd._fullLayout;
17403
17404 var colorBars = fullLayout._infolayer
17405 .selectAll('g.' + cn.colorbar)
17406 .data(makeColorBarData(gd), function(opts) { return opts._id; });
17407
17408 colorBars.enter().append('g')
17409 .attr('class', function(opts) { return opts._id; })
17410 .classed(cn.colorbar, true);
17411
17412 colorBars.each(function(opts) {
17413 var g = d3.select(this);
17414
17415 Lib.ensureSingle(g, 'rect', cn.cbbg);
17416 Lib.ensureSingle(g, 'g', cn.cbfills);
17417 Lib.ensureSingle(g, 'g', cn.cblines);
17418 Lib.ensureSingle(g, 'g', cn.cbaxis, function(s) { s.classed(cn.crisp, true); });
17419 Lib.ensureSingle(g, 'g', cn.cbtitleunshift, function(s) { s.append('g').classed(cn.cbtitle, true); });
17420 Lib.ensureSingle(g, 'rect', cn.cboutline);
17421
17422 var done = drawColorBar(g, opts, gd);
17423 if(done && done.then) (gd._promises || []).push(done);
17424
17425 if(gd._context.edits.colorbarPosition) {
17426 makeEditable(g, opts, gd);
17427 }
17428 });
17429
17430 colorBars.exit()
17431 .each(function(opts) { Plots.autoMargin(gd, opts._id); })
17432 .remove();
17433
17434 colorBars.order();
17435}
17436
17437function makeColorBarData(gd) {
17438 var fullLayout = gd._fullLayout;
17439 var calcdata = gd.calcdata;
17440 var out = [];
17441
17442 // single out item
17443 var opts;
17444 // colorbar attr parent container
17445 var cont;
17446 // trace attr container
17447 var trace;
17448 // colorbar options
17449 var cbOpt;
17450
17451 function initOpts(opts) {
17452 return extendFlat(opts, {
17453 // fillcolor can be a d3 scale, domain is z values, range is colors
17454 // or leave it out for no fill,
17455 // or set to a string constant for single-color fill
17456 _fillcolor: null,
17457 // line.color has the same options as fillcolor
17458 _line: {color: null, width: null, dash: null},
17459 // levels of lines to draw.
17460 // note that this DOES NOT determine the extent of the bar
17461 // that's given by the domain of fillcolor
17462 // (or line.color if no fillcolor domain)
17463 _levels: {start: null, end: null, size: null},
17464 // separate fill levels (for example, heatmap coloring of a
17465 // contour map) if this is omitted, fillcolors will be
17466 // evaluated halfway between levels
17467 _filllevels: null,
17468 // for continuous colorscales: fill with a gradient instead of explicit levels
17469 // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
17470 _fillgradient: null,
17471 // when using a gradient, we need the data range specified separately
17472 _zrange: null
17473 });
17474 }
17475
17476 function calcOpts() {
17477 if(typeof cbOpt.calc === 'function') {
17478 cbOpt.calc(gd, trace, opts);
17479 } else {
17480 opts._fillgradient = cont.reversescale ?
17481 flipScale(cont.colorscale) :
17482 cont.colorscale;
17483 opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]];
17484 }
17485 }
17486
17487 for(var i = 0; i < calcdata.length; i++) {
17488 var cd = calcdata[i];
17489 trace = cd[0].trace;
17490 var moduleOpts = trace._module.colorbar;
17491
17492 if(trace.visible === true && moduleOpts) {
17493 var allowsMultiplotCbs = Array.isArray(moduleOpts);
17494 var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts];
17495
17496 for(var j = 0; j < cbOpts.length; j++) {
17497 cbOpt = cbOpts[j];
17498 var contName = cbOpt.container;
17499 cont = contName ? trace[contName] : trace;
17500
17501 if(cont && cont.showscale) {
17502 opts = initOpts(cont.colorbar);
17503 opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : '');
17504 opts._traceIndex = trace.index;
17505 opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.';
17506 opts._meta = trace._meta;
17507 calcOpts();
17508 out.push(opts);
17509 }
17510 }
17511 }
17512 }
17513
17514 for(var k in fullLayout._colorAxes) {
17515 cont = fullLayout[k];
17516
17517 if(cont.showscale) {
17518 var colorAxOpts = fullLayout._colorAxes[k];
17519
17520 opts = initOpts(cont.colorbar);
17521 opts._id = 'cb' + k;
17522 opts._propPrefix = k + '.colorbar.';
17523 opts._meta = fullLayout._meta;
17524
17525 cbOpt = {min: 'cmin', max: 'cmax'};
17526 if(colorAxOpts[0] !== 'heatmap') {
17527 trace = colorAxOpts[1];
17528 cbOpt.calc = trace._module.colorbar.calc;
17529 }
17530
17531 calcOpts();
17532 out.push(opts);
17533 }
17534 }
17535
17536 return out;
17537}
17538
17539function drawColorBar(g, opts, gd) {
17540 var fullLayout = gd._fullLayout;
17541 var gs = fullLayout._size;
17542
17543 var fillColor = opts._fillcolor;
17544 var line = opts._line;
17545 var title = opts.title;
17546 var titleSide = title.side;
17547
17548 var zrange = opts._zrange ||
17549 d3.extent((typeof fillColor === 'function' ? fillColor : line.color).domain());
17550
17551 var lineColormap = typeof line.color === 'function' ?
17552 line.color :
17553 function() { return line.color; };
17554 var fillColormap = typeof fillColor === 'function' ?
17555 fillColor :
17556 function() { return fillColor; };
17557
17558 var levelsIn = opts._levels;
17559 var levelsOut = calcLevels(gd, opts, zrange);
17560 var fillLevels = levelsOut.fill;
17561 var lineLevels = levelsOut.line;
17562
17563 // we calculate pixel sizes based on the specified graph size,
17564 // not the actual (in case something pushed the margins around)
17565 // which is a little odd but avoids an odd iterative effect
17566 // when the colorbar itself is pushing the margins.
17567 // but then the fractional size is calculated based on the
17568 // actual graph size, so that the axes will size correctly.
17569 var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? gs.w : 1));
17570 var thickFrac = thickPx / gs.w;
17571 var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? gs.h : 1));
17572 var lenFrac = lenPx / gs.h;
17573 var xpadFrac = opts.xpad / gs.w;
17574 var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2;
17575 var ypadFrac = opts.ypad / gs.h;
17576
17577 // x positioning: do it initially just for left anchor,
17578 // then fix at the end (since we don't know the width yet)
17579 var xLeft = Math.round(opts.x * gs.w + opts.xpad);
17580 // for dragging... this is getting a little muddled...
17581 var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor] || 0);
17582
17583 // y positioning we can do correctly from the start
17584 var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5);
17585 var yBottomPx = Math.round(gs.h * (1 - yBottomFrac));
17586 var yTopPx = yBottomPx - lenPx;
17587
17588 // stash a few things for makeEditable
17589 opts._lenFrac = lenFrac;
17590 opts._thickFrac = thickFrac;
17591 opts._xLeftFrac = xLeftFrac;
17592 opts._yBottomFrac = yBottomFrac;
17593
17594 // stash mocked axis for contour label formatting
17595 var ax = opts._axis = mockColorBarAxis(gd, opts, zrange);
17596
17597 // position can't go in through supplyDefaults
17598 // because that restricts it to [0,1]
17599 ax.position = opts.x + xpadFrac + thickFrac;
17600
17601 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
17602 ax.title.side = titleSide;
17603 ax.titlex = opts.x + xpadFrac;
17604 ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac);
17605 }
17606
17607 if(line.color && opts.tickmode === 'auto') {
17608 ax.tickmode = 'linear';
17609 ax.tick0 = levelsIn.start;
17610 var dtick = levelsIn.size;
17611 // expand if too many contours, so we don't get too many ticks
17612 var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1;
17613 var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick);
17614 if(dtFactor > 1) {
17615 var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10));
17616 dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]);
17617 // if the contours are at round multiples, reset tick0
17618 // so they're still at round multiples. Otherwise,
17619 // keep the first label on the first contour level
17620 if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) {
17621 ax.tick0 = 0;
17622 }
17623 }
17624 ax.dtick = dtick;
17625 }
17626
17627 // set domain after init, because we may want to
17628 // allow it outside [0,1]
17629 ax.domain = [
17630 yBottomFrac + ypadFrac,
17631 yBottomFrac + lenFrac - ypadFrac
17632 ];
17633
17634 ax.setScale();
17635
17636 g.attr('transform', 'translate(' + Math.round(gs.l) + ',' + Math.round(gs.t) + ')');
17637
17638 var titleCont = g.select('.' + cn.cbtitleunshift)
17639 .attr('transform', 'translate(-' + Math.round(gs.l) + ',-' + Math.round(gs.t) + ')');
17640
17641 var axLayer = g.select('.' + cn.cbaxis);
17642 var titleEl;
17643 var titleHeight = 0;
17644
17645 function drawTitle(titleClass, titleOpts) {
17646 var dfltTitleOpts = {
17647 propContainer: ax,
17648 propName: opts._propPrefix + 'title',
17649 traceIndex: opts._traceIndex,
17650 _meta: opts._meta,
17651 placeholder: fullLayout._dfltTitle.colorbar,
17652 containerGroup: g.select('.' + cn.cbtitle)
17653 };
17654
17655 // this class-to-rotate thing with convertToTspans is
17656 // getting hackier and hackier... delete groups with the
17657 // wrong class (in case earlier the colorbar was drawn on
17658 // a different side, I think?)
17659 var otherClass = titleClass.charAt(0) === 'h' ?
17660 titleClass.substr(1) :
17661 'h' + titleClass;
17662 g.selectAll('.' + otherClass + ',.' + otherClass + '-math-group').remove();
17663
17664 Titles.draw(gd, titleClass, extendFlat(dfltTitleOpts, titleOpts || {}));
17665 }
17666
17667 function drawDummyTitle() {
17668 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
17669 // draw the title so we know how much room it needs
17670 // when we squish the axis. This one only applies to
17671 // top or bottom titles, not right side.
17672 var x = gs.l + (opts.x + xpadFrac) * gs.w;
17673 var fontSize = ax.title.font.size;
17674 var y;
17675
17676 if(titleSide === 'top') {
17677 y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h +
17678 gs.t + 3 + fontSize * 0.75;
17679 } else {
17680 y = (1 - (yBottomFrac + ypadFrac)) * gs.h +
17681 gs.t - 3 - fontSize * 0.25;
17682 }
17683 drawTitle(ax._id + 'title', {
17684 attributes: {x: x, y: y, 'text-anchor': 'start'}
17685 });
17686 }
17687 }
17688
17689 function drawCbTitle() {
17690 if(['top', 'bottom'].indexOf(titleSide) === -1) {
17691 var fontSize = ax.title.font.size;
17692 var y = ax._offset + ax._length / 2;
17693 var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ?
17694 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) :
17695 -10 - fontSize * ((ax.showticklabels ? 0.5 : 0)));
17696
17697 // the 'h' + is a hack to get around the fact that
17698 // convertToTspans rotates any 'y...' class by 90 degrees.
17699 // TODO: find a better way to control this.
17700 drawTitle('h' + ax._id + 'title', {
17701 avoid: {
17702 selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'),
17703 side: titleSide,
17704 offsetLeft: gs.l,
17705 offsetTop: 0,
17706 maxShift: fullLayout.width
17707 },
17708 attributes: {x: x, y: y, 'text-anchor': 'middle'},
17709 transform: {rotate: '-90', offset: 0}
17710 });
17711 }
17712 }
17713
17714 function drawAxis() {
17715 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
17716 // squish the axis top to make room for the title
17717 var titleGroup = g.select('.' + cn.cbtitle);
17718 var titleText = titleGroup.select('text');
17719 var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2];
17720 var mathJaxNode = titleGroup
17721 .select('.h' + ax._id + 'title-math-group')
17722 .node();
17723 var lineSize = 15.6;
17724 if(titleText.node()) {
17725 lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING;
17726 }
17727 if(mathJaxNode) {
17728 titleHeight = Drawing.bBox(mathJaxNode).height;
17729 if(titleHeight > lineSize) {
17730 // not entirely sure how mathjax is doing
17731 // vertical alignment, but this seems to work.
17732 titleTrans[1] -= (titleHeight - lineSize) / 2;
17733 }
17734 } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) {
17735 titleHeight = Drawing.bBox(titleText.node()).height;
17736 }
17737 if(titleHeight) {
17738 // buffer btwn colorbar and title
17739 // TODO: configurable
17740 titleHeight += 5;
17741
17742 if(titleSide === 'top') {
17743 ax.domain[1] -= titleHeight / gs.h;
17744 titleTrans[1] *= -1;
17745 } else {
17746 ax.domain[0] += titleHeight / gs.h;
17747 var nlines = svgTextUtils.lineCount(titleText);
17748 titleTrans[1] += (1 - nlines) * lineSize;
17749 }
17750
17751 titleGroup.attr('transform', 'translate(' + titleTrans + ')');
17752 ax.setScale();
17753 }
17754 }
17755
17756 g.selectAll('.' + cn.cbfills + ',.' + cn.cblines)
17757 .attr('transform', 'translate(0,' + Math.round(gs.h * (1 - ax.domain[1])) + ')');
17758
17759 axLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')');
17760
17761 var fills = g.select('.' + cn.cbfills)
17762 .selectAll('rect.' + cn.cbfill)
17763 .data(fillLevels);
17764 fills.enter().append('rect')
17765 .classed(cn.cbfill, true)
17766 .style('stroke', 'none');
17767 fills.exit().remove();
17768
17769 var zBounds = zrange
17770 .map(ax.c2p)
17771 .map(Math.round)
17772 .sort(function(a, b) { return a - b; });
17773
17774 fills.each(function(d, i) {
17775 var z = [
17776 (i === 0) ? zrange[0] : (fillLevels[i] + fillLevels[i - 1]) / 2,
17777 (i === fillLevels.length - 1) ? zrange[1] : (fillLevels[i] + fillLevels[i + 1]) / 2
17778 ]
17779 .map(ax.c2p)
17780 .map(Math.round);
17781
17782 // offset the side adjoining the next rectangle so they
17783 // overlap, to prevent antialiasing gaps
17784 z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
17785
17786
17787 // Colorbar cannot currently support opacities so we
17788 // use an opaque fill even when alpha channels present
17789 var fillEl = d3.select(this).attr({
17790 x: xLeft,
17791 width: Math.max(thickPx, 2),
17792 y: d3.min(z),
17793 height: Math.max(d3.max(z) - d3.min(z), 2),
17794 });
17795
17796 if(opts._fillgradient) {
17797 Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill');
17798 } else {
17799 // tinycolor can't handle exponents and
17800 // at this scale, removing it makes no difference.
17801 var colorString = fillColormap(d).replace('e-', '');
17802 fillEl.attr('fill', tinycolor(colorString).toHexString());
17803 }
17804 });
17805
17806 var lines = g.select('.' + cn.cblines)
17807 .selectAll('path.' + cn.cbline)
17808 .data(line.color && line.width ? lineLevels : []);
17809 lines.enter().append('path')
17810 .classed(cn.cbline, true);
17811 lines.exit().remove();
17812 lines.each(function(d) {
17813 d3.select(this)
17814 .attr('d', 'M' + xLeft + ',' +
17815 (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx)
17816 .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash);
17817 });
17818
17819 // force full redraw of labels and ticks
17820 axLayer.selectAll('g.' + ax._id + 'tick,path').remove();
17821
17822 var shift = xLeft + thickPx +
17823 (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0);
17824
17825 var vals = Axes.calcTicks(ax);
17826 var transFn = Axes.makeTransFn(ax);
17827 var tickSign = Axes.getTickSigns(ax)[2];
17828
17829 Axes.drawTicks(gd, ax, {
17830 vals: ax.ticks === 'inside' ? Axes.clipEnds(ax, vals) : vals,
17831 layer: axLayer,
17832 path: Axes.makeTickPath(ax, shift, tickSign),
17833 transFn: transFn
17834 });
17835
17836 return Axes.drawLabels(gd, ax, {
17837 vals: vals,
17838 layer: axLayer,
17839 transFn: transFn,
17840 labelFns: Axes.makeLabelFns(ax, shift)
17841 });
17842 }
17843
17844 // wait for the axis & title to finish rendering before
17845 // continuing positioning
17846 // TODO: why are we redrawing multiple times now with this?
17847 // I guess autoMargin doesn't like being post-promise?
17848 function positionCB() {
17849 var innerWidth = thickPx + opts.outlinewidth / 2 + Drawing.bBox(axLayer.node()).width;
17850 titleEl = titleCont.select('text');
17851
17852 if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) {
17853 var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node();
17854 var titleWidth;
17855 if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) {
17856 titleWidth = Drawing.bBox(mathJaxNode).width;
17857 } else {
17858 // note: the formula below works for all title sides,
17859 // (except for top/bottom mathjax, above)
17860 // but the weird gs.l is because the titleunshift
17861 // transform gets removed by Drawing.bBox
17862 titleWidth = Drawing.bBox(titleCont.node()).right - xLeft - gs.l;
17863 }
17864 innerWidth = Math.max(innerWidth, titleWidth);
17865 }
17866
17867 var outerwidth = 2 * opts.xpad + innerWidth + opts.borderwidth + opts.outlinewidth / 2;
17868 var outerheight = yBottomPx - yTopPx;
17869
17870 g.select('.' + cn.cbbg).attr({
17871 x: xLeft - opts.xpad - (opts.borderwidth + opts.outlinewidth) / 2,
17872 y: yTopPx - yExtraPx,
17873 width: Math.max(outerwidth, 2),
17874 height: Math.max(outerheight + 2 * yExtraPx, 2)
17875 })
17876 .call(Color.fill, opts.bgcolor)
17877 .call(Color.stroke, opts.bordercolor)
17878 .style('stroke-width', opts.borderwidth);
17879
17880 g.selectAll('.' + cn.cboutline).attr({
17881 x: xLeft,
17882 y: yTopPx + opts.ypad + (titleSide === 'top' ? titleHeight : 0),
17883 width: Math.max(thickPx, 2),
17884 height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2)
17885 })
17886 .call(Color.stroke, opts.outlinecolor)
17887 .style({
17888 fill: 'none',
17889 'stroke-width': opts.outlinewidth
17890 });
17891
17892 // fix positioning for xanchor!='left'
17893 var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * outerwidth;
17894 g.attr('transform', 'translate(' + (gs.l - xoffset) + ',' + gs.t + ')');
17895
17896 // auto margin adjustment
17897 var marginOpts = {};
17898 var tFrac = FROM_TL[opts.yanchor];
17899 var bFrac = FROM_BR[opts.yanchor];
17900 if(opts.lenmode === 'pixels') {
17901 marginOpts.y = opts.y;
17902 marginOpts.t = outerheight * tFrac;
17903 marginOpts.b = outerheight * bFrac;
17904 } else {
17905 marginOpts.t = marginOpts.b = 0;
17906 marginOpts.yt = opts.y + opts.len * tFrac;
17907 marginOpts.yb = opts.y - opts.len * bFrac;
17908 }
17909
17910 var lFrac = FROM_TL[opts.xanchor];
17911 var rFrac = FROM_BR[opts.xanchor];
17912 if(opts.thicknessmode === 'pixels') {
17913 marginOpts.x = opts.x;
17914 marginOpts.l = outerwidth * lFrac;
17915 marginOpts.r = outerwidth * rFrac;
17916 } else {
17917 var extraThickness = outerwidth - thickPx;
17918 marginOpts.l = extraThickness * lFrac;
17919 marginOpts.r = extraThickness * rFrac;
17920 marginOpts.xl = opts.x - opts.thickness * lFrac;
17921 marginOpts.xr = opts.x + opts.thickness * rFrac;
17922 }
17923
17924 Plots.autoMargin(gd, opts._id, marginOpts);
17925 }
17926
17927 return Lib.syncOrAsync([
17928 Plots.previousPromises,
17929 drawDummyTitle,
17930 drawAxis,
17931 drawCbTitle,
17932 Plots.previousPromises,
17933 positionCB
17934 ], gd);
17935}
17936
17937function makeEditable(g, opts, gd) {
17938 var fullLayout = gd._fullLayout;
17939 var gs = fullLayout._size;
17940 var t0, xf, yf;
17941
17942 dragElement.init({
17943 element: g.node(),
17944 gd: gd,
17945 prepFn: function() {
17946 t0 = g.attr('transform');
17947 setCursor(g);
17948 },
17949 moveFn: function(dx, dy) {
17950 g.attr('transform', t0 + ' ' + 'translate(' + dx + ',' + dy + ')');
17951
17952 xf = dragElement.align(opts._xLeftFrac + (dx / gs.w), opts._thickFrac,
17953 0, 1, opts.xanchor);
17954 yf = dragElement.align(opts._yBottomFrac - (dy / gs.h), opts._lenFrac,
17955 0, 1, opts.yanchor);
17956
17957 var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor);
17958 setCursor(g, csr);
17959 },
17960 doneFn: function() {
17961 setCursor(g);
17962
17963 if(xf !== undefined && yf !== undefined) {
17964 var update = {};
17965 update[opts._propPrefix + 'x'] = xf;
17966 update[opts._propPrefix + 'y'] = yf;
17967 if(opts._traceIndex !== undefined) {
17968 Registry.call('_guiRestyle', gd, update, opts._traceIndex);
17969 } else {
17970 Registry.call('_guiRelayout', gd, update);
17971 }
17972 }
17973 }
17974 });
17975}
17976
17977function calcLevels(gd, opts, zrange) {
17978 var levelsIn = opts._levels;
17979 var lineLevels = [];
17980 var fillLevels = [];
17981 var l;
17982 var i;
17983
17984 var l0 = levelsIn.end + levelsIn.size / 100;
17985 var ls = levelsIn.size;
17986 var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]);
17987 var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]);
17988
17989 for(i = 0; i < 1e5; i++) {
17990 l = levelsIn.start + i * ls;
17991 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
17992 if(l > zr0 && l < zr1) lineLevels.push(l);
17993 }
17994
17995 if(opts._fillgradient) {
17996 fillLevels = [0];
17997 } else if(typeof opts._fillcolor === 'function') {
17998 var fillLevelsIn = opts._filllevels;
17999
18000 if(fillLevelsIn) {
18001 l0 = fillLevelsIn.end + fillLevelsIn.size / 100;
18002 ls = fillLevelsIn.size;
18003 for(i = 0; i < 1e5; i++) {
18004 l = fillLevelsIn.start + i * ls;
18005 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
18006 if(l > zrange[0] && l < zrange[1]) fillLevels.push(l);
18007 }
18008 } else {
18009 fillLevels = lineLevels.map(function(v) {
18010 return v - levelsIn.size / 2;
18011 });
18012 fillLevels.push(fillLevels[fillLevels.length - 1] + levelsIn.size);
18013 }
18014 } else if(opts._fillcolor && typeof opts._fillcolor === 'string') {
18015 // doesn't matter what this value is, with a single value
18016 // we'll make a single fill rect covering the whole bar
18017 fillLevels = [0];
18018 }
18019
18020 if(levelsIn.size < 0) {
18021 lineLevels.reverse();
18022 fillLevels.reverse();
18023 }
18024
18025 return {line: lineLevels, fill: fillLevels};
18026}
18027
18028function mockColorBarAxis(gd, opts, zrange) {
18029 var fullLayout = gd._fullLayout;
18030
18031 var cbAxisIn = {
18032 type: 'linear',
18033 range: zrange,
18034 tickmode: opts.tickmode,
18035 nticks: opts.nticks,
18036 tick0: opts.tick0,
18037 dtick: opts.dtick,
18038 tickvals: opts.tickvals,
18039 ticktext: opts.ticktext,
18040 ticks: opts.ticks,
18041 ticklen: opts.ticklen,
18042 tickwidth: opts.tickwidth,
18043 tickcolor: opts.tickcolor,
18044 showticklabels: opts.showticklabels,
18045 tickfont: opts.tickfont,
18046 tickangle: opts.tickangle,
18047 tickformat: opts.tickformat,
18048 exponentformat: opts.exponentformat,
18049 separatethousands: opts.separatethousands,
18050 showexponent: opts.showexponent,
18051 showtickprefix: opts.showtickprefix,
18052 tickprefix: opts.tickprefix,
18053 showticksuffix: opts.showticksuffix,
18054 ticksuffix: opts.ticksuffix,
18055 title: opts.title,
18056 showline: true,
18057 anchor: 'free',
18058 side: 'right',
18059 position: 1
18060 };
18061
18062 var cbAxisOut = {
18063 type: 'linear',
18064 _id: 'y' + opts._id
18065 };
18066
18067 var axisOptions = {
18068 letter: 'y',
18069 font: fullLayout.font,
18070 noHover: true,
18071 noTickson: true,
18072 calendar: fullLayout.calendar // not really necessary (yet?)
18073 };
18074
18075 function coerce(attr, dflt) {
18076 return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt);
18077 }
18078
18079 handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout);
18080 handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions);
18081
18082 return cbAxisOut;
18083}
18084
18085module.exports = {
18086 draw: draw
18087};
18088
18089},{"../../constants/alignment":154,"../../lib":178,"../../lib/extend":173,"../../lib/setcursor":197,"../../lib/svg_text_utils":199,"../../plots/cartesian/axes":222,"../../plots/cartesian/axis_defaults":224,"../../plots/cartesian/layout_attributes":236,"../../plots/cartesian/position_defaults":239,"../../plots/plots":256,"../../registry":269,"../color":52,"../colorscale/helpers":63,"../dragelement":71,"../drawing":74,"../titles":147,"./constants":54,"d3":16,"tinycolor2":35}],57:[function(_dereq_,module,exports){
18090/**
18091* Copyright 2012-2020, Plotly, Inc.
18092* All rights reserved.
18093*
18094* This source code is licensed under the MIT license found in the
18095* LICENSE file in the root directory of this source tree.
18096*/
18097
18098
18099'use strict';
18100
18101var Lib = _dereq_('../../lib');
18102
18103
18104module.exports = function hasColorbar(container) {
18105 return Lib.isPlainObject(container.colorbar);
18106};
18107
18108},{"../../lib":178}],58:[function(_dereq_,module,exports){
18109/**
18110* Copyright 2012-2020, Plotly, Inc.
18111* All rights reserved.
18112*
18113* This source code is licensed under the MIT license found in the
18114* LICENSE file in the root directory of this source tree.
18115*/
18116
18117'use strict';
18118
18119module.exports = {
18120 moduleType: 'component',
18121 name: 'colorbar',
18122
18123 attributes: _dereq_('./attributes'),
18124 supplyDefaults: _dereq_('./defaults'),
18125
18126 draw: _dereq_('./draw').draw,
18127 hasColorbar: _dereq_('./has_colorbar')
18128};
18129
18130},{"./attributes":53,"./defaults":55,"./draw":56,"./has_colorbar":57}],59:[function(_dereq_,module,exports){
18131/**
18132* Copyright 2012-2020, Plotly, Inc.
18133* All rights reserved.
18134*
18135* This source code is licensed under the MIT license found in the
18136* LICENSE file in the root directory of this source tree.
18137*/
18138
18139'use strict';
18140
18141var colorbarAttrs = _dereq_('../colorbar/attributes');
18142var counterRegex = _dereq_('../../lib/regex').counter;
18143
18144var palettes = _dereq_('./scales.js').scales;
18145var paletteStr = Object.keys(palettes);
18146
18147function code(s) {
18148 return '`' + s + '`';
18149}
18150
18151/**
18152 * Make colorscale attribute declarations for
18153 *
18154 * - colorscale,
18155 * - (c|z)auto, (c|z)min, (c|z)max,
18156 * - autocolorscale, reversescale,
18157 * - showscale (optionally)
18158 * - color (optionally)
18159 *
18160 * @param {string} context (dflt: '', i.e. from trace root):
18161 * the container this is in ('', *marker*, *marker.line* etc)
18162 *
18163 * @param {object} opts:
18164 * - cLetter {string} (dflt: 'c'):
18165 * leading letter for 'min', 'max and 'auto' attribute (either 'z' or 'c')
18166 *
18167 * - colorAttr {string} (dflt: 'z' if `cLetter: 'z'`, 'color' if `cLetter: 'c'`):
18168 * (for descriptions) sets the name of the color attribute that maps to the colorscale.
18169 *
18170 * N.B. if `colorAttr: 'color'`, we include the `color` declaration here.
18171 *
18172 * - onlyIfNumerical {string} (dflt: false' if `cLetter: 'z'`, true if `cLetter: 'c'`):
18173 * (for descriptions) set to true if colorscale attribute only
18174 *
18175 * - colorscaleDflt {string}:
18176 * overrides the colorscale dflt
18177 *
18178 * - autoColorDflt {boolean} (dflt true):
18179 * normally autocolorscale.dflt is `true`, but pass `false` to override
18180 *
18181 * - noScale {boolean} (dflt: true if `context: 'marker.line'`, false otherwise):
18182 * set to `false` to not include showscale attribute (e.g. for 'marker.line')
18183 *
18184 * - showScaleDflt {boolean} (dflt: true if `cLetter: 'z'`, false otherwise)
18185 *
18186 * - editTypeOverride {boolean} (dflt: ''):
18187 * most of these attributes already require a recalc, but the ones that do not
18188 * have editType *style* or *plot* unless you override (presumably with *calc*)
18189 *
18190 * - anim {boolean) (dflt: undefined): is 'color' animatable?
18191 *
18192 * @return {object}
18193 */
18194module.exports = function colorScaleAttrs(context, opts) {
18195 context = context || '';
18196 opts = opts || {};
18197
18198 var cLetter = opts.cLetter || 'c';
18199 var onlyIfNumerical = ('onlyIfNumerical' in opts) ? opts.onlyIfNumerical : Boolean(context);
18200 var noScale = ('noScale' in opts) ? opts.noScale : context === 'marker.line';
18201 var showScaleDflt = ('showScaleDflt' in opts) ? opts.showScaleDflt : cLetter === 'z';
18202 var colorscaleDflt = typeof opts.colorscaleDflt === 'string' ? palettes[opts.colorscaleDflt] : null;
18203 var editTypeOverride = opts.editTypeOverride || '';
18204 var contextHead = context ? (context + '.') : '';
18205
18206 var colorAttr, colorAttrFull;
18207
18208 if('colorAttr' in opts) {
18209 colorAttr = opts.colorAttr;
18210 colorAttrFull = opts.colorAttr;
18211 } else {
18212 colorAttr = {z: 'z', c: 'color'}[cLetter];
18213 colorAttrFull = 'in ' + code(contextHead + colorAttr);
18214 }
18215
18216 var effectDesc = onlyIfNumerical ?
18217 ' Has an effect only if ' + colorAttrFull + 'is set to a numerical array.' :
18218 '';
18219
18220 var auto = cLetter + 'auto';
18221 var min = cLetter + 'min';
18222 var max = cLetter + 'max';
18223 var mid = cLetter + 'mid';
18224 var autoFull = code(contextHead + auto);
18225 var minFull = code(contextHead + min);
18226 var maxFull = code(contextHead + max);
18227 var minmaxFull = minFull + ' and ' + maxFull;
18228 var autoImpliedEdits = {};
18229 autoImpliedEdits[min] = autoImpliedEdits[max] = undefined;
18230 var minmaxImpliedEdits = {};
18231 minmaxImpliedEdits[auto] = false;
18232
18233 var attrs = {};
18234
18235 if(colorAttr === 'color') {
18236 attrs.color = {
18237 valType: 'color',
18238 arrayOk: true,
18239
18240 editType: editTypeOverride || 'style',
18241
18242 };
18243
18244 if(opts.anim) {
18245 attrs.color.anim = true;
18246 }
18247 }
18248
18249 attrs[auto] = {
18250 valType: 'boolean',
18251
18252 dflt: true,
18253 editType: 'calc',
18254 impliedEdits: autoImpliedEdits,
18255
18256 };
18257
18258 attrs[min] = {
18259 valType: 'number',
18260
18261 dflt: null,
18262 editType: editTypeOverride || 'plot',
18263 impliedEdits: minmaxImpliedEdits,
18264
18265 };
18266
18267 attrs[max] = {
18268 valType: 'number',
18269
18270 dflt: null,
18271 editType: editTypeOverride || 'plot',
18272 impliedEdits: minmaxImpliedEdits,
18273
18274 };
18275
18276 attrs[mid] = {
18277 valType: 'number',
18278
18279 dflt: null,
18280 editType: 'calc',
18281 impliedEdits: autoImpliedEdits,
18282
18283 };
18284
18285 attrs.colorscale = {
18286 valType: 'colorscale',
18287
18288 editType: 'calc',
18289 dflt: colorscaleDflt,
18290 impliedEdits: {autocolorscale: false},
18291
18292 };
18293
18294 attrs.autocolorscale = {
18295 valType: 'boolean',
18296
18297 // gets overrode in 'heatmap' & 'surface' for backwards comp.
18298 dflt: opts.autoColorDflt === false ? false : true,
18299 editType: 'calc',
18300 impliedEdits: {colorscale: undefined},
18301
18302 };
18303
18304 attrs.reversescale = {
18305 valType: 'boolean',
18306
18307 dflt: false,
18308 editType: 'plot',
18309
18310 };
18311
18312 if(!noScale) {
18313 attrs.showscale = {
18314 valType: 'boolean',
18315
18316 dflt: showScaleDflt,
18317 editType: 'calc',
18318
18319 };
18320
18321 attrs.colorbar = colorbarAttrs;
18322 }
18323
18324 if(!opts.noColorAxis) {
18325 attrs.coloraxis = {
18326 valType: 'subplotid',
18327
18328 regex: counterRegex('coloraxis'),
18329 dflt: null,
18330 editType: 'calc',
18331
18332 };
18333 }
18334
18335 return attrs;
18336};
18337
18338},{"../../lib/regex":193,"../colorbar/attributes":53,"./scales.js":67}],60:[function(_dereq_,module,exports){
18339/**
18340* Copyright 2012-2020, Plotly, Inc.
18341* All rights reserved.
18342*
18343* This source code is licensed under the MIT license found in the
18344* LICENSE file in the root directory of this source tree.
18345*/
18346
18347'use strict';
18348
18349var isNumeric = _dereq_('fast-isnumeric');
18350
18351var Lib = _dereq_('../../lib');
18352var extractOpts = _dereq_('./helpers').extractOpts;
18353
18354module.exports = function calc(gd, trace, opts) {
18355 var fullLayout = gd._fullLayout;
18356 var vals = opts.vals;
18357 var containerStr = opts.containerStr;
18358
18359 var container = containerStr ?
18360 Lib.nestedProperty(trace, containerStr).get() :
18361 trace;
18362
18363 var cOpts = extractOpts(container);
18364 var auto = cOpts.auto !== false;
18365 var min = cOpts.min;
18366 var max = cOpts.max;
18367 var mid = cOpts.mid;
18368
18369 var minVal = function() { return Lib.aggNums(Math.min, null, vals); };
18370 var maxVal = function() { return Lib.aggNums(Math.max, null, vals); };
18371
18372 if(min === undefined) {
18373 min = minVal();
18374 } else if(auto) {
18375 if(container._colorAx && isNumeric(min)) {
18376 min = Math.min(min, minVal());
18377 } else {
18378 min = minVal();
18379 }
18380 }
18381
18382 if(max === undefined) {
18383 max = maxVal();
18384 } else if(auto) {
18385 if(container._colorAx && isNumeric(max)) {
18386 max = Math.max(max, maxVal());
18387 } else {
18388 max = maxVal();
18389 }
18390 }
18391
18392 if(auto && mid !== undefined) {
18393 if(max - mid > mid - min) {
18394 min = mid - (max - mid);
18395 } else if(max - mid < mid - min) {
18396 max = mid + (mid - min);
18397 }
18398 }
18399
18400 if(min === max) {
18401 min -= 0.5;
18402 max += 0.5;
18403 }
18404
18405 cOpts._sync('min', min);
18406 cOpts._sync('max', max);
18407
18408 if(cOpts.autocolorscale) {
18409 var scl;
18410 if(min * max < 0) scl = fullLayout.colorscale.diverging;
18411 else if(min >= 0) scl = fullLayout.colorscale.sequential;
18412 else scl = fullLayout.colorscale.sequentialminus;
18413 cOpts._sync('colorscale', scl);
18414 }
18415};
18416
18417},{"../../lib":178,"./helpers":63,"fast-isnumeric":18}],61:[function(_dereq_,module,exports){
18418/**
18419* Copyright 2012-2020, Plotly, Inc.
18420* All rights reserved.
18421*
18422* This source code is licensed under the MIT license found in the
18423* LICENSE file in the root directory of this source tree.
18424*/
18425
18426'use strict';
18427
18428var Lib = _dereq_('../../lib');
18429var hasColorscale = _dereq_('./helpers').hasColorscale;
18430var extractOpts = _dereq_('./helpers').extractOpts;
18431
18432module.exports = function crossTraceDefaults(fullData, fullLayout) {
18433 function replace(cont, k) {
18434 var val = cont['_' + k];
18435 if(val !== undefined) {
18436 cont[k] = val;
18437 }
18438 }
18439
18440 function relinkColorAttrs(outerCont, cbOpt) {
18441 var cont = cbOpt.container ?
18442 Lib.nestedProperty(outerCont, cbOpt.container).get() :
18443 outerCont;
18444
18445 if(cont) {
18446 if(cont.coloraxis) {
18447 // stash ref to color axis
18448 cont._colorAx = fullLayout[cont.coloraxis];
18449 } else {
18450 var cOpts = extractOpts(cont);
18451 var isAuto = cOpts.auto;
18452
18453 if(isAuto || cOpts.min === undefined) {
18454 replace(cont, cbOpt.min);
18455 }
18456 if(isAuto || cOpts.max === undefined) {
18457 replace(cont, cbOpt.max);
18458 }
18459 if(cOpts.autocolorscale) {
18460 replace(cont, 'colorscale');
18461 }
18462 }
18463 }
18464 }
18465
18466 for(var i = 0; i < fullData.length; i++) {
18467 var trace = fullData[i];
18468 var cbOpts = trace._module.colorbar;
18469
18470 if(cbOpts) {
18471 if(Array.isArray(cbOpts)) {
18472 for(var j = 0; j < cbOpts.length; j++) {
18473 relinkColorAttrs(trace, cbOpts[j]);
18474 }
18475 } else {
18476 relinkColorAttrs(trace, cbOpts);
18477 }
18478 }
18479
18480 if(hasColorscale(trace, 'marker.line')) {
18481 relinkColorAttrs(trace, {
18482 container: 'marker.line',
18483 min: 'cmin',
18484 max: 'cmax'
18485 });
18486 }
18487 }
18488
18489 for(var k in fullLayout._colorAxes) {
18490 relinkColorAttrs(fullLayout[k], {min: 'cmin', max: 'cmax'});
18491 }
18492};
18493
18494},{"../../lib":178,"./helpers":63}],62:[function(_dereq_,module,exports){
18495/**
18496* Copyright 2012-2020, Plotly, Inc.
18497* All rights reserved.
18498*
18499* This source code is licensed under the MIT license found in the
18500* LICENSE file in the root directory of this source tree.
18501*/
18502
18503'use strict';
18504
18505var isNumeric = _dereq_('fast-isnumeric');
18506
18507var Lib = _dereq_('../../lib');
18508var hasColorbar = _dereq_('../colorbar/has_colorbar');
18509var colorbarDefaults = _dereq_('../colorbar/defaults');
18510
18511var isValidScale = _dereq_('./scales').isValid;
18512var traceIs = _dereq_('../../registry').traceIs;
18513
18514function npMaybe(parentCont, prefix) {
18515 var containerStr = prefix.slice(0, prefix.length - 1);
18516 return prefix ?
18517 Lib.nestedProperty(parentCont, containerStr).get() || {} :
18518 parentCont;
18519}
18520
18521/**
18522 * Colorscale / colorbar default handler
18523 *
18524 * @param {object} parentContIn : user (input) parent container (e.g. trace or layout coloraxis object)
18525 * @param {object} parentContOut : full parent container
18526 * @param {object} layout : (full) layout object
18527 * @param {fn} coerce : Lib.coerce wrapper
18528 * @param {object} opts :
18529 * - prefix {string} : attr string prefix to colorscale container from parent root
18530 * - cLetter {string} : 'c or 'z' color letter
18531 */
18532module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts) {
18533 var prefix = opts.prefix;
18534 var cLetter = opts.cLetter;
18535 var inTrace = '_module' in parentContOut;
18536 var containerIn = npMaybe(parentContIn, prefix);
18537 var containerOut = npMaybe(parentContOut, prefix);
18538 var template = npMaybe(parentContOut._template || {}, prefix) || {};
18539
18540 // colorScaleDefaults wrapper called if-ever we need to reset the colorscale
18541 // attributes for containers that were linked to invalid color axes
18542 var thisFn = function() {
18543 delete parentContIn.coloraxis;
18544 delete parentContOut.coloraxis;
18545 return colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts);
18546 };
18547
18548 if(inTrace) {
18549 var colorAxes = layout._colorAxes || {};
18550 var colorAx = coerce(prefix + 'coloraxis');
18551
18552 if(colorAx) {
18553 var colorbarVisuals = (
18554 traceIs(parentContOut, 'contour') &&
18555 Lib.nestedProperty(parentContOut, 'contours.coloring').get()
18556 ) || 'heatmap';
18557
18558 var stash = colorAxes[colorAx];
18559
18560 if(stash) {
18561 stash[2].push(thisFn);
18562
18563 if(stash[0] !== colorbarVisuals) {
18564 stash[0] = false;
18565 Lib.warn([
18566 'Ignoring coloraxis:', colorAx, 'setting',
18567 'as it is linked to incompatible colorscales.'
18568 ].join(' '));
18569 }
18570 } else {
18571 // stash:
18572 // - colorbar visual 'type'
18573 // - colorbar options to help in Colorbar.draw
18574 // - list of colorScaleDefaults wrapper functions
18575 colorAxes[colorAx] = [colorbarVisuals, parentContOut, [thisFn]];
18576 }
18577 return;
18578 }
18579 }
18580
18581 var minIn = containerIn[cLetter + 'min'];
18582 var maxIn = containerIn[cLetter + 'max'];
18583 var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn);
18584 var auto = coerce(prefix + cLetter + 'auto', !validMinMax);
18585
18586 if(auto) {
18587 coerce(prefix + cLetter + 'mid');
18588 } else {
18589 coerce(prefix + cLetter + 'min');
18590 coerce(prefix + cLetter + 'max');
18591 }
18592
18593 // handles both the trace case (autocolorscale is false by default) and
18594 // the marker and marker.line case (autocolorscale is true by default)
18595 var sclIn = containerIn.colorscale;
18596 var sclTemplate = template.colorscale;
18597 var autoColorscaleDflt;
18598 if(sclIn !== undefined) autoColorscaleDflt = !isValidScale(sclIn);
18599 if(sclTemplate !== undefined) autoColorscaleDflt = !isValidScale(sclTemplate);
18600 coerce(prefix + 'autocolorscale', autoColorscaleDflt);
18601
18602 coerce(prefix + 'colorscale');
18603 coerce(prefix + 'reversescale');
18604
18605 if(prefix !== 'marker.line.') {
18606 // handles both the trace case where the dflt is listed in attributes and
18607 // the marker case where the dflt is determined by hasColorbar
18608 var showScaleDflt;
18609 if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn);
18610
18611 var showScale = coerce(prefix + 'showscale', showScaleDflt);
18612 if(showScale) {
18613 if(prefix && template) containerOut._template = template;
18614 colorbarDefaults(containerIn, containerOut, layout);
18615 }
18616 }
18617};
18618
18619},{"../../lib":178,"../../registry":269,"../colorbar/defaults":55,"../colorbar/has_colorbar":57,"./scales":67,"fast-isnumeric":18}],63:[function(_dereq_,module,exports){
18620/**
18621* Copyright 2012-2020, Plotly, Inc.
18622* All rights reserved.
18623*
18624* This source code is licensed under the MIT license found in the
18625* LICENSE file in the root directory of this source tree.
18626*/
18627
18628'use strict';
18629
18630var d3 = _dereq_('d3');
18631var tinycolor = _dereq_('tinycolor2');
18632var isNumeric = _dereq_('fast-isnumeric');
18633
18634var Lib = _dereq_('../../lib');
18635var Color = _dereq_('../color');
18636
18637var isValidScale = _dereq_('./scales').isValid;
18638
18639function hasColorscale(trace, containerStr, colorKey) {
18640 var container = containerStr ?
18641 Lib.nestedProperty(trace, containerStr).get() || {} :
18642 trace;
18643 var color = container[colorKey || 'color'];
18644
18645 var isArrayWithOneNumber = false;
18646 if(Lib.isArrayOrTypedArray(color)) {
18647 for(var i = 0; i < color.length; i++) {
18648 if(isNumeric(color[i])) {
18649 isArrayWithOneNumber = true;
18650 break;
18651 }
18652 }
18653 }
18654
18655 return (
18656 Lib.isPlainObject(container) && (
18657 isArrayWithOneNumber ||
18658 container.showscale === true ||
18659 (isNumeric(container.cmin) && isNumeric(container.cmax)) ||
18660 isValidScale(container.colorscale) ||
18661 Lib.isPlainObject(container.colorbar)
18662 )
18663 );
18664}
18665
18666var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar'];
18667var letterAttrs = ['min', 'max', 'mid', 'auto'];
18668
18669/**
18670 * Extract 'c' / 'z', trace / color axis colorscale options
18671 *
18672 * Note that it would be nice to replace all z* with c* equivalents in v2
18673 *
18674 * @param {object} cont : attribute container
18675 * @return {object}:
18676 * - min: cmin or zmin
18677 * - max: cmax or zmax
18678 * - mid: cmid or zmid
18679 * - auto: cauto or zauto
18680 * - *scale: *scale attrs
18681 * - colorbar: colorbar
18682 * - _sync: function syncing attr and underscore dual (useful when calc'ing min/max)
18683 */
18684function extractOpts(cont) {
18685 var colorAx = cont._colorAx;
18686 var cont2 = colorAx ? colorAx : cont;
18687 var out = {};
18688 var cLetter;
18689 var i, k;
18690
18691 for(i = 0; i < constantAttrs.length; i++) {
18692 k = constantAttrs[i];
18693 out[k] = cont2[k];
18694 }
18695
18696 if(colorAx) {
18697 cLetter = 'c';
18698 for(i = 0; i < letterAttrs.length; i++) {
18699 k = letterAttrs[i];
18700 out[k] = cont2['c' + k];
18701 }
18702 } else {
18703 var k2;
18704 for(i = 0; i < letterAttrs.length; i++) {
18705 k = letterAttrs[i];
18706 k2 = 'c' + k;
18707 if(k2 in cont2) {
18708 out[k] = cont2[k2];
18709 continue;
18710 }
18711 k2 = 'z' + k;
18712 if(k2 in cont2) {
18713 out[k] = cont2[k2];
18714 }
18715 }
18716 cLetter = k2.charAt(0);
18717 }
18718
18719 out._sync = function(k, v) {
18720 var k2 = letterAttrs.indexOf(k) !== -1 ? cLetter + k : k;
18721 cont2[k2] = cont2['_' + k2] = v;
18722 };
18723
18724 return out;
18725}
18726
18727/**
18728 * Extract colorscale into numeric domain and color range.
18729 *
18730 * @param {object} cont colorscale container (e.g. trace, marker)
18731 * - colorscale {array of arrays}
18732 * - cmin/zmin {number}
18733 * - cmax/zmax {number}
18734 * - reversescale {boolean}
18735 *
18736 * @return {object}
18737 * - domain {array}
18738 * - range {array}
18739 */
18740function extractScale(cont) {
18741 var cOpts = extractOpts(cont);
18742 var cmin = cOpts.min;
18743 var cmax = cOpts.max;
18744
18745 var scl = cOpts.reversescale ?
18746 flipScale(cOpts.colorscale) :
18747 cOpts.colorscale;
18748
18749 var N = scl.length;
18750 var domain = new Array(N);
18751 var range = new Array(N);
18752
18753 for(var i = 0; i < N; i++) {
18754 var si = scl[i];
18755 domain[i] = cmin + si[0] * (cmax - cmin);
18756 range[i] = si[1];
18757 }
18758
18759 return {domain: domain, range: range};
18760}
18761
18762function flipScale(scl) {
18763 var N = scl.length;
18764 var sclNew = new Array(N);
18765
18766 for(var i = N - 1, j = 0; i >= 0; i--, j++) {
18767 var si = scl[i];
18768 sclNew[j] = [1 - si[0], si[1]];
18769 }
18770 return sclNew;
18771}
18772
18773/**
18774 * General colorscale function generator.
18775 *
18776 * @param {object} specs output of Colorscale.extractScale or precomputed domain, range.
18777 * - domain {array}
18778 * - range {array}
18779 *
18780 * @param {object} opts
18781 * - noNumericCheck {boolean} if true, scale func bypasses numeric checks
18782 * - returnArray {boolean} if true, scale func return 4-item array instead of color strings
18783 *
18784 * @return {function}
18785 */
18786function makeColorScaleFunc(specs, opts) {
18787 opts = opts || {};
18788
18789 var domain = specs.domain;
18790 var range = specs.range;
18791 var N = range.length;
18792 var _range = new Array(N);
18793
18794 for(var i = 0; i < N; i++) {
18795 var rgba = tinycolor(range[i]).toRgb();
18796 _range[i] = [rgba.r, rgba.g, rgba.b, rgba.a];
18797 }
18798
18799 var _sclFunc = d3.scale.linear()
18800 .domain(domain)
18801 .range(_range)
18802 .clamp(true);
18803
18804 var noNumericCheck = opts.noNumericCheck;
18805 var returnArray = opts.returnArray;
18806 var sclFunc;
18807
18808 if(noNumericCheck && returnArray) {
18809 sclFunc = _sclFunc;
18810 } else if(noNumericCheck) {
18811 sclFunc = function(v) {
18812 return colorArray2rbga(_sclFunc(v));
18813 };
18814 } else if(returnArray) {
18815 sclFunc = function(v) {
18816 if(isNumeric(v)) return _sclFunc(v);
18817 else if(tinycolor(v).isValid()) return v;
18818 else return Color.defaultLine;
18819 };
18820 } else {
18821 sclFunc = function(v) {
18822 if(isNumeric(v)) return colorArray2rbga(_sclFunc(v));
18823 else if(tinycolor(v).isValid()) return v;
18824 else return Color.defaultLine;
18825 };
18826 }
18827
18828 // colorbar draw looks into the d3 scale closure for domain and range
18829 sclFunc.domain = _sclFunc.domain;
18830 sclFunc.range = function() { return range; };
18831
18832 return sclFunc;
18833}
18834
18835function makeColorScaleFuncFromTrace(trace, opts) {
18836 return makeColorScaleFunc(extractScale(trace), opts);
18837}
18838
18839function colorArray2rbga(colorArray) {
18840 var colorObj = {
18841 r: colorArray[0],
18842 g: colorArray[1],
18843 b: colorArray[2],
18844 a: colorArray[3]
18845 };
18846
18847 return tinycolor(colorObj).toRgbString();
18848}
18849
18850module.exports = {
18851 hasColorscale: hasColorscale,
18852 extractOpts: extractOpts,
18853 extractScale: extractScale,
18854 flipScale: flipScale,
18855 makeColorScaleFunc: makeColorScaleFunc,
18856 makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace
18857};
18858
18859},{"../../lib":178,"../color":52,"./scales":67,"d3":16,"fast-isnumeric":18,"tinycolor2":35}],64:[function(_dereq_,module,exports){
18860/**
18861* Copyright 2012-2020, Plotly, Inc.
18862* All rights reserved.
18863*
18864* This source code is licensed under the MIT license found in the
18865* LICENSE file in the root directory of this source tree.
18866*/
18867
18868'use strict';
18869
18870var scales = _dereq_('./scales');
18871var helpers = _dereq_('./helpers');
18872
18873module.exports = {
18874 moduleType: 'component',
18875 name: 'colorscale',
18876
18877 attributes: _dereq_('./attributes'),
18878 layoutAttributes: _dereq_('./layout_attributes'),
18879
18880 supplyLayoutDefaults: _dereq_('./layout_defaults'),
18881 handleDefaults: _dereq_('./defaults'),
18882 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
18883
18884 calc: _dereq_('./calc'),
18885
18886 // ./scales.js is required in lib/coerce.js ;
18887 // it needs to be a seperate module to avoid circular a dependency
18888 scales: scales.scales,
18889 defaultScale: scales.defaultScale,
18890 getScale: scales.get,
18891 isValidScale: scales.isValid,
18892
18893 hasColorscale: helpers.hasColorscale,
18894 extractOpts: helpers.extractOpts,
18895 extractScale: helpers.extractScale,
18896 flipScale: helpers.flipScale,
18897 makeColorScaleFunc: helpers.makeColorScaleFunc,
18898 makeColorScaleFuncFromTrace: helpers.makeColorScaleFuncFromTrace
18899};
18900
18901},{"./attributes":59,"./calc":60,"./cross_trace_defaults":61,"./defaults":62,"./helpers":63,"./layout_attributes":65,"./layout_defaults":66,"./scales":67}],65:[function(_dereq_,module,exports){
18902/**
18903* Copyright 2012-2020, Plotly, Inc.
18904* All rights reserved.
18905*
18906* This source code is licensed under the MIT license found in the
18907* LICENSE file in the root directory of this source tree.
18908*/
18909
18910'use strict';
18911
18912var extendFlat = _dereq_('../../lib/extend').extendFlat;
18913
18914var colorScaleAttrs = _dereq_('./attributes');
18915var scales = _dereq_('./scales').scales;
18916
18917var msg = 'Note that `autocolorscale` must be true for this attribute to work.';
18918
18919module.exports = {
18920 editType: 'calc',
18921
18922 colorscale: {
18923 editType: 'calc',
18924
18925 sequential: {
18926 valType: 'colorscale',
18927 dflt: scales.Reds,
18928
18929 editType: 'calc',
18930
18931 },
18932 sequentialminus: {
18933 valType: 'colorscale',
18934 dflt: scales.Blues,
18935
18936 editType: 'calc',
18937
18938 },
18939 diverging: {
18940 valType: 'colorscale',
18941 dflt: scales.RdBu,
18942
18943 editType: 'calc',
18944
18945 }
18946 },
18947
18948 coloraxis: extendFlat({
18949 // not really a 'subplot' attribute container,
18950 // but this is the flag we use to denote attributes that
18951 // support yaxis, yaxis2, yaxis3, ... counters
18952 _isSubplotObj: true,
18953 editType: 'calc',
18954
18955 }, colorScaleAttrs('', {
18956 colorAttr: 'corresponding trace color array(s)',
18957 noColorAxis: true,
18958 showScaleDflt: true
18959 }))
18960};
18961
18962},{"../../lib/extend":173,"./attributes":59,"./scales":67}],66:[function(_dereq_,module,exports){
18963/**
18964* Copyright 2012-2020, Plotly, Inc.
18965* All rights reserved.
18966*
18967* This source code is licensed under the MIT license found in the
18968* LICENSE file in the root directory of this source tree.
18969*/
18970
18971'use strict';
18972
18973var Lib = _dereq_('../../lib');
18974var Template = _dereq_('../../plot_api/plot_template');
18975
18976var colorScaleAttrs = _dereq_('./layout_attributes');
18977var colorScaleDefaults = _dereq_('./defaults');
18978
18979module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
18980 function coerce(attr, dflt) {
18981 return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt);
18982 }
18983
18984 coerce('colorscale.sequential');
18985 coerce('colorscale.sequentialminus');
18986 coerce('colorscale.diverging');
18987
18988 var colorAxes = layoutOut._colorAxes;
18989 var colorAxIn, colorAxOut;
18990
18991 function coerceAx(attr, dflt) {
18992 return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt);
18993 }
18994
18995 for(var k in colorAxes) {
18996 var stash = colorAxes[k];
18997
18998 if(stash[0]) {
18999 colorAxIn = layoutIn[k] || {};
19000 colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis');
19001 colorAxOut._name = k;
19002 colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'});
19003 } else {
19004 // re-coerce colorscale attributes w/o coloraxis
19005 for(var i = 0; i < stash[2].length; i++) {
19006 stash[2][i]();
19007 }
19008 delete layoutOut._colorAxes[k];
19009 }
19010 }
19011};
19012
19013},{"../../lib":178,"../../plot_api/plot_template":212,"./defaults":62,"./layout_attributes":65}],67:[function(_dereq_,module,exports){
19014/**
19015* Copyright 2012-2020, Plotly, Inc.
19016* All rights reserved.
19017*
19018* This source code is licensed under the MIT license found in the
19019* LICENSE file in the root directory of this source tree.
19020*/
19021
19022'use strict';
19023
19024var tinycolor = _dereq_('tinycolor2');
19025
19026var scales = {
19027 'Greys': [
19028 [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']
19029 ],
19030
19031 'YlGnBu': [
19032 [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'],
19033 [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'],
19034 [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'],
19035 [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'],
19036 [1, 'rgb(255,255,217)']
19037 ],
19038
19039 'Greens': [
19040 [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'],
19041 [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'],
19042 [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'],
19043 [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'],
19044 [1, 'rgb(247,252,245)']
19045 ],
19046
19047 'YlOrRd': [
19048 [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'],
19049 [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'],
19050 [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'],
19051 [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'],
19052 [1, 'rgb(255,255,204)']
19053 ],
19054
19055 'Bluered': [
19056 [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']
19057 ],
19058
19059 // modified RdBu based on
19060 // http://www.kennethmoreland.com/color-maps/
19061 'RdBu': [
19062 [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'],
19063 [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'],
19064 [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)']
19065 ],
19066
19067 // Scale for non-negative numeric values
19068 'Reds': [
19069 [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'],
19070 [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)']
19071 ],
19072
19073 // Scale for non-positive numeric values
19074 'Blues': [
19075 [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'],
19076 [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'],
19077 [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)']
19078 ],
19079
19080 'Picnic': [
19081 [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'],
19082 [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'],
19083 [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'],
19084 [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'],
19085 [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'],
19086 [1, 'rgb(255,0,0)']
19087 ],
19088
19089 'Rainbow': [
19090 [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'],
19091 [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'],
19092 [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'],
19093 [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'],
19094 [1, 'rgb(255,0,0)']
19095 ],
19096
19097 'Portland': [
19098 [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'],
19099 [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'],
19100 [1, 'rgb(217,30,30)']
19101 ],
19102
19103 'Jet': [
19104 [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'],
19105 [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'],
19106 [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)']
19107 ],
19108
19109 'Hot': [
19110 [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'],
19111 [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)']
19112 ],
19113
19114 'Blackbody': [
19115 [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'],
19116 [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'],
19117 [1, 'rgb(160,200,255)']
19118 ],
19119
19120 'Earth': [
19121 [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'],
19122 [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'],
19123 [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)']
19124 ],
19125
19126 'Electric': [
19127 [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'],
19128 [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'],
19129 [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)']
19130 ],
19131
19132 'Viridis': [
19133 [0, '#440154'], [0.06274509803921569, '#48186a'],
19134 [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'],
19135 [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'],
19136 [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'],
19137 [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'],
19138 [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'],
19139 [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'],
19140 [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'],
19141 [1, '#fde725']
19142 ],
19143
19144 'Cividis': [
19145 [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'],
19146 [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'],
19147 [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'],
19148 [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'],
19149 [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'],
19150 [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'],
19151 [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'],
19152 [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'],
19153 [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)']
19154 ]
19155};
19156
19157var defaultScale = scales.RdBu;
19158
19159function getScale(scl, dflt) {
19160 if(!dflt) dflt = defaultScale;
19161 if(!scl) return dflt;
19162
19163 function parseScale() {
19164 try {
19165 scl = scales[scl] || JSON.parse(scl);
19166 } catch(e) {
19167 scl = dflt;
19168 }
19169 }
19170
19171 if(typeof scl === 'string') {
19172 parseScale();
19173 // occasionally scl is double-JSON encoded...
19174 if(typeof scl === 'string') parseScale();
19175 }
19176
19177 if(!isValidScaleArray(scl)) return dflt;
19178 return scl;
19179}
19180
19181
19182function isValidScaleArray(scl) {
19183 var highestVal = 0;
19184
19185 if(!Array.isArray(scl) || scl.length < 2) return false;
19186
19187 if(!scl[0] || !scl[scl.length - 1]) return false;
19188
19189 if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false;
19190
19191 for(var i = 0; i < scl.length; i++) {
19192 var si = scl[i];
19193
19194 if(si.length !== 2 || +si[0] < highestVal || !tinycolor(si[1]).isValid()) {
19195 return false;
19196 }
19197
19198 highestVal = +si[0];
19199 }
19200
19201 return true;
19202}
19203
19204function isValidScale(scl) {
19205 if(scales[scl] !== undefined) return true;
19206 else return isValidScaleArray(scl);
19207}
19208
19209module.exports = {
19210 scales: scales,
19211 defaultScale: defaultScale,
19212
19213 get: getScale,
19214 isValid: isValidScale
19215};
19216
19217},{"tinycolor2":35}],68:[function(_dereq_,module,exports){
19218/**
19219* Copyright 2012-2020, Plotly, Inc.
19220* All rights reserved.
19221*
19222* This source code is licensed under the MIT license found in the
19223* LICENSE file in the root directory of this source tree.
19224*/
19225
19226
19227'use strict';
19228
19229
19230// for automatic alignment on dragging, <1/3 means left align,
19231// >2/3 means right, and between is center. Pick the right fraction
19232// based on where you are, and return the fraction corresponding to
19233// that position on the object
19234module.exports = function align(v, dv, v0, v1, anchor) {
19235 var vmin = (v - v0) / (v1 - v0);
19236 var vmax = vmin + dv / (v1 - v0);
19237 var vc = (vmin + vmax) / 2;
19238
19239 // explicitly specified anchor
19240 if(anchor === 'left' || anchor === 'bottom') return vmin;
19241 if(anchor === 'center' || anchor === 'middle') return vc;
19242 if(anchor === 'right' || anchor === 'top') return vmax;
19243
19244 // automatic based on position
19245 if(vmin < (2 / 3) - vc) return vmin;
19246 if(vmax > (4 / 3) - vc) return vmax;
19247 return vc;
19248};
19249
19250},{}],69:[function(_dereq_,module,exports){
19251/**
19252* Copyright 2012-2020, Plotly, Inc.
19253* All rights reserved.
19254*
19255* This source code is licensed under the MIT license found in the
19256* LICENSE file in the root directory of this source tree.
19257*/
19258
19259
19260'use strict';
19261
19262var Lib = _dereq_('../../lib');
19263
19264
19265// set cursors pointing toward the closest corner/side,
19266// to indicate alignment
19267// x and y are 0-1, fractions of the plot area
19268var cursorset = [
19269 ['sw-resize', 's-resize', 'se-resize'],
19270 ['w-resize', 'move', 'e-resize'],
19271 ['nw-resize', 'n-resize', 'ne-resize']
19272];
19273
19274module.exports = function getCursor(x, y, xanchor, yanchor) {
19275 if(xanchor === 'left') x = 0;
19276 else if(xanchor === 'center') x = 1;
19277 else if(xanchor === 'right') x = 2;
19278 else x = Lib.constrain(Math.floor(x * 3), 0, 2);
19279
19280 if(yanchor === 'bottom') y = 0;
19281 else if(yanchor === 'middle') y = 1;
19282 else if(yanchor === 'top') y = 2;
19283 else y = Lib.constrain(Math.floor(y * 3), 0, 2);
19284
19285 return cursorset[y][x];
19286};
19287
19288},{"../../lib":178}],70:[function(_dereq_,module,exports){
19289/**
19290* Copyright 2012-2020, Plotly, Inc.
19291* All rights reserved.
19292*
19293* This source code is licensed under the MIT license found in the
19294* LICENSE file in the root directory of this source tree.
19295*/
19296
19297'use strict';
19298
19299exports.selectMode = function(dragmode) {
19300 return (
19301 dragmode === 'lasso' ||
19302 dragmode === 'select'
19303 );
19304};
19305
19306exports.drawMode = function(dragmode) {
19307 return (
19308 dragmode === 'drawclosedpath' ||
19309 dragmode === 'drawopenpath' ||
19310 dragmode === 'drawline' ||
19311 dragmode === 'drawrect' ||
19312 dragmode === 'drawcircle'
19313 );
19314};
19315
19316exports.openMode = function(dragmode) {
19317 return (
19318 dragmode === 'drawline' ||
19319 dragmode === 'drawopenpath'
19320 );
19321};
19322
19323exports.rectMode = function(dragmode) {
19324 return (
19325 dragmode === 'select' ||
19326 dragmode === 'drawline' ||
19327 dragmode === 'drawrect' ||
19328 dragmode === 'drawcircle'
19329 );
19330};
19331
19332exports.freeMode = function(dragmode) {
19333 return (
19334 dragmode === 'lasso' ||
19335 dragmode === 'drawclosedpath' ||
19336 dragmode === 'drawopenpath'
19337 );
19338};
19339
19340exports.selectingOrDrawing = function(dragmode) {
19341 return (
19342 exports.freeMode(dragmode) ||
19343 exports.rectMode(dragmode)
19344 );
19345};
19346
19347},{}],71:[function(_dereq_,module,exports){
19348/**
19349* Copyright 2012-2020, Plotly, Inc.
19350* All rights reserved.
19351*
19352* This source code is licensed under the MIT license found in the
19353* LICENSE file in the root directory of this source tree.
19354*/
19355
19356'use strict';
19357
19358var mouseOffset = _dereq_('mouse-event-offset');
19359var hasHover = _dereq_('has-hover');
19360var supportsPassive = _dereq_('has-passive-events');
19361
19362var removeElement = _dereq_('../../lib').removeElement;
19363var constants = _dereq_('../../plots/cartesian/constants');
19364
19365var dragElement = module.exports = {};
19366
19367dragElement.align = _dereq_('./align');
19368dragElement.getCursor = _dereq_('./cursor');
19369
19370var unhover = _dereq_('./unhover');
19371dragElement.unhover = unhover.wrapped;
19372dragElement.unhoverRaw = unhover.raw;
19373
19374/**
19375 * Abstracts click & drag interactions
19376 *
19377 * During the interaction, a "coverSlip" element - a transparent
19378 * div covering the whole page - is created, which has two key effects:
19379 * - Lets you drag beyond the boundaries of the plot itself without
19380 * dropping (but if you drag all the way out of the browser window the
19381 * interaction will end)
19382 * - Freezes the cursor: whatever mouse cursor the drag element had when the
19383 * interaction started gets copied to the coverSlip for use until mouseup
19384 *
19385 * If the user executes a drag bigger than MINDRAG, callbacks will fire as:
19386 * prepFn, moveFn (1 or more times), doneFn
19387 * If the user does not drag enough, prepFn and clickFn will fire.
19388 *
19389 * Note: If you cancel contextmenu, clickFn will fire even with a right click
19390 * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
19391 * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
19392 * TODO: we should probably turn this into a `config` parameter, so we can fix it
19393 * such that if you *don't* cancel contextmenu, we can prevent partial drags, which
19394 * put you in a weird state.
19395 *
19396 * If the user clicks multiple times quickly, clickFn will fire each time
19397 * but numClicks will increase to help you recognize doubleclicks.
19398 *
19399 * @param {object} options with keys:
19400 * element (required) the DOM element to drag
19401 * prepFn (optional) function(event, startX, startY)
19402 * executed on mousedown
19403 * startX and startY are the clientX and clientY pixel position
19404 * of the mousedown event
19405 * moveFn (optional) function(dx, dy)
19406 * executed on move, ONLY after we've exceeded MINDRAG
19407 * (we keep executing moveFn if you move back to where you started)
19408 * dx and dy are the net pixel offset of the drag,
19409 * dragged is true/false, has the mouse moved enough to
19410 * constitute a drag
19411 * doneFn (optional) function(e)
19412 * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
19413 * sure that moveFn has been called at least once)
19414 * numClicks is how many clicks we've registered within
19415 * a doubleclick time
19416 * e is the original mouseup event
19417 * clickFn (optional) function(numClicks, e)
19418 * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
19419 * has not been called at all)
19420 * numClicks is how many clicks we've registered within
19421 * a doubleclick time
19422 * e is the original mousedown event
19423 * clampFn (optional, function(dx, dy) return [dx2, dy2])
19424 * Provide custom clamping function for small displacements.
19425 * By default, clamping is done using `minDrag` to x and y displacements
19426 * independently.
19427 */
19428dragElement.init = function init(options) {
19429 var gd = options.gd;
19430 var numClicks = 1;
19431 var doubleClickDelay = gd._context.doubleClickDelay;
19432 var element = options.element;
19433
19434 var startX,
19435 startY,
19436 newMouseDownTime,
19437 cursor,
19438 dragCover,
19439 initialEvent,
19440 initialTarget,
19441 rightClick;
19442
19443 if(!gd._mouseDownTime) gd._mouseDownTime = 0;
19444
19445 element.style.pointerEvents = 'all';
19446
19447 element.onmousedown = onStart;
19448
19449 if(!supportsPassive) {
19450 element.ontouchstart = onStart;
19451 } else {
19452 if(element._ontouchstart) {
19453 element.removeEventListener('touchstart', element._ontouchstart);
19454 }
19455 element._ontouchstart = onStart;
19456 element.addEventListener('touchstart', onStart, {passive: false});
19457 }
19458
19459 function _clampFn(dx, dy, minDrag) {
19460 if(Math.abs(dx) < minDrag) dx = 0;
19461 if(Math.abs(dy) < minDrag) dy = 0;
19462 return [dx, dy];
19463 }
19464
19465 var clampFn = options.clampFn || _clampFn;
19466
19467 function onStart(e) {
19468 // make dragging and dragged into properties of gd
19469 // so that others can look at and modify them
19470 gd._dragged = false;
19471 gd._dragging = true;
19472 var offset = pointerOffset(e);
19473 startX = offset[0];
19474 startY = offset[1];
19475 initialTarget = e.target;
19476 initialEvent = e;
19477 rightClick = e.buttons === 2 || e.ctrlKey;
19478
19479 // fix Fx.hover for touch events
19480 if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') {
19481 e.clientX = startX;
19482 e.clientY = startY;
19483 }
19484
19485 newMouseDownTime = (new Date()).getTime();
19486 if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) {
19487 // in a click train
19488 numClicks += 1;
19489 } else {
19490 // new click train
19491 numClicks = 1;
19492 gd._mouseDownTime = newMouseDownTime;
19493 }
19494
19495 if(options.prepFn) options.prepFn(e, startX, startY);
19496
19497 if(hasHover && !rightClick) {
19498 dragCover = coverSlip();
19499 dragCover.style.cursor = window.getComputedStyle(element).cursor;
19500 } else if(!hasHover) {
19501 // document acts as a dragcover for mobile, bc we can't create dragcover dynamically
19502 dragCover = document;
19503 cursor = window.getComputedStyle(document.documentElement).cursor;
19504 document.documentElement.style.cursor = window.getComputedStyle(element).cursor;
19505 }
19506
19507 document.addEventListener('mouseup', onDone);
19508 document.addEventListener('touchend', onDone);
19509
19510 if(options.dragmode !== false) {
19511 e.preventDefault();
19512 document.addEventListener('mousemove', onMove);
19513 document.addEventListener('touchmove', onMove, {passive: false});
19514 }
19515
19516 return;
19517 }
19518
19519 function onMove(e) {
19520 e.preventDefault();
19521
19522 var offset = pointerOffset(e);
19523 var minDrag = options.minDrag || constants.MINDRAG;
19524 var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag);
19525 var dx = dxdy[0];
19526 var dy = dxdy[1];
19527
19528 if(dx || dy) {
19529 gd._dragged = true;
19530 dragElement.unhover(gd);
19531 }
19532
19533 if(gd._dragged && options.moveFn && !rightClick) {
19534 gd._dragdata = {
19535 element: element,
19536 dx: dx,
19537 dy: dy
19538 };
19539 options.moveFn(dx, dy);
19540 }
19541
19542 return;
19543 }
19544
19545 function onDone(e) {
19546 delete gd._dragdata;
19547
19548 if(options.dragmode !== false) {
19549 e.preventDefault();
19550 document.removeEventListener('mousemove', onMove);
19551 document.removeEventListener('touchmove', onMove);
19552 }
19553
19554 document.removeEventListener('mouseup', onDone);
19555 document.removeEventListener('touchend', onDone);
19556
19557 if(hasHover) {
19558 removeElement(dragCover);
19559 } else if(cursor) {
19560 dragCover.documentElement.style.cursor = cursor;
19561 cursor = null;
19562 }
19563
19564 if(!gd._dragging) {
19565 gd._dragged = false;
19566 return;
19567 }
19568 gd._dragging = false;
19569
19570 // don't count as a dblClick unless the mouseUp is also within
19571 // the dblclick delay
19572 if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) {
19573 numClicks = Math.max(numClicks - 1, 1);
19574 }
19575
19576 if(gd._dragged) {
19577 if(options.doneFn) options.doneFn();
19578 } else {
19579 if(options.clickFn) options.clickFn(numClicks, initialEvent);
19580
19581 // If we haven't dragged, this should be a click. But because of the
19582 // coverSlip changing the element, the natural system might not generate one,
19583 // so we need to make our own. But right clicks don't normally generate
19584 // click events, only contextmenu events, which happen on mousedown.
19585 if(!rightClick) {
19586 var e2;
19587
19588 try {
19589 e2 = new MouseEvent('click', e);
19590 } catch(err) {
19591 var offset = pointerOffset(e);
19592 e2 = document.createEvent('MouseEvents');
19593 e2.initMouseEvent('click',
19594 e.bubbles, e.cancelable,
19595 e.view, e.detail,
19596 e.screenX, e.screenY,
19597 offset[0], offset[1],
19598 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
19599 e.button, e.relatedTarget);
19600 }
19601
19602 initialTarget.dispatchEvent(e2);
19603 }
19604 }
19605
19606 gd._dragging = false;
19607 gd._dragged = false;
19608 return;
19609 }
19610};
19611
19612function coverSlip() {
19613 var cover = document.createElement('div');
19614
19615 cover.className = 'dragcover';
19616 var cStyle = cover.style;
19617 cStyle.position = 'fixed';
19618 cStyle.left = 0;
19619 cStyle.right = 0;
19620 cStyle.top = 0;
19621 cStyle.bottom = 0;
19622 cStyle.zIndex = 999999999;
19623 cStyle.background = 'none';
19624
19625 document.body.appendChild(cover);
19626
19627 return cover;
19628}
19629
19630dragElement.coverSlip = coverSlip;
19631
19632function pointerOffset(e) {
19633 return mouseOffset(
19634 e.changedTouches ? e.changedTouches[0] : e,
19635 document.body
19636 );
19637}
19638
19639},{"../../lib":178,"../../plots/cartesian/constants":228,"./align":68,"./cursor":69,"./unhover":72,"has-hover":20,"has-passive-events":21,"mouse-event-offset":24}],72:[function(_dereq_,module,exports){
19640/**
19641* Copyright 2012-2020, Plotly, Inc.
19642* All rights reserved.
19643*
19644* This source code is licensed under the MIT license found in the
19645* LICENSE file in the root directory of this source tree.
19646*/
19647
19648'use strict';
19649
19650var Events = _dereq_('../../lib/events');
19651var throttle = _dereq_('../../lib/throttle');
19652var getGraphDiv = _dereq_('../../lib/dom').getGraphDiv;
19653
19654var hoverConstants = _dereq_('../fx/constants');
19655
19656var unhover = module.exports = {};
19657
19658unhover.wrapped = function(gd, evt, subplot) {
19659 gd = getGraphDiv(gd);
19660
19661 // Important, clear any queued hovers
19662 if(gd._fullLayout) {
19663 throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID);
19664 }
19665
19666 unhover.raw(gd, evt, subplot);
19667};
19668
19669
19670// remove hover effects on mouse out, and emit unhover event
19671unhover.raw = function raw(gd, evt) {
19672 var fullLayout = gd._fullLayout;
19673 var oldhoverdata = gd._hoverdata;
19674
19675 if(!evt) evt = {};
19676 if(evt.target &&
19677 Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
19678 return;
19679 }
19680
19681 fullLayout._hoverlayer.selectAll('g').remove();
19682 fullLayout._hoverlayer.selectAll('line').remove();
19683 fullLayout._hoverlayer.selectAll('circle').remove();
19684 gd._hoverdata = undefined;
19685
19686 if(evt.target && oldhoverdata) {
19687 gd.emit('plotly_unhover', {
19688 event: evt,
19689 points: oldhoverdata
19690 });
19691 }
19692};
19693
19694},{"../../lib/dom":171,"../../lib/events":172,"../../lib/throttle":200,"../fx/constants":86}],73:[function(_dereq_,module,exports){
19695/**
19696* Copyright 2012-2020, Plotly, Inc.
19697* All rights reserved.
19698*
19699* This source code is licensed under the MIT license found in the
19700* LICENSE file in the root directory of this source tree.
19701*/
19702
19703
19704'use strict';
19705
19706exports.dash = {
19707 valType: 'string',
19708 // string type usually doesn't take values... this one should really be
19709 // a special type or at least a special coercion function, from the GUI
19710 // you only get these values but elsewhere the user can supply a list of
19711 // dash lengths in px, and it will be honored
19712 values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'],
19713 dflt: 'solid',
19714
19715 editType: 'style',
19716
19717};
19718
19719},{}],74:[function(_dereq_,module,exports){
19720/**
19721* Copyright 2012-2020, Plotly, Inc.
19722* All rights reserved.
19723*
19724* This source code is licensed under the MIT license found in the
19725* LICENSE file in the root directory of this source tree.
19726*/
19727
19728
19729'use strict';
19730
19731var d3 = _dereq_('d3');
19732var isNumeric = _dereq_('fast-isnumeric');
19733var tinycolor = _dereq_('tinycolor2');
19734
19735var Registry = _dereq_('../../registry');
19736var Color = _dereq_('../color');
19737var Colorscale = _dereq_('../colorscale');
19738var Lib = _dereq_('../../lib');
19739var svgTextUtils = _dereq_('../../lib/svg_text_utils');
19740
19741var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
19742var alignment = _dereq_('../../constants/alignment');
19743var LINE_SPACING = alignment.LINE_SPACING;
19744var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM;
19745
19746var subTypes = _dereq_('../../traces/scatter/subtypes');
19747var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func');
19748var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue;
19749
19750var drawing = module.exports = {};
19751
19752// -----------------------------------------------------
19753// styling functions for plot elements
19754// -----------------------------------------------------
19755
19756drawing.font = function(s, family, size, color) {
19757 // also allow the form font(s, {family, size, color})
19758 if(Lib.isPlainObject(family)) {
19759 color = family.color;
19760 size = family.size;
19761 family = family.family;
19762 }
19763 if(family) s.style('font-family', family);
19764 if(size + 1) s.style('font-size', size + 'px');
19765 if(color) s.call(Color.fill, color);
19766};
19767
19768/*
19769 * Positioning helpers
19770 * Note: do not use `setPosition` with <text> nodes modified by
19771 * `svgTextUtils.convertToTspans`. Use `svgTextUtils.positionText`
19772 * instead, so that <tspan.line> elements get updated to match.
19773 */
19774drawing.setPosition = function(s, x, y) { s.attr('x', x).attr('y', y); };
19775drawing.setSize = function(s, w, h) { s.attr('width', w).attr('height', h); };
19776drawing.setRect = function(s, x, y, w, h) {
19777 s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
19778};
19779
19780/** Translate node
19781 *
19782 * @param {object} d : calcdata point item
19783 * @param {sel} sel : d3 selction of node to translate
19784 * @param {object} xa : corresponding full xaxis object
19785 * @param {object} ya : corresponding full yaxis object
19786 *
19787 * @return {boolean} :
19788 * true if selection got translated
19789 * false if selection could not get translated
19790 */
19791drawing.translatePoint = function(d, sel, xa, ya) {
19792 var x = xa.c2p(d.x);
19793 var y = ya.c2p(d.y);
19794
19795 if(isNumeric(x) && isNumeric(y) && sel.node()) {
19796 // for multiline text this works better
19797 if(sel.node().nodeName === 'text') {
19798 sel.attr('x', x).attr('y', y);
19799 } else {
19800 sel.attr('transform', 'translate(' + x + ',' + y + ')');
19801 }
19802 } else {
19803 return false;
19804 }
19805
19806 return true;
19807};
19808
19809drawing.translatePoints = function(s, xa, ya) {
19810 s.each(function(d) {
19811 var sel = d3.select(this);
19812 drawing.translatePoint(d, sel, xa, ya);
19813 });
19814};
19815
19816drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
19817 sel.attr(
19818 'display',
19819 (xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none'
19820 );
19821};
19822
19823drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
19824 if(!subplot._hasClipOnAxisFalse) return;
19825
19826 var xa = subplot.xaxis;
19827 var ya = subplot.yaxis;
19828
19829 traceGroups.each(function(d) {
19830 var trace = d[0].trace;
19831 var xcalendar = trace.xcalendar;
19832 var ycalendar = trace.ycalendar;
19833 var selector = Registry.traceIs(trace, 'bar-like') ? '.bartext' : '.point,.textpoint';
19834
19835 traceGroups.selectAll(selector).each(function(d) {
19836 drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
19837 });
19838 });
19839};
19840
19841drawing.crispRound = function(gd, lineWidth, dflt) {
19842 // for lines that disable antialiasing we want to
19843 // make sure the width is an integer, and at least 1 if it's nonzero
19844
19845 if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0;
19846
19847 // but not for static plots - these don't get antialiased anyway.
19848 if(gd._context.staticPlot) return lineWidth;
19849
19850 if(lineWidth < 1) return 1;
19851 return Math.round(lineWidth);
19852};
19853
19854drawing.singleLineStyle = function(d, s, lw, lc, ld) {
19855 s.style('fill', 'none');
19856 var line = (((d || [])[0] || {}).trace || {}).line || {};
19857 var lw1 = lw || line.width || 0;
19858 var dash = ld || line.dash || '';
19859
19860 Color.stroke(s, lc || line.color);
19861 drawing.dashLine(s, dash, lw1);
19862};
19863
19864drawing.lineGroupStyle = function(s, lw, lc, ld) {
19865 s.style('fill', 'none')
19866 .each(function(d) {
19867 var line = (((d || [])[0] || {}).trace || {}).line || {};
19868 var lw1 = lw || line.width || 0;
19869 var dash = ld || line.dash || '';
19870
19871 d3.select(this)
19872 .call(Color.stroke, lc || line.color)
19873 .call(drawing.dashLine, dash, lw1);
19874 });
19875};
19876
19877drawing.dashLine = function(s, dash, lineWidth) {
19878 lineWidth = +lineWidth || 0;
19879
19880 dash = drawing.dashStyle(dash, lineWidth);
19881
19882 s.style({
19883 'stroke-dasharray': dash,
19884 'stroke-width': lineWidth + 'px'
19885 });
19886};
19887
19888drawing.dashStyle = function(dash, lineWidth) {
19889 lineWidth = +lineWidth || 1;
19890 var dlw = Math.max(lineWidth, 3);
19891
19892 if(dash === 'solid') dash = '';
19893 else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
19894 else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
19895 else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
19896 else if(dash === 'dashdot') {
19897 dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
19898 } else if(dash === 'longdashdot') {
19899 dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
19900 }
19901 // otherwise user wrote the dasharray themselves - leave it be
19902
19903 return dash;
19904};
19905
19906// Same as fillGroupStyle, except in this case the selection may be a transition
19907drawing.singleFillStyle = function(sel) {
19908 var node = d3.select(sel.node());
19909 var data = node.data();
19910 var fillcolor = (((data[0] || [])[0] || {}).trace || {}).fillcolor;
19911 if(fillcolor) {
19912 sel.call(Color.fill, fillcolor);
19913 }
19914};
19915
19916drawing.fillGroupStyle = function(s) {
19917 s.style('stroke-width', 0)
19918 .each(function(d) {
19919 var shape = d3.select(this);
19920 // N.B. 'd' won't be a calcdata item when
19921 // fill !== 'none' on a segment-less and marker-less trace
19922 if(d[0].trace) {
19923 shape.call(Color.fill, d[0].trace.fillcolor);
19924 }
19925 });
19926};
19927
19928var SYMBOLDEFS = _dereq_('./symbol_defs');
19929
19930drawing.symbolNames = [];
19931drawing.symbolFuncs = [];
19932drawing.symbolNeedLines = {};
19933drawing.symbolNoDot = {};
19934drawing.symbolNoFill = {};
19935drawing.symbolList = [];
19936
19937Object.keys(SYMBOLDEFS).forEach(function(k) {
19938 var symDef = SYMBOLDEFS[k];
19939 var n = symDef.n;
19940 drawing.symbolList.push(
19941 n,
19942 k,
19943 n + 100,
19944 k + '-open'
19945 );
19946 drawing.symbolNames[n] = k;
19947 drawing.symbolFuncs[n] = symDef.f;
19948
19949 if(symDef.needLine) {
19950 drawing.symbolNeedLines[n] = true;
19951 }
19952 if(symDef.noDot) {
19953 drawing.symbolNoDot[n] = true;
19954 } else {
19955 drawing.symbolList.push(
19956 n + 200,
19957 k + '-dot',
19958 n + 300,
19959 k + '-open-dot'
19960 );
19961 }
19962 if(symDef.noFill) {
19963 drawing.symbolNoFill[n] = true;
19964 }
19965});
19966
19967var MAXSYMBOL = drawing.symbolNames.length;
19968// add a dot in the middle of the symbol
19969var DOTPATH = 'M0,0.5L0.5,0L0,-0.5L-0.5,0Z';
19970
19971drawing.symbolNumber = function(v) {
19972 if(typeof v === 'string') {
19973 var vbase = 0;
19974 if(v.indexOf('-open') > 0) {
19975 vbase = 100;
19976 v = v.replace('-open', '');
19977 }
19978 if(v.indexOf('-dot') > 0) {
19979 vbase += 200;
19980 v = v.replace('-dot', '');
19981 }
19982 v = drawing.symbolNames.indexOf(v);
19983 if(v >= 0) { v += vbase; }
19984 }
19985
19986 return (v % 100 >= MAXSYMBOL || v >= 400) ?
19987 0 : Math.floor(Math.max(v, 0));
19988};
19989
19990function makePointPath(symbolNumber, r) {
19991 var base = symbolNumber % 100;
19992 return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
19993}
19994
19995var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
19996var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0};
19997var stopFormatter = d3.format('~.1f');
19998var gradientInfo = {
19999 radial: {node: 'radialGradient'},
20000 radialreversed: {node: 'radialGradient', reversed: true},
20001 horizontal: {node: 'linearGradient', attrs: HORZGRADIENT},
20002 horizontalreversed: {node: 'linearGradient', attrs: HORZGRADIENT, reversed: true},
20003 vertical: {node: 'linearGradient', attrs: VERTGRADIENT},
20004 verticalreversed: {node: 'linearGradient', attrs: VERTGRADIENT, reversed: true}
20005};
20006
20007/**
20008 * gradient: create and apply a gradient fill
20009 *
20010 * @param {object} sel: d3 selection to apply this gradient to
20011 * You can use `selection.call(Drawing.gradient, ...)`
20012 * @param {DOM element} gd: the graph div `sel` is part of
20013 * @param {string} gradientID: a unique (within this plot) identifier
20014 * for this gradient, so that we don't create unnecessary definitions
20015 * @param {string} type: 'radial', 'horizontal', or 'vertical', optionally with
20016 * 'reversed' at the end. Normally radial goes center to edge,
20017 * horizontal goes right to left, and vertical goes bottom to top
20018 * @param {array} colorscale: as in attribute values, [[fraction, color], ...]
20019 * @param {string} prop: the property to apply to, 'fill' or 'stroke'
20020 */
20021drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
20022 var len = colorscale.length;
20023 var info = gradientInfo[type];
20024 var colorStops = new Array(len);
20025 for(var i = 0; i < len; i++) {
20026 if(info.reversed) {
20027 colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]];
20028 } else {
20029 colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]];
20030 }
20031 }
20032
20033 var fullLayout = gd._fullLayout;
20034 var fullID = 'g' + fullLayout._uid + '-' + gradientID;
20035
20036 var gradient = fullLayout._defs.select('.gradients')
20037 .selectAll('#' + fullID)
20038 .data([type + colorStops.join(';')], Lib.identity);
20039
20040 gradient.exit().remove();
20041
20042 gradient.enter()
20043 .append(info.node)
20044 .each(function() {
20045 var el = d3.select(this);
20046 if(info.attrs) el.attr(info.attrs);
20047
20048 el.attr('id', fullID);
20049
20050 var stops = el.selectAll('stop')
20051 .data(colorStops);
20052 stops.exit().remove();
20053 stops.enter().append('stop');
20054
20055 stops.each(function(d) {
20056 var tc = tinycolor(d[1]);
20057 d3.select(this).attr({
20058 offset: d[0] + '%',
20059 'stop-color': Color.tinyRGB(tc),
20060 'stop-opacity': tc.getAlpha()
20061 });
20062 });
20063 });
20064
20065 sel.style(prop, getFullUrl(fullID, gd))
20066 .style(prop + '-opacity', null);
20067
20068 var className2query = function(s) {
20069 return '.' + s.attr('class').replace(/\s/g, '.');
20070 };
20071 var k = className2query(d3.select(sel.node().parentNode)) +
20072 '>' + className2query(sel);
20073 fullLayout._gradientUrlQueryParts[k] = 1;
20074};
20075
20076/*
20077 * Make the gradients container and clear out any previous gradients.
20078 * We never collect all the gradients we need in one place,
20079 * so we can't ever remove gradients that have stopped being useful,
20080 * except all at once before a full redraw.
20081 * The upside of this is arbitrary points can share gradient defs
20082 */
20083drawing.initGradients = function(gd) {
20084 var fullLayout = gd._fullLayout;
20085
20086 var gradientsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'gradients');
20087 gradientsGroup.selectAll('linearGradient,radialGradient').remove();
20088
20089 // initialize stash of query parts filled in Drawing.gradient,
20090 // used to fix URL strings during image exports
20091 fullLayout._gradientUrlQueryParts = {};
20092};
20093
20094
20095drawing.pointStyle = function(s, trace, gd) {
20096 if(!s.size()) return;
20097
20098 var fns = drawing.makePointStyleFns(trace);
20099
20100 s.each(function(d) {
20101 drawing.singlePointStyle(d, d3.select(this), trace, fns, gd);
20102 });
20103};
20104
20105drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
20106 var marker = trace.marker;
20107 var markerLine = marker.line;
20108
20109 sel.style('opacity',
20110 fns.selectedOpacityFn ? fns.selectedOpacityFn(d) :
20111 (d.mo === undefined ? marker.opacity : d.mo)
20112 );
20113
20114 if(fns.ms2mrc) {
20115 var r;
20116
20117 // handle multi-trace graph edit case
20118 if(d.ms === 'various' || marker.size === 'various') {
20119 r = 3;
20120 } else {
20121 r = fns.ms2mrc(d.ms);
20122 }
20123
20124 // store the calculated size so hover can use it
20125 d.mrc = r;
20126
20127 if(fns.selectedSizeFn) {
20128 r = d.mrc = fns.selectedSizeFn(d);
20129 }
20130
20131 // turn the symbol into a sanitized number
20132 var x = drawing.symbolNumber(d.mx || marker.symbol) || 0;
20133
20134 // save if this marker is open
20135 // because that impacts how to handle colors
20136 d.om = x % 200 >= 100;
20137
20138 sel.attr('d', makePointPath(x, r));
20139 }
20140
20141 var perPointGradient = false;
20142 var fillColor, lineColor, lineWidth;
20143
20144 // 'so' is suspected outliers, for box plots
20145 if(d.so) {
20146 lineWidth = markerLine.outlierwidth;
20147 lineColor = markerLine.outliercolor;
20148 fillColor = marker.outliercolor;
20149 } else {
20150 var markerLineWidth = (markerLine || {}).width;
20151
20152 lineWidth = (
20153 d.mlw + 1 ||
20154 markerLineWidth + 1 ||
20155 // TODO: we need the latter for legends... can we get rid of it?
20156 (d.trace ? (d.trace.marker.line || {}).width : 0) + 1
20157 ) - 1 || 0;
20158
20159 if('mlc' in d) lineColor = d.mlcc = fns.lineScale(d.mlc);
20160 // weird case: array wasn't long enough to apply to every point
20161 else if(Lib.isArrayOrTypedArray(markerLine.color)) lineColor = Color.defaultLine;
20162 else lineColor = markerLine.color;
20163
20164 if(Lib.isArrayOrTypedArray(marker.color)) {
20165 fillColor = Color.defaultLine;
20166 perPointGradient = true;
20167 }
20168
20169 if('mc' in d) {
20170 fillColor = d.mcc = fns.markerScale(d.mc);
20171 } else {
20172 fillColor = marker.color || 'rgba(0,0,0,0)';
20173 }
20174
20175 if(fns.selectedColorFn) {
20176 fillColor = fns.selectedColorFn(d);
20177 }
20178 }
20179
20180 if(d.om) {
20181 // open markers can't have zero linewidth, default to 1px,
20182 // and use fill color as stroke color
20183 sel.call(Color.stroke, fillColor)
20184 .style({
20185 'stroke-width': (lineWidth || 1) + 'px',
20186 fill: 'none'
20187 });
20188 } else {
20189 sel.style('stroke-width', (d.isBlank ? 0 : lineWidth) + 'px');
20190
20191 var markerGradient = marker.gradient;
20192
20193 var gradientType = d.mgt;
20194 if(gradientType) perPointGradient = true;
20195 else gradientType = markerGradient && markerGradient.type;
20196
20197 // for legend - arrays will propagate through here, but we don't need
20198 // to treat it as per-point.
20199 if(Array.isArray(gradientType)) {
20200 gradientType = gradientType[0];
20201 if(!gradientInfo[gradientType]) gradientType = 0;
20202 }
20203
20204 if(gradientType && gradientType !== 'none') {
20205 var gradientColor = d.mgc;
20206 if(gradientColor) perPointGradient = true;
20207 else gradientColor = markerGradient.color;
20208
20209 var gradientID = trace.uid;
20210 if(perPointGradient) gradientID += '-' + d.i;
20211
20212 drawing.gradient(sel, gd, gradientID, gradientType,
20213 [[0, gradientColor], [1, fillColor]], 'fill');
20214 } else {
20215 Color.fill(sel, fillColor);
20216 }
20217
20218 if(lineWidth) {
20219 Color.stroke(sel, lineColor);
20220 }
20221 }
20222};
20223
20224drawing.makePointStyleFns = function(trace) {
20225 var out = {};
20226 var marker = trace.marker;
20227
20228 // allow array marker and marker line colors to be
20229 // scaled by given max and min to colorscales
20230 out.markerScale = drawing.tryColorscale(marker, '');
20231 out.lineScale = drawing.tryColorscale(marker, 'line');
20232
20233 if(Registry.traceIs(trace, 'symbols')) {
20234 out.ms2mrc = subTypes.isBubble(trace) ?
20235 makeBubbleSizeFn(trace) :
20236 function() { return (marker.size || 6) / 2; };
20237 }
20238
20239 if(trace.selectedpoints) {
20240 Lib.extendFlat(out, drawing.makeSelectedPointStyleFns(trace));
20241 }
20242
20243 return out;
20244};
20245
20246drawing.makeSelectedPointStyleFns = function(trace) {
20247 var out = {};
20248
20249 var selectedAttrs = trace.selected || {};
20250 var unselectedAttrs = trace.unselected || {};
20251
20252 var marker = trace.marker || {};
20253 var selectedMarker = selectedAttrs.marker || {};
20254 var unselectedMarker = unselectedAttrs.marker || {};
20255
20256 var mo = marker.opacity;
20257 var smo = selectedMarker.opacity;
20258 var usmo = unselectedMarker.opacity;
20259 var smoIsDefined = smo !== undefined;
20260 var usmoIsDefined = usmo !== undefined;
20261
20262 if(Lib.isArrayOrTypedArray(mo) || smoIsDefined || usmoIsDefined) {
20263 out.selectedOpacityFn = function(d) {
20264 var base = d.mo === undefined ? marker.opacity : d.mo;
20265
20266 if(d.selected) {
20267 return smoIsDefined ? smo : base;
20268 } else {
20269 return usmoIsDefined ? usmo : DESELECTDIM * base;
20270 }
20271 };
20272 }
20273
20274 var mc = marker.color;
20275 var smc = selectedMarker.color;
20276 var usmc = unselectedMarker.color;
20277
20278 if(smc || usmc) {
20279 out.selectedColorFn = function(d) {
20280 var base = d.mcc || mc;
20281
20282 if(d.selected) {
20283 return smc || base;
20284 } else {
20285 return usmc || base;
20286 }
20287 };
20288 }
20289
20290 var ms = marker.size;
20291 var sms = selectedMarker.size;
20292 var usms = unselectedMarker.size;
20293 var smsIsDefined = sms !== undefined;
20294 var usmsIsDefined = usms !== undefined;
20295
20296 if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
20297 out.selectedSizeFn = function(d) {
20298 var base = d.mrc || ms / 2;
20299
20300 if(d.selected) {
20301 return smsIsDefined ? sms / 2 : base;
20302 } else {
20303 return usmsIsDefined ? usms / 2 : base;
20304 }
20305 };
20306 }
20307
20308 return out;
20309};
20310
20311drawing.makeSelectedTextStyleFns = function(trace) {
20312 var out = {};
20313
20314 var selectedAttrs = trace.selected || {};
20315 var unselectedAttrs = trace.unselected || {};
20316
20317 var textFont = trace.textfont || {};
20318 var selectedTextFont = selectedAttrs.textfont || {};
20319 var unselectedTextFont = unselectedAttrs.textfont || {};
20320
20321 var tc = textFont.color;
20322 var stc = selectedTextFont.color;
20323 var utc = unselectedTextFont.color;
20324
20325 out.selectedTextColorFn = function(d) {
20326 var base = d.tc || tc;
20327
20328 if(d.selected) {
20329 return stc || base;
20330 } else {
20331 if(utc) return utc;
20332 else return stc ? base : Color.addOpacity(base, DESELECTDIM);
20333 }
20334 };
20335
20336 return out;
20337};
20338
20339drawing.selectedPointStyle = function(s, trace) {
20340 if(!s.size() || !trace.selectedpoints) return;
20341
20342 var fns = drawing.makeSelectedPointStyleFns(trace);
20343 var marker = trace.marker || {};
20344 var seq = [];
20345
20346 if(fns.selectedOpacityFn) {
20347 seq.push(function(pt, d) {
20348 pt.style('opacity', fns.selectedOpacityFn(d));
20349 });
20350 }
20351
20352 if(fns.selectedColorFn) {
20353 seq.push(function(pt, d) {
20354 Color.fill(pt, fns.selectedColorFn(d));
20355 });
20356 }
20357
20358 if(fns.selectedSizeFn) {
20359 seq.push(function(pt, d) {
20360 var mx = d.mx || marker.symbol || 0;
20361 var mrc2 = fns.selectedSizeFn(d);
20362
20363 pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
20364
20365 // save for Drawing.selectedTextStyle
20366 d.mrc2 = mrc2;
20367 });
20368 }
20369
20370 if(seq.length) {
20371 s.each(function(d) {
20372 var pt = d3.select(this);
20373 for(var i = 0; i < seq.length; i++) {
20374 seq[i](pt, d);
20375 }
20376 });
20377 }
20378};
20379
20380drawing.tryColorscale = function(marker, prefix) {
20381 var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker;
20382
20383 if(cont) {
20384 var colorArray = cont.color;
20385 if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) {
20386 return Colorscale.makeColorScaleFuncFromTrace(cont);
20387 }
20388 }
20389 return Lib.identity;
20390};
20391
20392var TEXTOFFSETSIGN = {
20393 start: 1, end: -1, middle: 0, bottom: 1, top: -1
20394};
20395
20396function textPointPosition(s, textPosition, fontSize, markerRadius) {
20397 var group = d3.select(s.node().parentNode);
20398
20399 var v = textPosition.indexOf('top') !== -1 ?
20400 'top' :
20401 textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle';
20402 var h = textPosition.indexOf('left') !== -1 ?
20403 'end' :
20404 textPosition.indexOf('right') !== -1 ? 'start' : 'middle';
20405
20406 // if markers are shown, offset a little more than
20407 // the nominal marker size
20408 // ie 2/1.6 * nominal, bcs some markers are a bit bigger
20409 var r = markerRadius ? markerRadius / 0.8 + 1 : 0;
20410
20411 var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1;
20412 var dx = TEXTOFFSETSIGN[h] * r;
20413 var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
20414 (TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
20415
20416 // fix the overall text group position
20417 s.attr('text-anchor', h);
20418 group.attr('transform', 'translate(' + dx + ',' + dy + ')');
20419}
20420
20421function extracTextFontSize(d, trace) {
20422 var fontSize = d.ts || trace.textfont.size;
20423 return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
20424}
20425
20426// draw text at points
20427drawing.textPointStyle = function(s, trace, gd) {
20428 if(!s.size()) return;
20429
20430 var selectedTextColorFn;
20431 if(trace.selectedpoints) {
20432 var fns = drawing.makeSelectedTextStyleFns(trace);
20433 selectedTextColorFn = fns.selectedTextColorFn;
20434 }
20435
20436 var texttemplate = trace.texttemplate;
20437 var fullLayout = gd._fullLayout;
20438
20439 s.each(function(d) {
20440 var p = d3.select(this);
20441
20442 var text = texttemplate ?
20443 Lib.extractOption(d, trace, 'txt', 'texttemplate') :
20444 Lib.extractOption(d, trace, 'tx', 'text');
20445
20446 if(!text && text !== 0) {
20447 p.remove();
20448 return;
20449 }
20450
20451 if(texttemplate) {
20452 var labels = trace._module.formatLabels ? trace._module.formatLabels(d, trace, fullLayout) : {};
20453 var pointValues = {};
20454 appendArrayPointValue(pointValues, trace, d.i);
20455 var meta = trace._meta || {};
20456 text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta);
20457 }
20458
20459 var pos = d.tp || trace.textposition;
20460 var fontSize = extracTextFontSize(d, trace);
20461 var fontColor = selectedTextColorFn ?
20462 selectedTextColorFn(d) :
20463 (d.tc || trace.textfont.color);
20464
20465 p.call(drawing.font,
20466 d.tf || trace.textfont.family,
20467 fontSize,
20468 fontColor)
20469 .text(text)
20470 .call(svgTextUtils.convertToTspans, gd)
20471 .call(textPointPosition, pos, fontSize, d.mrc);
20472 });
20473};
20474
20475drawing.selectedTextStyle = function(s, trace) {
20476 if(!s.size() || !trace.selectedpoints) return;
20477
20478 var fns = drawing.makeSelectedTextStyleFns(trace);
20479
20480 s.each(function(d) {
20481 var tx = d3.select(this);
20482 var tc = fns.selectedTextColorFn(d);
20483 var tp = d.tp || trace.textposition;
20484 var fontSize = extracTextFontSize(d, trace);
20485
20486 Color.fill(tx, tc);
20487 textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc);
20488 });
20489};
20490
20491// generalized Catmull-Rom splines, per
20492// http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
20493var CatmullRomExp = 0.5;
20494drawing.smoothopen = function(pts, smoothness) {
20495 if(pts.length < 3) { return 'M' + pts.join('L');}
20496 var path = 'M' + pts[0];
20497 var tangents = [];
20498 var i;
20499 for(i = 1; i < pts.length - 1; i++) {
20500 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
20501 }
20502 path += 'Q' + tangents[0][0] + ' ' + pts[1];
20503 for(i = 2; i < pts.length - 1; i++) {
20504 path += 'C' + tangents[i - 2][1] + ' ' + tangents[i - 1][0] + ' ' + pts[i];
20505 }
20506 path += 'Q' + tangents[pts.length - 3][1] + ' ' + pts[pts.length - 1];
20507 return path;
20508};
20509
20510drawing.smoothclosed = function(pts, smoothness) {
20511 if(pts.length < 3) { return 'M' + pts.join('L') + 'Z'; }
20512 var path = 'M' + pts[0];
20513 var pLast = pts.length - 1;
20514 var tangents = [makeTangent(pts[pLast], pts[0], pts[1], smoothness)];
20515 var i;
20516 for(i = 1; i < pLast; i++) {
20517 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
20518 }
20519 tangents.push(
20520 makeTangent(pts[pLast - 1], pts[pLast], pts[0], smoothness)
20521 );
20522
20523 for(i = 1; i <= pLast; i++) {
20524 path += 'C' + tangents[i - 1][1] + ' ' + tangents[i][0] + ' ' + pts[i];
20525 }
20526 path += 'C' + tangents[pLast][1] + ' ' + tangents[0][0] + ' ' + pts[0] + 'Z';
20527 return path;
20528};
20529
20530function makeTangent(prevpt, thispt, nextpt, smoothness) {
20531 var d1x = prevpt[0] - thispt[0];
20532 var d1y = prevpt[1] - thispt[1];
20533 var d2x = nextpt[0] - thispt[0];
20534 var d2y = nextpt[1] - thispt[1];
20535 var d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2);
20536 var d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2);
20537 var numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness;
20538 var numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness;
20539 var denom1 = 3 * d2a * (d1a + d2a);
20540 var denom2 = 3 * d1a * (d1a + d2a);
20541 return [
20542 [
20543 d3.round(thispt[0] + (denom1 && numx / denom1), 2),
20544 d3.round(thispt[1] + (denom1 && numy / denom1), 2)
20545 ], [
20546 d3.round(thispt[0] - (denom2 && numx / denom2), 2),
20547 d3.round(thispt[1] - (denom2 && numy / denom2), 2)
20548 ]
20549 ];
20550}
20551
20552// step paths - returns a generator function for paths
20553// with the given step shape
20554var STEPPATH = {
20555 hv: function(p0, p1) {
20556 return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
20557 },
20558 vh: function(p0, p1) {
20559 return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
20560 },
20561 hvh: function(p0, p1) {
20562 return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' +
20563 d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
20564 },
20565 vhv: function(p0, p1) {
20566 return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' +
20567 d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
20568 }
20569};
20570var STEPLINEAR = function(p0, p1) {
20571 return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2);
20572};
20573drawing.steps = function(shape) {
20574 var onestep = STEPPATH[shape] || STEPLINEAR;
20575 return function(pts) {
20576 var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2);
20577 for(var i = 1; i < pts.length; i++) {
20578 path += onestep(pts[i - 1], pts[i]);
20579 }
20580 return path;
20581 };
20582};
20583
20584// off-screen svg render testing element, shared by the whole page
20585// uses the id 'js-plotly-tester' and stores it in drawing.tester
20586drawing.makeTester = function() {
20587 var tester = Lib.ensureSingleById(d3.select('body'), 'svg', 'js-plotly-tester', function(s) {
20588 s.attr(xmlnsNamespaces.svgAttrs)
20589 .style({
20590 position: 'absolute',
20591 left: '-10000px',
20592 top: '-10000px',
20593 width: '9000px',
20594 height: '9000px',
20595 'z-index': '1'
20596 });
20597 });
20598
20599 // browsers differ on how they describe the bounding rect of
20600 // the svg if its contents spill over... so make a 1x1px
20601 // reference point we can measure off of.
20602 var testref = Lib.ensureSingle(tester, 'path', 'js-reference-point', function(s) {
20603 s.attr('d', 'M0,0H1V1H0Z')
20604 .style({
20605 'stroke-width': 0,
20606 fill: 'black'
20607 });
20608 });
20609
20610 drawing.tester = tester;
20611 drawing.testref = testref;
20612};
20613
20614/*
20615 * use our offscreen tester to get a clientRect for an element,
20616 * in a reference frame where it isn't translated (or transformed) and
20617 * its anchor point is at (0,0)
20618 * always returns a copy of the bbox, so the caller can modify it safely
20619 *
20620 * @param {SVGElement} node: the element to measure. If possible this should be
20621 * a <text> or MathJax <g> element that's already passed through
20622 * `convertToTspans` because in that case we can cache the results, but it's
20623 * possible to pass in any svg element.
20624 *
20625 * @param {boolean} inTester: is this element already in `drawing.tester`?
20626 * If you are measuring a dummy element, rather than one you really intend
20627 * to use on the plot, making it in `drawing.tester` in the first place
20628 * allows us to test faster because it cuts out cloning and appending it.
20629 *
20630 * @param {string} hash: for internal use only, if we already know the cache key
20631 * for this element beforehand.
20632 *
20633 * @return {object}: a plain object containing the width, height, left, right,
20634 * top, and bottom of `node`
20635 */
20636drawing.savedBBoxes = {};
20637var savedBBoxesCount = 0;
20638var maxSavedBBoxes = 10000;
20639
20640drawing.bBox = function(node, inTester, hash) {
20641 /*
20642 * Cache elements we've already measured so we don't have to
20643 * remeasure the same thing many times
20644 * We have a few bBox callers though who pass a node larger than
20645 * a <text> or a MathJax <g>, such as an axis group containing many labels.
20646 * These will not generate a hash (unless we figure out an appropriate
20647 * hash key for them) and thus we will not hash them.
20648 */
20649 if(!hash) hash = nodeHash(node);
20650 var out;
20651 if(hash) {
20652 out = drawing.savedBBoxes[hash];
20653 if(out) return Lib.extendFlat({}, out);
20654 } else if(node.childNodes.length === 1) {
20655 /*
20656 * If we have only one child element, which is itself hashable, make
20657 * a new hash from this element plus its x,y,transform
20658 * These bounding boxes *include* x,y,transform - mostly for use by
20659 * callers trying to avoid overlaps (ie titles)
20660 */
20661 var innerNode = node.childNodes[0];
20662
20663 hash = nodeHash(innerNode);
20664 if(hash) {
20665 var x = +innerNode.getAttribute('x') || 0;
20666 var y = +innerNode.getAttribute('y') || 0;
20667 var transform = innerNode.getAttribute('transform');
20668
20669 if(!transform) {
20670 // in this case, just varying x and y, don't bother caching
20671 // the final bBox because the alteration is quick.
20672 var innerBB = drawing.bBox(innerNode, false, hash);
20673 if(x) {
20674 innerBB.left += x;
20675 innerBB.right += x;
20676 }
20677 if(y) {
20678 innerBB.top += y;
20679 innerBB.bottom += y;
20680 }
20681 return innerBB;
20682 }
20683 /*
20684 * else we have a transform - rather than make a complicated
20685 * (and error-prone and probably slow) transform parser/calculator,
20686 * just continue on calculating the boundingClientRect of the group
20687 * and use the new composite hash to cache it.
20688 * That said, `innerNode.transform.baseVal` is an array of
20689 * `SVGTransform` objects, that *do* seem to have a nice matrix
20690 * multiplication interface that we could use to avoid making
20691 * another getBoundingClientRect call...
20692 */
20693 hash += '~' + x + '~' + y + '~' + transform;
20694
20695 out = drawing.savedBBoxes[hash];
20696 if(out) return Lib.extendFlat({}, out);
20697 }
20698 }
20699 var testNode, tester;
20700 if(inTester) {
20701 testNode = node;
20702 } else {
20703 tester = drawing.tester.node();
20704
20705 // copy the node to test into the tester
20706 testNode = node.cloneNode(true);
20707 tester.appendChild(testNode);
20708 }
20709
20710 // standardize its position (and newline tspans if any)
20711 d3.select(testNode)
20712 .attr('transform', null)
20713 .call(svgTextUtils.positionText, 0, 0);
20714
20715 var testRect = testNode.getBoundingClientRect();
20716 var refRect = drawing.testref
20717 .node()
20718 .getBoundingClientRect();
20719
20720 if(!inTester) tester.removeChild(testNode);
20721
20722 var bb = {
20723 height: testRect.height,
20724 width: testRect.width,
20725 left: testRect.left - refRect.left,
20726 top: testRect.top - refRect.top,
20727 right: testRect.right - refRect.left,
20728 bottom: testRect.bottom - refRect.top
20729 };
20730
20731 // make sure we don't have too many saved boxes,
20732 // or a long session could overload on memory
20733 // by saving boxes for long-gone elements
20734 if(savedBBoxesCount >= maxSavedBBoxes) {
20735 drawing.savedBBoxes = {};
20736 savedBBoxesCount = 0;
20737 }
20738
20739 // cache this bbox
20740 if(hash) drawing.savedBBoxes[hash] = bb;
20741 savedBBoxesCount++;
20742
20743 return Lib.extendFlat({}, bb);
20744};
20745
20746// capture everything about a node (at least in our usage) that
20747// impacts its bounding box, given that bBox clears x, y, and transform
20748function nodeHash(node) {
20749 var inputText = node.getAttribute('data-unformatted');
20750 if(inputText === null) return;
20751 return inputText +
20752 node.getAttribute('data-math') +
20753 node.getAttribute('text-anchor') +
20754 node.getAttribute('style');
20755}
20756
20757/**
20758 * Set clipPath URL in a way that work for all situations.
20759 *
20760 * In details, graphs on pages with <base> HTML tags need to prepend
20761 * the clip path ids with the page's base url EXCEPT during toImage exports.
20762 *
20763 * @param {d3 selection} s : node to add clip-path attribute
20764 * @param {string} localId : local clip-path (w/o base url) id
20765 * @param {DOM element || object} gd
20766 * - context._baseUrl {string}
20767 * - context._exportedPlot {boolean}
20768 */
20769drawing.setClipUrl = function(s, localId, gd) {
20770 s.attr('clip-path', getFullUrl(localId, gd));
20771};
20772
20773function getFullUrl(localId, gd) {
20774 if(!localId) return null;
20775
20776 var context = gd._context;
20777 var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');
20778 return 'url(\'' + baseUrl + '#' + localId + '\')';
20779}
20780
20781drawing.getTranslate = function(element) {
20782 // Note the separator [^\d] between x and y in this regex
20783 // We generally use ',' but IE will convert it to ' '
20784 var re = /.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/;
20785 var getter = element.attr ? 'attr' : 'getAttribute';
20786 var transform = element[getter]('transform') || '';
20787
20788 var translate = transform.replace(re, function(match, p1, p2) {
20789 return [p1, p2].join(' ');
20790 })
20791 .split(' ');
20792
20793 return {
20794 x: +translate[0] || 0,
20795 y: +translate[1] || 0
20796 };
20797};
20798
20799drawing.setTranslate = function(element, x, y) {
20800 var re = /(\btranslate\(.*?\);?)/;
20801 var getter = element.attr ? 'attr' : 'getAttribute';
20802 var setter = element.attr ? 'attr' : 'setAttribute';
20803 var transform = element[getter]('transform') || '';
20804
20805 x = x || 0;
20806 y = y || 0;
20807
20808 transform = transform.replace(re, '').trim();
20809 transform += ' translate(' + x + ', ' + y + ')';
20810 transform = transform.trim();
20811
20812 element[setter]('transform', transform);
20813
20814 return transform;
20815};
20816
20817drawing.getScale = function(element) {
20818 var re = /.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/;
20819 var getter = element.attr ? 'attr' : 'getAttribute';
20820 var transform = element[getter]('transform') || '';
20821
20822 var translate = transform.replace(re, function(match, p1, p2) {
20823 return [p1, p2].join(' ');
20824 })
20825 .split(' ');
20826
20827 return {
20828 x: +translate[0] || 1,
20829 y: +translate[1] || 1
20830 };
20831};
20832
20833drawing.setScale = function(element, x, y) {
20834 var re = /(\bscale\(.*?\);?)/;
20835 var getter = element.attr ? 'attr' : 'getAttribute';
20836 var setter = element.attr ? 'attr' : 'setAttribute';
20837 var transform = element[getter]('transform') || '';
20838
20839 x = x || 1;
20840 y = y || 1;
20841
20842 transform = transform.replace(re, '').trim();
20843 transform += ' scale(' + x + ', ' + y + ')';
20844 transform = transform.trim();
20845
20846 element[setter]('transform', transform);
20847
20848 return transform;
20849};
20850
20851var SCALE_RE = /\s*sc.*/;
20852
20853drawing.setPointGroupScale = function(selection, xScale, yScale) {
20854 xScale = xScale || 1;
20855 yScale = yScale || 1;
20856
20857 if(!selection) return;
20858
20859 // The same scale transform for every point:
20860 var scale = (xScale === 1 && yScale === 1) ?
20861 '' :
20862 ' scale(' + xScale + ',' + yScale + ')';
20863
20864 selection.each(function() {
20865 var t = (this.getAttribute('transform') || '').replace(SCALE_RE, '');
20866 t += scale;
20867 t = t.trim();
20868 this.setAttribute('transform', t);
20869 });
20870};
20871
20872var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/;
20873
20874drawing.setTextPointsScale = function(selection, xScale, yScale) {
20875 if(!selection) return;
20876
20877 selection.each(function() {
20878 var transforms;
20879 var el = d3.select(this);
20880 var text = el.select('text');
20881
20882 if(!text.node()) return;
20883
20884 var x = parseFloat(text.attr('x') || 0);
20885 var y = parseFloat(text.attr('y') || 0);
20886
20887 var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE);
20888
20889 if(xScale === 1 && yScale === 1) {
20890 transforms = [];
20891 } else {
20892 transforms = [
20893 'translate(' + x + ',' + y + ')',
20894 'scale(' + xScale + ',' + yScale + ')',
20895 'translate(' + (-x) + ',' + (-y) + ')',
20896 ];
20897 }
20898
20899 if(existingTransform) {
20900 transforms.push(existingTransform);
20901 }
20902
20903 el.attr('transform', transforms.join(' '));
20904 });
20905};
20906
20907},{"../../components/fx/helpers":88,"../../constants/alignment":154,"../../constants/interactions":157,"../../constants/xmlns_namespaces":159,"../../lib":178,"../../lib/svg_text_utils":199,"../../registry":269,"../../traces/scatter/make_bubble_size_func":406,"../../traces/scatter/subtypes":413,"../color":52,"../colorscale":64,"./symbol_defs":75,"d3":16,"fast-isnumeric":18,"tinycolor2":35}],75:[function(_dereq_,module,exports){
20908/**
20909* Copyright 2012-2020, Plotly, Inc.
20910* All rights reserved.
20911*
20912* This source code is licensed under the MIT license found in the
20913* LICENSE file in the root directory of this source tree.
20914*/
20915
20916
20917'use strict';
20918
20919var d3 = _dereq_('d3');
20920
20921/** Marker symbol definitions
20922 * users can specify markers either by number or name
20923 * add 100 (or '-open') and you get an open marker
20924 * open markers have no fill and use line color as the stroke color
20925 * add 200 (or '-dot') and you get a dot in the middle
20926 * add both and you get both
20927 */
20928
20929module.exports = {
20930 circle: {
20931 n: 0,
20932 f: function(r) {
20933 var rs = d3.round(r, 2);
20934 return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
20935 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
20936 }
20937 },
20938 square: {
20939 n: 1,
20940 f: function(r) {
20941 var rs = d3.round(r, 2);
20942 return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
20943 }
20944 },
20945 diamond: {
20946 n: 2,
20947 f: function(r) {
20948 var rd = d3.round(r * 1.3, 2);
20949 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z';
20950 }
20951 },
20952 cross: {
20953 n: 3,
20954 f: function(r) {
20955 var rc = d3.round(r * 0.4, 2);
20956 var rc2 = d3.round(r * 1.2, 2);
20957 return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
20958 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 +
20959 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z';
20960 }
20961 },
20962 x: {
20963 n: 4,
20964 f: function(r) {
20965 var rx = d3.round(r * 0.8 / Math.sqrt(2), 2);
20966 var ne = 'l' + rx + ',' + rx;
20967 var se = 'l' + rx + ',-' + rx;
20968 var sw = 'l-' + rx + ',-' + rx;
20969 var nw = 'l-' + rx + ',' + rx;
20970 return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z';
20971 }
20972 },
20973 'triangle-up': {
20974 n: 5,
20975 f: function(r) {
20976 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
20977 var r2 = d3.round(r / 2, 2);
20978 var rs = d3.round(r, 2);
20979 return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z';
20980 }
20981 },
20982 'triangle-down': {
20983 n: 6,
20984 f: function(r) {
20985 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
20986 var r2 = d3.round(r / 2, 2);
20987 var rs = d3.round(r, 2);
20988 return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z';
20989 }
20990 },
20991 'triangle-left': {
20992 n: 7,
20993 f: function(r) {
20994 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
20995 var r2 = d3.round(r / 2, 2);
20996 var rs = d3.round(r, 2);
20997 return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z';
20998 }
20999 },
21000 'triangle-right': {
21001 n: 8,
21002 f: function(r) {
21003 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
21004 var r2 = d3.round(r / 2, 2);
21005 var rs = d3.round(r, 2);
21006 return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z';
21007 }
21008 },
21009 'triangle-ne': {
21010 n: 9,
21011 f: function(r) {
21012 var r1 = d3.round(r * 0.6, 2);
21013 var r2 = d3.round(r * 1.2, 2);
21014 return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z';
21015 }
21016 },
21017 'triangle-se': {
21018 n: 10,
21019 f: function(r) {
21020 var r1 = d3.round(r * 0.6, 2);
21021 var r2 = d3.round(r * 1.2, 2);
21022 return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z';
21023 }
21024 },
21025 'triangle-sw': {
21026 n: 11,
21027 f: function(r) {
21028 var r1 = d3.round(r * 0.6, 2);
21029 var r2 = d3.round(r * 1.2, 2);
21030 return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z';
21031 }
21032 },
21033 'triangle-nw': {
21034 n: 12,
21035 f: function(r) {
21036 var r1 = d3.round(r * 0.6, 2);
21037 var r2 = d3.round(r * 1.2, 2);
21038 return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z';
21039 }
21040 },
21041 pentagon: {
21042 n: 13,
21043 f: function(r) {
21044 var x1 = d3.round(r * 0.951, 2);
21045 var x2 = d3.round(r * 0.588, 2);
21046 var y0 = d3.round(-r, 2);
21047 var y1 = d3.round(r * -0.309, 2);
21048 var y2 = d3.round(r * 0.809, 2);
21049 return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
21050 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z';
21051 }
21052 },
21053 hexagon: {
21054 n: 14,
21055 f: function(r) {
21056 var y0 = d3.round(r, 2);
21057 var y1 = d3.round(r / 2, 2);
21058 var x = d3.round(r * Math.sqrt(3) / 2, 2);
21059 return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
21060 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z';
21061 }
21062 },
21063 hexagon2: {
21064 n: 15,
21065 f: function(r) {
21066 var x0 = d3.round(r, 2);
21067 var x1 = d3.round(r / 2, 2);
21068 var y = d3.round(r * Math.sqrt(3) / 2, 2);
21069 return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
21070 ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z';
21071 }
21072 },
21073 octagon: {
21074 n: 16,
21075 f: function(r) {
21076 var a = d3.round(r * 0.924, 2);
21077 var b = d3.round(r * 0.383, 2);
21078 return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
21079 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z';
21080 }
21081 },
21082 star: {
21083 n: 17,
21084 f: function(r) {
21085 var rs = r * 1.4;
21086 var x1 = d3.round(rs * 0.225, 2);
21087 var x2 = d3.round(rs * 0.951, 2);
21088 var x3 = d3.round(rs * 0.363, 2);
21089 var x4 = d3.round(rs * 0.588, 2);
21090 var y0 = d3.round(-rs, 2);
21091 var y1 = d3.round(rs * -0.309, 2);
21092 var y3 = d3.round(rs * 0.118, 2);
21093 var y4 = d3.round(rs * 0.809, 2);
21094 var y5 = d3.round(rs * 0.382, 2);
21095 return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
21096 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 +
21097 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 +
21098 'L0,' + y0 + 'Z';
21099 }
21100 },
21101 hexagram: {
21102 n: 18,
21103 f: function(r) {
21104 var y = d3.round(r * 0.66, 2);
21105 var x1 = d3.round(r * 0.38, 2);
21106 var x2 = d3.round(r * 0.76, 2);
21107 return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
21108 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 +
21109 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 +
21110 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z';
21111 }
21112 },
21113 'star-triangle-up': {
21114 n: 19,
21115 f: function(r) {
21116 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
21117 var y1 = d3.round(r * 0.8, 2);
21118 var y2 = d3.round(r * 1.6, 2);
21119 var rc = d3.round(r * 4, 2);
21120 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
21121 return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
21122 aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z';
21123 }
21124 },
21125 'star-triangle-down': {
21126 n: 20,
21127 f: function(r) {
21128 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
21129 var y1 = d3.round(r * 0.8, 2);
21130 var y2 = d3.round(r * 1.6, 2);
21131 var rc = d3.round(r * 4, 2);
21132 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
21133 return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
21134 aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z';
21135 }
21136 },
21137 'star-square': {
21138 n: 21,
21139 f: function(r) {
21140 var rp = d3.round(r * 1.1, 2);
21141 var rc = d3.round(r * 2, 2);
21142 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
21143 return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
21144 aPart + rp + ',' + rp + aPart + rp + ',-' + rp +
21145 aPart + '-' + rp + ',-' + rp + 'Z';
21146 }
21147 },
21148 'star-diamond': {
21149 n: 22,
21150 f: function(r) {
21151 var rp = d3.round(r * 1.4, 2);
21152 var rc = d3.round(r * 1.9, 2);
21153 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
21154 return 'M-' + rp + ',0' + aPart + '0,' + rp +
21155 aPart + rp + ',0' + aPart + '0,-' + rp +
21156 aPart + '-' + rp + ',0' + 'Z';
21157 }
21158 },
21159 'diamond-tall': {
21160 n: 23,
21161 f: function(r) {
21162 var x = d3.round(r * 0.7, 2);
21163 var y = d3.round(r * 1.4, 2);
21164 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
21165 }
21166 },
21167 'diamond-wide': {
21168 n: 24,
21169 f: function(r) {
21170 var x = d3.round(r * 1.4, 2);
21171 var y = d3.round(r * 0.7, 2);
21172 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
21173 }
21174 },
21175 hourglass: {
21176 n: 25,
21177 f: function(r) {
21178 var rs = d3.round(r, 2);
21179 return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z';
21180 },
21181 noDot: true
21182 },
21183 bowtie: {
21184 n: 26,
21185 f: function(r) {
21186 var rs = d3.round(r, 2);
21187 return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z';
21188 },
21189 noDot: true
21190 },
21191 'circle-cross': {
21192 n: 27,
21193 f: function(r) {
21194 var rs = d3.round(r, 2);
21195 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
21196 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
21197 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
21198 },
21199 needLine: true,
21200 noDot: true
21201 },
21202 'circle-x': {
21203 n: 28,
21204 f: function(r) {
21205 var rs = d3.round(r, 2);
21206 var rc = d3.round(r / Math.sqrt(2), 2);
21207 return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
21208 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc +
21209 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
21210 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
21211 },
21212 needLine: true,
21213 noDot: true
21214 },
21215 'square-cross': {
21216 n: 29,
21217 f: function(r) {
21218 var rs = d3.round(r, 2);
21219 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
21220 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
21221 },
21222 needLine: true,
21223 noDot: true
21224 },
21225 'square-x': {
21226 n: 30,
21227 f: function(r) {
21228 var rs = d3.round(r, 2);
21229 return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
21230 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs +
21231 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
21232 },
21233 needLine: true,
21234 noDot: true
21235 },
21236 'diamond-cross': {
21237 n: 31,
21238 f: function(r) {
21239 var rd = d3.round(r * 1.3, 2);
21240 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
21241 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd;
21242 },
21243 needLine: true,
21244 noDot: true
21245 },
21246 'diamond-x': {
21247 n: 32,
21248 f: function(r) {
21249 var rd = d3.round(r * 1.3, 2);
21250 var r2 = d3.round(r * 0.65, 2);
21251 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
21252 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 +
21253 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2;
21254 },
21255 needLine: true,
21256 noDot: true
21257 },
21258 'cross-thin': {
21259 n: 33,
21260 f: function(r) {
21261 var rc = d3.round(r * 1.4, 2);
21262 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
21263 },
21264 needLine: true,
21265 noDot: true,
21266 noFill: true
21267 },
21268 'x-thin': {
21269 n: 34,
21270 f: function(r) {
21271 var rx = d3.round(r, 2);
21272 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
21273 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
21274 },
21275 needLine: true,
21276 noDot: true,
21277 noFill: true
21278 },
21279 asterisk: {
21280 n: 35,
21281 f: function(r) {
21282 var rc = d3.round(r * 1.2, 2);
21283 var rs = d3.round(r * 0.85, 2);
21284 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
21285 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
21286 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
21287 },
21288 needLine: true,
21289 noDot: true,
21290 noFill: true
21291 },
21292 hash: {
21293 n: 36,
21294 f: function(r) {
21295 var r1 = d3.round(r / 2, 2);
21296 var r2 = d3.round(r, 2);
21297 return 'M' + r1 + ',' + r2 + 'V-' + r2 +
21298 'm-' + r2 + ',0V' + r2 +
21299 'M' + r2 + ',' + r1 + 'H-' + r2 +
21300 'm0,-' + r2 + 'H' + r2;
21301 },
21302 needLine: true,
21303 noFill: true
21304 },
21305 'y-up': {
21306 n: 37,
21307 f: function(r) {
21308 var x = d3.round(r * 1.2, 2);
21309 var y0 = d3.round(r * 1.6, 2);
21310 var y1 = d3.round(r * 0.8, 2);
21311 return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
21312 },
21313 needLine: true,
21314 noDot: true,
21315 noFill: true
21316 },
21317 'y-down': {
21318 n: 38,
21319 f: function(r) {
21320 var x = d3.round(r * 1.2, 2);
21321 var y0 = d3.round(r * 1.6, 2);
21322 var y1 = d3.round(r * 0.8, 2);
21323 return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
21324 },
21325 needLine: true,
21326 noDot: true,
21327 noFill: true
21328 },
21329 'y-left': {
21330 n: 39,
21331 f: function(r) {
21332 var y = d3.round(r * 1.2, 2);
21333 var x0 = d3.round(r * 1.6, 2);
21334 var x1 = d3.round(r * 0.8, 2);
21335 return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
21336 },
21337 needLine: true,
21338 noDot: true,
21339 noFill: true
21340 },
21341 'y-right': {
21342 n: 40,
21343 f: function(r) {
21344 var y = d3.round(r * 1.2, 2);
21345 var x0 = d3.round(r * 1.6, 2);
21346 var x1 = d3.round(r * 0.8, 2);
21347 return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
21348 },
21349 needLine: true,
21350 noDot: true,
21351 noFill: true
21352 },
21353 'line-ew': {
21354 n: 41,
21355 f: function(r) {
21356 var rc = d3.round(r * 1.4, 2);
21357 return 'M' + rc + ',0H-' + rc;
21358 },
21359 needLine: true,
21360 noDot: true,
21361 noFill: true
21362 },
21363 'line-ns': {
21364 n: 42,
21365 f: function(r) {
21366 var rc = d3.round(r * 1.4, 2);
21367 return 'M0,' + rc + 'V-' + rc;
21368 },
21369 needLine: true,
21370 noDot: true,
21371 noFill: true
21372 },
21373 'line-ne': {
21374 n: 43,
21375 f: function(r) {
21376 var rx = d3.round(r, 2);
21377 return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
21378 },
21379 needLine: true,
21380 noDot: true,
21381 noFill: true
21382 },
21383 'line-nw': {
21384 n: 44,
21385 f: function(r) {
21386 var rx = d3.round(r, 2);
21387 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
21388 },
21389 needLine: true,
21390 noDot: true,
21391 noFill: true
21392 }
21393};
21394
21395},{"d3":16}],76:[function(_dereq_,module,exports){
21396/**
21397* Copyright 2012-2020, Plotly, Inc.
21398* All rights reserved.
21399*
21400* This source code is licensed under the MIT license found in the
21401* LICENSE file in the root directory of this source tree.
21402*/
21403
21404'use strict';
21405
21406
21407module.exports = {
21408 visible: {
21409 valType: 'boolean',
21410
21411 editType: 'calc',
21412
21413 },
21414 type: {
21415 valType: 'enumerated',
21416 values: ['percent', 'constant', 'sqrt', 'data'],
21417
21418 editType: 'calc',
21419
21420 },
21421 symmetric: {
21422 valType: 'boolean',
21423
21424 editType: 'calc',
21425
21426 },
21427 array: {
21428 valType: 'data_array',
21429 editType: 'calc',
21430
21431 },
21432 arrayminus: {
21433 valType: 'data_array',
21434 editType: 'calc',
21435
21436 },
21437 value: {
21438 valType: 'number',
21439 min: 0,
21440 dflt: 10,
21441
21442 editType: 'calc',
21443
21444 },
21445 valueminus: {
21446 valType: 'number',
21447 min: 0,
21448 dflt: 10,
21449
21450 editType: 'calc',
21451
21452 },
21453 traceref: {
21454 valType: 'integer',
21455 min: 0,
21456 dflt: 0,
21457
21458 editType: 'style'
21459 },
21460 tracerefminus: {
21461 valType: 'integer',
21462 min: 0,
21463 dflt: 0,
21464
21465 editType: 'style'
21466 },
21467 copy_ystyle: {
21468 valType: 'boolean',
21469
21470 editType: 'plot'
21471 },
21472 copy_zstyle: {
21473 valType: 'boolean',
21474
21475 editType: 'style'
21476 },
21477 color: {
21478 valType: 'color',
21479
21480 editType: 'style',
21481
21482 },
21483 thickness: {
21484 valType: 'number',
21485 min: 0,
21486 dflt: 2,
21487
21488 editType: 'style',
21489
21490 },
21491 width: {
21492 valType: 'number',
21493 min: 0,
21494
21495 editType: 'plot',
21496
21497 },
21498 editType: 'calc',
21499
21500 _deprecated: {
21501 opacity: {
21502 valType: 'number',
21503
21504 editType: 'style',
21505
21506 }
21507 }
21508};
21509
21510},{}],77:[function(_dereq_,module,exports){
21511/**
21512* Copyright 2012-2020, Plotly, Inc.
21513* All rights reserved.
21514*
21515* This source code is licensed under the MIT license found in the
21516* LICENSE file in the root directory of this source tree.
21517*/
21518
21519'use strict';
21520
21521var isNumeric = _dereq_('fast-isnumeric');
21522
21523var Registry = _dereq_('../../registry');
21524var Axes = _dereq_('../../plots/cartesian/axes');
21525var Lib = _dereq_('../../lib');
21526
21527var makeComputeError = _dereq_('./compute_error');
21528
21529module.exports = function calc(gd) {
21530 var calcdata = gd.calcdata;
21531
21532 for(var i = 0; i < calcdata.length; i++) {
21533 var calcTrace = calcdata[i];
21534 var trace = calcTrace[0].trace;
21535
21536 if(trace.visible === true && Registry.traceIs(trace, 'errorBarsOK')) {
21537 var xa = Axes.getFromId(gd, trace.xaxis);
21538 var ya = Axes.getFromId(gd, trace.yaxis);
21539 calcOneAxis(calcTrace, trace, xa, 'x');
21540 calcOneAxis(calcTrace, trace, ya, 'y');
21541 }
21542 }
21543};
21544
21545function calcOneAxis(calcTrace, trace, axis, coord) {
21546 var opts = trace['error_' + coord] || {};
21547 var isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1);
21548 var vals = [];
21549
21550 if(!isVisible) return;
21551
21552 var computeError = makeComputeError(opts);
21553
21554 for(var i = 0; i < calcTrace.length; i++) {
21555 var calcPt = calcTrace[i];
21556
21557 var iIn = calcPt.i;
21558
21559 // for types that don't include `i` in each calcdata point
21560 if(iIn === undefined) iIn = i;
21561
21562 // for stacked area inserted points
21563 // TODO: errorbars have been tested cursorily with stacked area,
21564 // but not thoroughly. It's not even really clear what you want to do:
21565 // Should it just be calculated based on that trace's size data?
21566 // Should you add errors from below in quadrature?
21567 // And what about normalization, where in principle the errors shrink
21568 // again when you get up to the top end?
21569 // One option would be to forbid errorbars with stacking until we
21570 // decide how to handle these questions.
21571 else if(iIn === null) continue;
21572
21573 var calcCoord = calcPt[coord];
21574
21575 if(!isNumeric(axis.c2l(calcCoord))) continue;
21576
21577 var errors = computeError(calcCoord, iIn);
21578 if(isNumeric(errors[0]) && isNumeric(errors[1])) {
21579 var shoe = calcPt[coord + 's'] = calcCoord - errors[0];
21580 var hat = calcPt[coord + 'h'] = calcCoord + errors[1];
21581 vals.push(shoe, hat);
21582 }
21583 }
21584
21585 var axId = axis._id;
21586 var baseExtremes = trace._extremes[axId];
21587 var extremes = Axes.findExtremes(
21588 axis,
21589 vals,
21590 Lib.extendFlat({tozero: baseExtremes.opts.tozero}, {padded: true})
21591 );
21592 baseExtremes.min = baseExtremes.min.concat(extremes.min);
21593 baseExtremes.max = baseExtremes.max.concat(extremes.max);
21594}
21595
21596},{"../../lib":178,"../../plots/cartesian/axes":222,"../../registry":269,"./compute_error":78,"fast-isnumeric":18}],78:[function(_dereq_,module,exports){
21597/**
21598* Copyright 2012-2020, Plotly, Inc.
21599* All rights reserved.
21600*
21601* This source code is licensed under the MIT license found in the
21602* LICENSE file in the root directory of this source tree.
21603*/
21604
21605
21606'use strict';
21607
21608
21609/**
21610 * Error bar computing function generator
21611 *
21612 * N.B. The generated function does not clean the dataPt entries. Non-numeric
21613 * entries result in undefined error magnitudes.
21614 *
21615 * @param {object} opts error bar attributes
21616 *
21617 * @return {function} :
21618 * @param {numeric} dataPt data point from where to compute the error magnitude
21619 * @param {number} index index of dataPt in its corresponding data array
21620 * @return {array}
21621 * - error[0] : error magnitude in the negative direction
21622 * - error[1] : " " " " positive "
21623 */
21624module.exports = function makeComputeError(opts) {
21625 var type = opts.type;
21626 var symmetric = opts.symmetric;
21627
21628 if(type === 'data') {
21629 var array = opts.array || [];
21630
21631 if(symmetric) {
21632 return function computeError(dataPt, index) {
21633 var val = +(array[index]);
21634 return [val, val];
21635 };
21636 } else {
21637 var arrayminus = opts.arrayminus || [];
21638 return function computeError(dataPt, index) {
21639 var val = +array[index];
21640 var valMinus = +arrayminus[index];
21641 // in case one is present and the other is missing, fill in 0
21642 // so we still see the present one. Mostly useful during manual
21643 // data entry.
21644 if(!isNaN(val) || !isNaN(valMinus)) {
21645 return [valMinus || 0, val || 0];
21646 }
21647 return [NaN, NaN];
21648 };
21649 }
21650 } else {
21651 var computeErrorValue = makeComputeErrorValue(type, opts.value);
21652 var computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus);
21653
21654 if(symmetric || opts.valueminus === undefined) {
21655 return function computeError(dataPt) {
21656 var val = computeErrorValue(dataPt);
21657 return [val, val];
21658 };
21659 } else {
21660 return function computeError(dataPt) {
21661 return [
21662 computeErrorValueMinus(dataPt),
21663 computeErrorValue(dataPt)
21664 ];
21665 };
21666 }
21667 }
21668};
21669
21670/**
21671 * Compute error bar magnitude (for all types except data)
21672 *
21673 * @param {string} type error bar type
21674 * @param {numeric} value error bar value
21675 *
21676 * @return {function} :
21677 * @param {numeric} dataPt
21678 */
21679function makeComputeErrorValue(type, value) {
21680 if(type === 'percent') {
21681 return function(dataPt) {
21682 return Math.abs(dataPt * value / 100);
21683 };
21684 }
21685 if(type === 'constant') {
21686 return function() {
21687 return Math.abs(value);
21688 };
21689 }
21690 if(type === 'sqrt') {
21691 return function(dataPt) {
21692 return Math.sqrt(Math.abs(dataPt));
21693 };
21694 }
21695}
21696
21697},{}],79:[function(_dereq_,module,exports){
21698/**
21699* Copyright 2012-2020, Plotly, Inc.
21700* All rights reserved.
21701*
21702* This source code is licensed under the MIT license found in the
21703* LICENSE file in the root directory of this source tree.
21704*/
21705
21706'use strict';
21707
21708var isNumeric = _dereq_('fast-isnumeric');
21709
21710var Registry = _dereq_('../../registry');
21711var Lib = _dereq_('../../lib');
21712var Template = _dereq_('../../plot_api/plot_template');
21713
21714var attributes = _dereq_('./attributes');
21715
21716
21717module.exports = function(traceIn, traceOut, defaultColor, opts) {
21718 var objName = 'error_' + opts.axis;
21719 var containerOut = Template.newContainer(traceOut, objName);
21720 var containerIn = traceIn[objName] || {};
21721
21722 function coerce(attr, dflt) {
21723 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
21724 }
21725
21726 var hasErrorBars = (
21727 containerIn.array !== undefined ||
21728 containerIn.value !== undefined ||
21729 containerIn.type === 'sqrt'
21730 );
21731
21732 var visible = coerce('visible', hasErrorBars);
21733
21734 if(visible === false) return;
21735
21736 var type = coerce('type', 'array' in containerIn ? 'data' : 'percent');
21737 var symmetric = true;
21738
21739 if(type !== 'sqrt') {
21740 symmetric = coerce('symmetric',
21741 !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn));
21742 }
21743
21744 if(type === 'data') {
21745 coerce('array');
21746 coerce('traceref');
21747 if(!symmetric) {
21748 coerce('arrayminus');
21749 coerce('tracerefminus');
21750 }
21751 } else if(type === 'percent' || type === 'constant') {
21752 coerce('value');
21753 if(!symmetric) coerce('valueminus');
21754 }
21755
21756 var copyAttr = 'copy_' + opts.inherit + 'style';
21757 if(opts.inherit) {
21758 var inheritObj = traceOut['error_' + opts.inherit];
21759 if((inheritObj || {}).visible) {
21760 coerce(copyAttr, !(containerIn.color ||
21761 isNumeric(containerIn.thickness) ||
21762 isNumeric(containerIn.width)));
21763 }
21764 }
21765 if(!opts.inherit || !containerOut[copyAttr]) {
21766 coerce('color', defaultColor);
21767 coerce('thickness');
21768 coerce('width', Registry.traceIs(traceOut, 'gl3d') ? 0 : 4);
21769 }
21770};
21771
21772},{"../../lib":178,"../../plot_api/plot_template":212,"../../registry":269,"./attributes":76,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){
21773/**
21774* Copyright 2012-2020, Plotly, Inc.
21775* All rights reserved.
21776*
21777* This source code is licensed under the MIT license found in the
21778* LICENSE file in the root directory of this source tree.
21779*/
21780
21781'use strict';
21782
21783var Lib = _dereq_('../../lib');
21784var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
21785
21786var attributes = _dereq_('./attributes');
21787
21788var xyAttrs = {
21789 error_x: Lib.extendFlat({}, attributes),
21790 error_y: Lib.extendFlat({}, attributes)
21791};
21792delete xyAttrs.error_x.copy_zstyle;
21793delete xyAttrs.error_y.copy_zstyle;
21794delete xyAttrs.error_y.copy_ystyle;
21795
21796var xyzAttrs = {
21797 error_x: Lib.extendFlat({}, attributes),
21798 error_y: Lib.extendFlat({}, attributes),
21799 error_z: Lib.extendFlat({}, attributes)
21800};
21801delete xyzAttrs.error_x.copy_ystyle;
21802delete xyzAttrs.error_y.copy_ystyle;
21803delete xyzAttrs.error_z.copy_ystyle;
21804delete xyzAttrs.error_z.copy_zstyle;
21805
21806module.exports = {
21807 moduleType: 'component',
21808 name: 'errorbars',
21809
21810 schema: {
21811 traces: {
21812 scatter: xyAttrs,
21813 bar: xyAttrs,
21814 histogram: xyAttrs,
21815 scatter3d: overrideAll(xyzAttrs, 'calc', 'nested'),
21816 scattergl: overrideAll(xyAttrs, 'calc', 'nested')
21817 }
21818 },
21819
21820 supplyDefaults: _dereq_('./defaults'),
21821
21822 calc: _dereq_('./calc'),
21823 makeComputeError: _dereq_('./compute_error'),
21824
21825 plot: _dereq_('./plot'),
21826 style: _dereq_('./style'),
21827 hoverInfo: hoverInfo
21828};
21829
21830function hoverInfo(calcPoint, trace, hoverPoint) {
21831 if((trace.error_y || {}).visible) {
21832 hoverPoint.yerr = calcPoint.yh - calcPoint.y;
21833 if(!trace.error_y.symmetric) hoverPoint.yerrneg = calcPoint.y - calcPoint.ys;
21834 }
21835 if((trace.error_x || {}).visible) {
21836 hoverPoint.xerr = calcPoint.xh - calcPoint.x;
21837 if(!trace.error_x.symmetric) hoverPoint.xerrneg = calcPoint.x - calcPoint.xs;
21838 }
21839}
21840
21841},{"../../lib":178,"../../plot_api/edit_types":205,"./attributes":76,"./calc":77,"./compute_error":78,"./defaults":79,"./plot":81,"./style":82}],81:[function(_dereq_,module,exports){
21842/**
21843* Copyright 2012-2020, Plotly, Inc.
21844* All rights reserved.
21845*
21846* This source code is licensed under the MIT license found in the
21847* LICENSE file in the root directory of this source tree.
21848*/
21849
21850
21851'use strict';
21852
21853var d3 = _dereq_('d3');
21854var isNumeric = _dereq_('fast-isnumeric');
21855
21856var Drawing = _dereq_('../drawing');
21857var subTypes = _dereq_('../../traces/scatter/subtypes');
21858
21859module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
21860 var isNew;
21861
21862 var xa = plotinfo.xaxis;
21863 var ya = plotinfo.yaxis;
21864
21865 var hasAnimation = transitionOpts && transitionOpts.duration > 0;
21866
21867 traces.each(function(d) {
21868 var trace = d[0].trace;
21869 // || {} is in case the trace (specifically scatterternary)
21870 // doesn't support error bars at all, but does go through
21871 // the scatter.plot mechanics, which calls ErrorBars.plot
21872 // internally
21873 var xObj = trace.error_x || {};
21874 var yObj = trace.error_y || {};
21875
21876 var keyFunc;
21877
21878 if(trace.ids) {
21879 keyFunc = function(d) {return d.id;};
21880 }
21881
21882 var sparse = (
21883 subTypes.hasMarkers(trace) &&
21884 trace.marker.maxdisplayed > 0
21885 );
21886
21887 if(!yObj.visible && !xObj.visible) d = [];
21888
21889 var errorbars = d3.select(this).selectAll('g.errorbar')
21890 .data(d, keyFunc);
21891
21892 errorbars.exit().remove();
21893
21894 if(!d.length) return;
21895
21896 if(!xObj.visible) errorbars.selectAll('path.xerror').remove();
21897 if(!yObj.visible) errorbars.selectAll('path.yerror').remove();
21898
21899 errorbars.style('opacity', 1);
21900
21901 var enter = errorbars.enter().append('g')
21902 .classed('errorbar', true);
21903
21904 if(hasAnimation) {
21905 enter.style('opacity', 0).transition()
21906 .duration(transitionOpts.duration)
21907 .style('opacity', 1);
21908 }
21909
21910 Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
21911
21912 errorbars.each(function(d) {
21913 var errorbar = d3.select(this);
21914 var coords = errorCoords(d, xa, ya);
21915
21916 if(sparse && !d.vis) return;
21917
21918 var path;
21919
21920 var yerror = errorbar.select('path.yerror');
21921 if(yObj.visible && isNumeric(coords.x) &&
21922 isNumeric(coords.yh) &&
21923 isNumeric(coords.ys)) {
21924 var yw = yObj.width;
21925
21926 path = 'M' + (coords.x - yw) + ',' +
21927 coords.yh + 'h' + (2 * yw) + // hat
21928 'm-' + yw + ',0V' + coords.ys; // bar
21929
21930
21931 if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
21932
21933 isNew = !yerror.size();
21934
21935 if(isNew) {
21936 yerror = errorbar.append('path')
21937 .style('vector-effect', 'non-scaling-stroke')
21938 .classed('yerror', true);
21939 } else if(hasAnimation) {
21940 yerror = yerror
21941 .transition()
21942 .duration(transitionOpts.duration)
21943 .ease(transitionOpts.easing);
21944 }
21945
21946 yerror.attr('d', path);
21947 } else yerror.remove();
21948
21949 var xerror = errorbar.select('path.xerror');
21950 if(xObj.visible && isNumeric(coords.y) &&
21951 isNumeric(coords.xh) &&
21952 isNumeric(coords.xs)) {
21953 var xw = (xObj.copy_ystyle ? yObj : xObj).width;
21954
21955 path = 'M' + coords.xh + ',' +
21956 (coords.y - xw) + 'v' + (2 * xw) + // hat
21957 'm0,-' + xw + 'H' + coords.xs; // bar
21958
21959 if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
21960
21961 isNew = !xerror.size();
21962
21963 if(isNew) {
21964 xerror = errorbar.append('path')
21965 .style('vector-effect', 'non-scaling-stroke')
21966 .classed('xerror', true);
21967 } else if(hasAnimation) {
21968 xerror = xerror
21969 .transition()
21970 .duration(transitionOpts.duration)
21971 .ease(transitionOpts.easing);
21972 }
21973
21974 xerror.attr('d', path);
21975 } else xerror.remove();
21976 });
21977 });
21978};
21979
21980// compute the coordinates of the error-bar objects
21981function errorCoords(d, xa, ya) {
21982 var out = {
21983 x: xa.c2p(d.x),
21984 y: ya.c2p(d.y)
21985 };
21986
21987 // calculate the error bar size and hat and shoe locations
21988 if(d.yh !== undefined) {
21989 out.yh = ya.c2p(d.yh);
21990 out.ys = ya.c2p(d.ys);
21991
21992 // if the shoes go off-scale (ie log scale, error bars past zero)
21993 // clip the bar and hide the shoes
21994 if(!isNumeric(out.ys)) {
21995 out.noYS = true;
21996 out.ys = ya.c2p(d.ys, true);
21997 }
21998 }
21999
22000 if(d.xh !== undefined) {
22001 out.xh = xa.c2p(d.xh);
22002 out.xs = xa.c2p(d.xs);
22003
22004 if(!isNumeric(out.xs)) {
22005 out.noXS = true;
22006 out.xs = xa.c2p(d.xs, true);
22007 }
22008 }
22009
22010 return out;
22011}
22012
22013},{"../../traces/scatter/subtypes":413,"../drawing":74,"d3":16,"fast-isnumeric":18}],82:[function(_dereq_,module,exports){
22014/**
22015* Copyright 2012-2020, Plotly, Inc.
22016* All rights reserved.
22017*
22018* This source code is licensed under the MIT license found in the
22019* LICENSE file in the root directory of this source tree.
22020*/
22021
22022
22023'use strict';
22024
22025var d3 = _dereq_('d3');
22026
22027var Color = _dereq_('../color');
22028
22029
22030module.exports = function style(traces) {
22031 traces.each(function(d) {
22032 var trace = d[0].trace;
22033 var yObj = trace.error_y || {};
22034 var xObj = trace.error_x || {};
22035
22036 var s = d3.select(this);
22037
22038 s.selectAll('path.yerror')
22039 .style('stroke-width', yObj.thickness + 'px')
22040 .call(Color.stroke, yObj.color);
22041
22042 if(xObj.copy_ystyle) xObj = yObj;
22043
22044 s.selectAll('path.xerror')
22045 .style('stroke-width', xObj.thickness + 'px')
22046 .call(Color.stroke, xObj.color);
22047 });
22048};
22049
22050},{"../color":52,"d3":16}],83:[function(_dereq_,module,exports){
22051/**
22052* Copyright 2012-2020, Plotly, Inc.
22053* All rights reserved.
22054*
22055* This source code is licensed under the MIT license found in the
22056* LICENSE file in the root directory of this source tree.
22057*/
22058
22059'use strict';
22060
22061var fontAttrs = _dereq_('../../plots/font_attributes');
22062var hoverLabelAttrs = _dereq_('./layout_attributes').hoverlabel;
22063var extendFlat = _dereq_('../../lib/extend').extendFlat;
22064
22065module.exports = {
22066 hoverlabel: {
22067 bgcolor: extendFlat({}, hoverLabelAttrs.bgcolor, {
22068 arrayOk: true,
22069
22070 }),
22071 bordercolor: extendFlat({}, hoverLabelAttrs.bordercolor, {
22072 arrayOk: true,
22073
22074 }),
22075 font: fontAttrs({
22076 arrayOk: true,
22077 editType: 'none',
22078
22079 }),
22080 align: extendFlat({}, hoverLabelAttrs.align, {arrayOk: true}),
22081 namelength: extendFlat({}, hoverLabelAttrs.namelength, {arrayOk: true}),
22082 editType: 'none'
22083 }
22084};
22085
22086},{"../../lib/extend":173,"../../plots/font_attributes":250,"./layout_attributes":93}],84:[function(_dereq_,module,exports){
22087/**
22088* Copyright 2012-2020, Plotly, Inc.
22089* All rights reserved.
22090*
22091* This source code is licensed under the MIT license found in the
22092* LICENSE file in the root directory of this source tree.
22093*/
22094
22095'use strict';
22096
22097var Lib = _dereq_('../../lib');
22098var Registry = _dereq_('../../registry');
22099
22100module.exports = function calc(gd) {
22101 var calcdata = gd.calcdata;
22102 var fullLayout = gd._fullLayout;
22103
22104 function makeCoerceHoverInfo(trace) {
22105 return function(val) {
22106 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
22107 };
22108 }
22109
22110 for(var i = 0; i < calcdata.length; i++) {
22111 var cd = calcdata[i];
22112 var trace = cd[0].trace;
22113
22114 // don't include hover calc fields for pie traces
22115 // as calcdata items might be sorted by value and
22116 // won't match the data array order.
22117 if(Registry.traceIs(trace, 'pie-like')) continue;
22118
22119 var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray;
22120
22121 fillFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace));
22122
22123 if(trace.hovertemplate) fillFn(trace.hovertemplate, cd, 'ht');
22124
22125 if(!trace.hoverlabel) continue;
22126
22127 fillFn(trace.hoverlabel.bgcolor, cd, 'hbg');
22128 fillFn(trace.hoverlabel.bordercolor, cd, 'hbc');
22129 fillFn(trace.hoverlabel.font.size, cd, 'hts');
22130 fillFn(trace.hoverlabel.font.color, cd, 'htc');
22131 fillFn(trace.hoverlabel.font.family, cd, 'htf');
22132 fillFn(trace.hoverlabel.namelength, cd, 'hnl');
22133 fillFn(trace.hoverlabel.align, cd, 'hta');
22134 }
22135};
22136
22137function paste(traceAttr, cd, cdAttr, fn) {
22138 fn = fn || Lib.identity;
22139
22140 if(Array.isArray(traceAttr)) {
22141 cd[0][cdAttr] = fn(traceAttr);
22142 }
22143}
22144
22145},{"../../lib":178,"../../registry":269}],85:[function(_dereq_,module,exports){
22146/**
22147* Copyright 2012-2020, Plotly, Inc.
22148* All rights reserved.
22149*
22150* This source code is licensed under the MIT license found in the
22151* LICENSE file in the root directory of this source tree.
22152*/
22153
22154'use strict';
22155
22156var Registry = _dereq_('../../registry');
22157var hover = _dereq_('./hover').hover;
22158
22159module.exports = function click(gd, evt, subplot) {
22160 var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
22161
22162 // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
22163 // Ternary, for example, didn't, but it was caught because tested.
22164 if(subplot !== undefined) {
22165 // The true flag at the end causes it to re-run the hover computation to figure out *which*
22166 // point is being clicked. Without this, clicking is somewhat unreliable.
22167 hover(gd, evt, subplot, true);
22168 }
22169
22170 function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
22171
22172 if(gd._hoverdata && evt && evt.target) {
22173 if(annotationsDone && annotationsDone.then) {
22174 annotationsDone.then(emitClick);
22175 } else emitClick();
22176
22177 // why do we get a double event without this???
22178 if(evt.stopImmediatePropagation) evt.stopImmediatePropagation();
22179 }
22180};
22181
22182},{"../../registry":269,"./hover":89}],86:[function(_dereq_,module,exports){
22183/**
22184* Copyright 2012-2020, Plotly, Inc.
22185* All rights reserved.
22186*
22187* This source code is licensed under the MIT license found in the
22188* LICENSE file in the root directory of this source tree.
22189*/
22190
22191'use strict';
22192
22193module.exports = {
22194 // hover labels for multiple horizontal bars get tilted by this angle
22195 YANGLE: 60,
22196
22197 // size and display constants for hover text
22198
22199 // pixel size of hover arrows
22200 HOVERARROWSIZE: 6,
22201 // pixels padding around text
22202 HOVERTEXTPAD: 3,
22203 // hover font
22204 HOVERFONTSIZE: 13,
22205 HOVERFONT: 'Arial, sans-serif',
22206
22207 // minimum time (msec) between hover calls
22208 HOVERMINTIME: 50,
22209
22210 // ID suffix (with fullLayout._uid) for hover events in the throttle cache
22211 HOVERID: '-hover'
22212};
22213
22214},{}],87:[function(_dereq_,module,exports){
22215/**
22216* Copyright 2012-2020, Plotly, Inc.
22217* All rights reserved.
22218*
22219* This source code is licensed under the MIT license found in the
22220* LICENSE file in the root directory of this source tree.
22221*/
22222
22223'use strict';
22224
22225var Lib = _dereq_('../../lib');
22226var attributes = _dereq_('./attributes');
22227var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
22228
22229module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
22230 function coerce(attr, dflt) {
22231 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
22232 }
22233
22234 var opts = Lib.extendFlat({}, layout.hoverlabel);
22235 if(traceOut.hovertemplate) opts.namelength = -1;
22236
22237 handleHoverLabelDefaults(traceIn, traceOut, coerce, opts);
22238};
22239
22240},{"../../lib":178,"./attributes":83,"./hoverlabel_defaults":90}],88:[function(_dereq_,module,exports){
22241/**
22242* Copyright 2012-2020, Plotly, Inc.
22243* All rights reserved.
22244*
22245* This source code is licensed under the MIT license found in the
22246* LICENSE file in the root directory of this source tree.
22247*/
22248
22249'use strict';
22250
22251var Lib = _dereq_('../../lib');
22252
22253// look for either subplot or xaxis and yaxis attributes
22254// does not handle splom case
22255exports.getSubplot = function(trace) {
22256 return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo;
22257};
22258
22259// is trace in given list of subplots?
22260// does handle splom case
22261exports.isTraceInSubplots = function(trace, subplots) {
22262 if(trace.type === 'splom') {
22263 var xaxes = trace.xaxes || [];
22264 var yaxes = trace.yaxes || [];
22265 for(var i = 0; i < xaxes.length; i++) {
22266 for(var j = 0; j < yaxes.length; j++) {
22267 if(subplots.indexOf(xaxes[i] + yaxes[j]) !== -1) {
22268 return true;
22269 }
22270 }
22271 }
22272 return false;
22273 }
22274
22275 return subplots.indexOf(exports.getSubplot(trace)) !== -1;
22276};
22277
22278// convenience functions for mapping all relevant axes
22279exports.flat = function(subplots, v) {
22280 var out = new Array(subplots.length);
22281 for(var i = 0; i < subplots.length; i++) {
22282 out[i] = v;
22283 }
22284 return out;
22285};
22286
22287exports.p2c = function(axArray, v) {
22288 var out = new Array(axArray.length);
22289 for(var i = 0; i < axArray.length; i++) {
22290 out[i] = axArray[i].p2c(v);
22291 }
22292 return out;
22293};
22294
22295exports.getDistanceFunction = function(mode, dx, dy, dxy) {
22296 if(mode === 'closest') return dxy || exports.quadrature(dx, dy);
22297 return mode.charAt(0) === 'x' ? dx : dy;
22298};
22299
22300exports.getClosest = function(cd, distfn, pointData) {
22301 // do we already have a point number? (array mode only)
22302 if(pointData.index !== false) {
22303 if(pointData.index >= 0 && pointData.index < cd.length) {
22304 pointData.distance = 0;
22305 } else pointData.index = false;
22306 } else {
22307 // apply the distance function to each data point
22308 // this is the longest loop... if this bogs down, we may need
22309 // to create pre-sorted data (by x or y), not sure how to
22310 // do this for 'closest'
22311 for(var i = 0; i < cd.length; i++) {
22312 var newDistance = distfn(cd[i]);
22313 if(newDistance <= pointData.distance) {
22314 pointData.index = i;
22315 pointData.distance = newDistance;
22316 }
22317 }
22318 }
22319 return pointData;
22320};
22321
22322/*
22323 * pseudo-distance function for hover effects on areas: inside the region
22324 * distance is finite (`passVal`), outside it's Infinity.
22325 *
22326 * @param {number} v0: signed difference between the current position and the left edge
22327 * @param {number} v1: signed difference between the current position and the right edge
22328 * @param {number} passVal: the value to return on success
22329 */
22330exports.inbox = function(v0, v1, passVal) {
22331 return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity;
22332};
22333
22334exports.quadrature = function(dx, dy) {
22335 return function(di) {
22336 var x = dx(di);
22337 var y = dy(di);
22338 return Math.sqrt(x * x + y * y);
22339 };
22340};
22341
22342/** Fill event data point object for hover and selection.
22343 * Invokes _module.eventData if present.
22344 *
22345 * N.B. note that point 'index' corresponds to input data array index
22346 * whereas 'number' is its post-transform version.
22347 *
22348 * If the hovered/selected pt corresponds to an multiple input points
22349 * (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices'
22350 * are include in the event data.
22351 *
22352 * @param {object} pt
22353 * @param {object} trace
22354 * @param {object} cd
22355 * @return {object}
22356 */
22357exports.makeEventData = function(pt, trace, cd) {
22358 // hover uses 'index', select uses 'pointNumber'
22359 var pointNumber = 'index' in pt ? pt.index : pt.pointNumber;
22360
22361 var out = {
22362 data: trace._input,
22363 fullData: trace,
22364 curveNumber: trace.index,
22365 pointNumber: pointNumber
22366 };
22367
22368 if(trace._indexToPoints) {
22369 var pointIndices = trace._indexToPoints[pointNumber];
22370
22371 if(pointIndices.length === 1) {
22372 out.pointIndex = pointIndices[0];
22373 } else {
22374 out.pointIndices = pointIndices;
22375 }
22376 } else {
22377 out.pointIndex = pointNumber;
22378 }
22379
22380 if(trace._module.eventData) {
22381 out = trace._module.eventData(out, pt, trace, cd, pointNumber);
22382 } else {
22383 if('xVal' in pt) out.x = pt.xVal;
22384 else if('x' in pt) out.x = pt.x;
22385
22386 if('yVal' in pt) out.y = pt.yVal;
22387 else if('y' in pt) out.y = pt.y;
22388
22389 if(pt.xa) out.xaxis = pt.xa;
22390 if(pt.ya) out.yaxis = pt.ya;
22391 if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
22392 }
22393
22394 exports.appendArrayPointValue(out, trace, pointNumber);
22395
22396 return out;
22397};
22398
22399/** Appends values inside array attributes corresponding to given point number
22400 *
22401 * @param {object} pointData : point data object (gets mutated here)
22402 * @param {object} trace : full trace object
22403 * @param {number|Array(number)} pointNumber : point number. May be a length-2 array
22404 * [row, col] to dig into 2D arrays
22405 */
22406exports.appendArrayPointValue = function(pointData, trace, pointNumber) {
22407 var arrayAttrs = trace._arrayAttrs;
22408
22409 if(!arrayAttrs) {
22410 return;
22411 }
22412
22413 for(var i = 0; i < arrayAttrs.length; i++) {
22414 var astr = arrayAttrs[i];
22415 var key = getPointKey(astr);
22416
22417 if(pointData[key] === undefined) {
22418 var val = Lib.nestedProperty(trace, astr).get();
22419 var pointVal = getPointData(val, pointNumber);
22420
22421 if(pointVal !== undefined) pointData[key] = pointVal;
22422 }
22423 }
22424};
22425
22426/**
22427 * Appends values inside array attributes corresponding to given point number array
22428 * For use when pointData references a plot entity that arose (or potentially arose)
22429 * from multiple points in the input data
22430 *
22431 * @param {object} pointData : point data object (gets mutated here)
22432 * @param {object} trace : full trace object
22433 * @param {Array(number)|Array(Array(number))} pointNumbers : Array of point numbers.
22434 * Each entry in the array may itself be a length-2 array [row, col] to dig into 2D arrays
22435 */
22436exports.appendArrayMultiPointValues = function(pointData, trace, pointNumbers) {
22437 var arrayAttrs = trace._arrayAttrs;
22438
22439 if(!arrayAttrs) {
22440 return;
22441 }
22442
22443 for(var i = 0; i < arrayAttrs.length; i++) {
22444 var astr = arrayAttrs[i];
22445 var key = getPointKey(astr);
22446
22447 if(pointData[key] === undefined) {
22448 var val = Lib.nestedProperty(trace, astr).get();
22449 var keyVal = new Array(pointNumbers.length);
22450
22451 for(var j = 0; j < pointNumbers.length; j++) {
22452 keyVal[j] = getPointData(val, pointNumbers[j]);
22453 }
22454 pointData[key] = keyVal;
22455 }
22456 }
22457};
22458
22459var pointKeyMap = {
22460 ids: 'id',
22461 locations: 'location',
22462 labels: 'label',
22463 values: 'value',
22464 'marker.colors': 'color',
22465 parents: 'parent'
22466};
22467
22468function getPointKey(astr) {
22469 return pointKeyMap[astr] || astr;
22470}
22471
22472function getPointData(val, pointNumber) {
22473 if(Array.isArray(pointNumber)) {
22474 if(Array.isArray(val) && Array.isArray(val[pointNumber[0]])) {
22475 return val[pointNumber[0]][pointNumber[1]];
22476 }
22477 } else {
22478 return val[pointNumber];
22479 }
22480}
22481
22482var xyHoverMode = {
22483 x: true,
22484 y: true
22485};
22486
22487var unifiedHoverMode = {
22488 'x unified': true,
22489 'y unified': true
22490};
22491
22492exports.isUnifiedHover = function(hovermode) {
22493 if(typeof hovermode !== 'string') return false;
22494 return !!unifiedHoverMode[hovermode];
22495};
22496
22497exports.isXYhover = function(hovermode) {
22498 if(typeof hovermode !== 'string') return false;
22499 return !!xyHoverMode[hovermode];
22500};
22501
22502},{"../../lib":178}],89:[function(_dereq_,module,exports){
22503/**
22504* Copyright 2012-2020, Plotly, Inc.
22505* All rights reserved.
22506*
22507* This source code is licensed under the MIT license found in the
22508* LICENSE file in the root directory of this source tree.
22509*/
22510
22511'use strict';
22512
22513var d3 = _dereq_('d3');
22514var isNumeric = _dereq_('fast-isnumeric');
22515var tinycolor = _dereq_('tinycolor2');
22516
22517var Lib = _dereq_('../../lib');
22518var Events = _dereq_('../../lib/events');
22519var svgTextUtils = _dereq_('../../lib/svg_text_utils');
22520var overrideCursor = _dereq_('../../lib/override_cursor');
22521var Drawing = _dereq_('../drawing');
22522var Color = _dereq_('../color');
22523var dragElement = _dereq_('../dragelement');
22524var Axes = _dereq_('../../plots/cartesian/axes');
22525var Registry = _dereq_('../../registry');
22526
22527var helpers = _dereq_('./helpers');
22528var constants = _dereq_('./constants');
22529
22530var legendSupplyDefaults = _dereq_('../legend/defaults');
22531var legendDraw = _dereq_('../legend/draw');
22532
22533// hover labels for multiple horizontal bars get tilted by some angle,
22534// then need to be offset differently if they overlap
22535var YANGLE = constants.YANGLE;
22536var YA_RADIANS = Math.PI * YANGLE / 180;
22537
22538// expansion of projected height
22539var YFACTOR = 1 / Math.sin(YA_RADIANS);
22540
22541// to make the appropriate post-rotation x offset,
22542// you need both x and y offsets
22543var YSHIFTX = Math.cos(YA_RADIANS);
22544var YSHIFTY = Math.sin(YA_RADIANS);
22545
22546// size and display constants for hover text
22547var HOVERARROWSIZE = constants.HOVERARROWSIZE;
22548var HOVERTEXTPAD = constants.HOVERTEXTPAD;
22549
22550// fx.hover: highlight data on hover
22551// evt can be a mousemove event, or an object with data about what points
22552// to hover on
22553// {xpx,ypx[,hovermode]} - pixel locations from top left
22554// (with optional overriding hovermode)
22555// {xval,yval[,hovermode]} - data values
22556// [{curveNumber,(pointNumber|xval and/or yval)}] -
22557// array of specific points to highlight
22558// pointNumber is a single integer if gd.data[curveNumber] is 1D,
22559// or a two-element array if it's 2D
22560// xval and yval are data values,
22561// 1D data may specify either or both,
22562// 2D data must specify both
22563// subplot is an id string (default "xy")
22564// makes use of gl.hovermode, which can be:
22565// x (find the points with the closest x values, ie a column),
22566// closest (find the single closest point)
22567// internally there are two more that occasionally get used:
22568// y (pick out a row - only used for multiple horizontal bar charts)
22569// array (used when the user specifies an explicit
22570// array of points to hover on)
22571//
22572// We wrap the hovers in a timer, to limit their frequency.
22573// The actual rendering is done by private function _hover.
22574exports.hover = function hover(gd, evt, subplot, noHoverEvent) {
22575 gd = Lib.getGraphDiv(gd);
22576
22577 Lib.throttle(
22578 gd._fullLayout._uid + constants.HOVERID,
22579 constants.HOVERMINTIME,
22580 function() { _hover(gd, evt, subplot, noHoverEvent); }
22581 );
22582};
22583
22584/*
22585 * Draw a single hover item or an array of hover item in a pre-existing svg container somewhere
22586 * hoverItem should have keys:
22587 * - x and y (or x0, x1, y0, and y1):
22588 * the pixel position to mark, relative to opts.container
22589 * - xLabel, yLabel, zLabel, text, and name:
22590 * info to go in the label
22591 * - color:
22592 * the background color for the label.
22593 * - idealAlign (optional):
22594 * 'left' or 'right' for which side of the x/y box to try to put this on first
22595 * - borderColor (optional):
22596 * color for the border, defaults to strongest contrast with color
22597 * - fontFamily (optional):
22598 * string, the font for this label, defaults to constants.HOVERFONT
22599 * - fontSize (optional):
22600 * the label font size, defaults to constants.HOVERFONTSIZE
22601 * - fontColor (optional):
22602 * defaults to borderColor
22603 * opts should have keys:
22604 * - bgColor:
22605 * the background color this is against, used if the trace is
22606 * non-opaque, and for the name, which goes outside the box
22607 * - container:
22608 * a <svg> or <g> element to add the hover label to
22609 * - outerContainer:
22610 * normally a parent of `container`, sets the bounding box to use to
22611 * constrain the hover label and determine whether to show it on the left or right
22612 * opts can have optional keys:
22613 * - anchorIndex:
22614 the index of the hover item used as an anchor for positioning.
22615 The other hover items will be pushed up or down to prevent overlap.
22616 */
22617exports.loneHover = function loneHover(hoverItems, opts) {
22618 var multiHover = true;
22619 if(!Array.isArray(hoverItems)) {
22620 multiHover = false;
22621 hoverItems = [hoverItems];
22622 }
22623
22624 var pointsData = hoverItems.map(function(hoverItem) {
22625 return {
22626 color: hoverItem.color || Color.defaultLine,
22627 x0: hoverItem.x0 || hoverItem.x || 0,
22628 x1: hoverItem.x1 || hoverItem.x || 0,
22629 y0: hoverItem.y0 || hoverItem.y || 0,
22630 y1: hoverItem.y1 || hoverItem.y || 0,
22631 xLabel: hoverItem.xLabel,
22632 yLabel: hoverItem.yLabel,
22633 zLabel: hoverItem.zLabel,
22634 text: hoverItem.text,
22635 name: hoverItem.name,
22636 idealAlign: hoverItem.idealAlign,
22637
22638 // optional extra bits of styling
22639 borderColor: hoverItem.borderColor,
22640 fontFamily: hoverItem.fontFamily,
22641 fontSize: hoverItem.fontSize,
22642 fontColor: hoverItem.fontColor,
22643 nameLength: hoverItem.nameLength,
22644 textAlign: hoverItem.textAlign,
22645
22646 // filler to make createHoverText happy
22647 trace: hoverItem.trace || {
22648 index: 0,
22649 hoverinfo: ''
22650 },
22651 xa: {_offset: 0},
22652 ya: {_offset: 0},
22653 index: 0,
22654
22655 hovertemplate: hoverItem.hovertemplate || false,
22656 eventData: hoverItem.eventData || false,
22657 hovertemplateLabels: hoverItem.hovertemplateLabels || false,
22658 };
22659 });
22660
22661 var container3 = d3.select(opts.container);
22662 var outerContainer3 = opts.outerContainer ? d3.select(opts.outerContainer) : container3;
22663
22664 var fullOpts = {
22665 hovermode: 'closest',
22666 rotateLabels: false,
22667 bgColor: opts.bgColor || Color.background,
22668 container: container3,
22669 outerContainer: outerContainer3
22670 };
22671
22672 var hoverLabel = createHoverText(pointsData, fullOpts, opts.gd);
22673
22674 // Fix vertical overlap
22675 var tooltipSpacing = 5;
22676 var lastBottomY = 0;
22677 var anchor = 0;
22678 hoverLabel
22679 .sort(function(a, b) {return a.y0 - b.y0;})
22680 .each(function(d, i) {
22681 var topY = d.y0 - d.by / 2;
22682
22683 if((topY - tooltipSpacing) < lastBottomY) {
22684 d.offset = (lastBottomY - topY) + tooltipSpacing;
22685 } else {
22686 d.offset = 0;
22687 }
22688
22689 lastBottomY = topY + d.by + d.offset;
22690
22691 if(i === opts.anchorIndex || 0) anchor = d.offset;
22692 })
22693 .each(function(d) {
22694 d.offset -= anchor;
22695 });
22696
22697 alignHoverText(hoverLabel, fullOpts.rotateLabels);
22698
22699 return multiHover ? hoverLabel : hoverLabel.node();
22700};
22701
22702// The actual implementation is here:
22703function _hover(gd, evt, subplot, noHoverEvent) {
22704 if(!subplot) subplot = 'xy';
22705
22706 // if the user passed in an array of subplots,
22707 // use those instead of finding overlayed plots
22708 var subplots = Array.isArray(subplot) ? subplot : [subplot];
22709
22710 var fullLayout = gd._fullLayout;
22711 var plots = fullLayout._plots || [];
22712 var plotinfo = plots[subplot];
22713 var hasCartesian = fullLayout._has('cartesian');
22714
22715 // list of all overlaid subplots to look at
22716 if(plotinfo) {
22717 var overlayedSubplots = plotinfo.overlays.map(function(pi) {
22718 return pi.id;
22719 });
22720
22721 subplots = subplots.concat(overlayedSubplots);
22722 }
22723
22724 var len = subplots.length;
22725 var xaArray = new Array(len);
22726 var yaArray = new Array(len);
22727 var supportsCompare = false;
22728
22729 for(var i = 0; i < len; i++) {
22730 var spId = subplots[i];
22731
22732 if(plots[spId]) {
22733 // 'cartesian' case
22734 supportsCompare = true;
22735 xaArray[i] = plots[spId].xaxis;
22736 yaArray[i] = plots[spId].yaxis;
22737 } else if(fullLayout[spId] && fullLayout[spId]._subplot) {
22738 // other subplot types
22739 var _subplot = fullLayout[spId]._subplot;
22740 xaArray[i] = _subplot.xaxis;
22741 yaArray[i] = _subplot.yaxis;
22742 } else {
22743 Lib.warn('Unrecognized subplot: ' + spId);
22744 return;
22745 }
22746 }
22747
22748 var hovermode = evt.hovermode || fullLayout.hovermode;
22749
22750 if(hovermode && !supportsCompare) hovermode = 'closest';
22751
22752 if(['x', 'y', 'closest', 'x unified', 'y unified'].indexOf(hovermode) === -1 || !gd.calcdata ||
22753 gd.querySelector('.zoombox') || gd._dragging) {
22754 return dragElement.unhoverRaw(gd, evt);
22755 }
22756
22757 var hoverdistance = fullLayout.hoverdistance === -1 ? Infinity : fullLayout.hoverdistance;
22758 var spikedistance = fullLayout.spikedistance === -1 ? Infinity : fullLayout.spikedistance;
22759
22760 // hoverData: the set of candidate points we've found to highlight
22761 var hoverData = [];
22762
22763 // searchData: the data to search in. Mostly this is just a copy of
22764 // gd.calcdata, filtered to the subplot and overlays we're on
22765 // but if a point array is supplied it will be a mapping
22766 // of indicated curves
22767 var searchData = [];
22768
22769 // [x|y]valArray: the axis values of the hover event
22770 // mapped onto each of the currently selected overlaid subplots
22771 var xvalArray, yvalArray;
22772
22773 var itemnum, curvenum, cd, trace, subplotId, subploti, mode,
22774 xval, yval, pointData, closedataPreviousLength;
22775
22776 // spikePoints: the set of candidate points we've found to draw spikes to
22777 var spikePoints = {
22778 hLinePoint: null,
22779 vLinePoint: null
22780 };
22781
22782 // does subplot have one (or more) horizontal traces?
22783 // This is used to determine whether we rotate the labels or not
22784 var hasOneHorizontalTrace = false;
22785
22786 // Figure out what we're hovering on:
22787 // mouse location or user-supplied data
22788
22789 if(Array.isArray(evt)) {
22790 // user specified an array of points to highlight
22791 hovermode = 'array';
22792 for(itemnum = 0; itemnum < evt.length; itemnum++) {
22793 cd = gd.calcdata[evt[itemnum].curveNumber || 0];
22794 if(cd) {
22795 trace = cd[0].trace;
22796 if(cd[0].trace.hoverinfo !== 'skip') {
22797 searchData.push(cd);
22798 if(trace.orientation === 'h') {
22799 hasOneHorizontalTrace = true;
22800 }
22801 }
22802 }
22803 }
22804 } else {
22805 for(curvenum = 0; curvenum < gd.calcdata.length; curvenum++) {
22806 cd = gd.calcdata[curvenum];
22807 trace = cd[0].trace;
22808 if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) {
22809 searchData.push(cd);
22810 if(trace.orientation === 'h') {
22811 hasOneHorizontalTrace = true;
22812 }
22813 }
22814 }
22815
22816 // [x|y]px: the pixels (from top left) of the mouse location
22817 // on the currently selected plot area
22818 // add pointerX|Y property for drawing the spikes in spikesnap 'cursor' situation
22819 var hasUserCalledHover = !evt.target;
22820 var xpx, ypx;
22821
22822 if(hasUserCalledHover) {
22823 if('xpx' in evt) xpx = evt.xpx;
22824 else xpx = xaArray[0]._length / 2;
22825
22826 if('ypx' in evt) ypx = evt.ypx;
22827 else ypx = yaArray[0]._length / 2;
22828 } else {
22829 // fire the beforehover event and quit if it returns false
22830 // note that we're only calling this on real mouse events, so
22831 // manual calls to fx.hover will always run.
22832 if(Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
22833 return;
22834 }
22835
22836 var dbb = evt.target.getBoundingClientRect();
22837
22838 xpx = evt.clientX - dbb.left;
22839 ypx = evt.clientY - dbb.top;
22840
22841 // in case hover was called from mouseout into hovertext,
22842 // it's possible you're not actually over the plot anymore
22843 if(xpx < 0 || xpx > xaArray[0]._length || ypx < 0 || ypx > yaArray[0]._length) {
22844 return dragElement.unhoverRaw(gd, evt);
22845 }
22846 }
22847
22848 evt.pointerX = xpx + xaArray[0]._offset;
22849 evt.pointerY = ypx + yaArray[0]._offset;
22850
22851 if('xval' in evt) xvalArray = helpers.flat(subplots, evt.xval);
22852 else xvalArray = helpers.p2c(xaArray, xpx);
22853
22854 if('yval' in evt) yvalArray = helpers.flat(subplots, evt.yval);
22855 else yvalArray = helpers.p2c(yaArray, ypx);
22856
22857 if(!isNumeric(xvalArray[0]) || !isNumeric(yvalArray[0])) {
22858 Lib.warn('Fx.hover failed', evt, gd);
22859 return dragElement.unhoverRaw(gd, evt);
22860 }
22861 }
22862
22863 // the pixel distance to beat as a matching point
22864 // in 'x' or 'y' mode this resets for each trace
22865 var distance = Infinity;
22866
22867 // find the closest point in each trace
22868 // this is minimum dx and/or dy, depending on mode
22869 // and the pixel position for the label (labelXpx, labelYpx)
22870 function findHoverPoints(customXVal, customYVal) {
22871 for(curvenum = 0; curvenum < searchData.length; curvenum++) {
22872 cd = searchData[curvenum];
22873
22874 // filter out invisible or broken data
22875 if(!cd || !cd[0] || !cd[0].trace) continue;
22876
22877 trace = cd[0].trace;
22878
22879 if(trace.visible !== true || trace._length === 0) continue;
22880
22881 // Explicitly bail out for these two. I don't know how to otherwise prevent
22882 // the rest of this function from running and failing
22883 if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue;
22884
22885 if(trace.type === 'splom') {
22886 // splom traces do not generate overlay subplots,
22887 // it is safe to assume here splom traces correspond to the 0th subplot
22888 subploti = 0;
22889 subplotId = subplots[subploti];
22890 } else {
22891 subplotId = helpers.getSubplot(trace);
22892 subploti = subplots.indexOf(subplotId);
22893 }
22894
22895 // within one trace mode can sometimes be overridden
22896 mode = hovermode;
22897 if(helpers.isUnifiedHover(mode)) {
22898 mode = mode.charAt(0);
22899 }
22900
22901 // container for new point, also used to pass info into module.hoverPoints
22902 pointData = {
22903 // trace properties
22904 cd: cd,
22905 trace: trace,
22906 xa: xaArray[subploti],
22907 ya: yaArray[subploti],
22908
22909 // max distances for hover and spikes - for points that want to show but do not
22910 // want to override other points, set distance/spikeDistance equal to max*Distance
22911 // and it will not get filtered out but it will be guaranteed to have a greater
22912 // distance than any point that calculated a real distance.
22913 maxHoverDistance: hoverdistance,
22914 maxSpikeDistance: spikedistance,
22915
22916 // point properties - override all of these
22917 index: false, // point index in trace - only used by plotly.js hoverdata consumers
22918 distance: Math.min(distance, hoverdistance), // pixel distance or pseudo-distance
22919
22920 // distance/pseudo-distance for spikes. This distance should always be calculated
22921 // as if in "closest" mode, and should only be set if this point should
22922 // generate a spike.
22923 spikeDistance: Infinity,
22924
22925 // in some cases the spikes have different positioning from the hover label
22926 // they don't need x0/x1, just one position
22927 xSpike: undefined,
22928 ySpike: undefined,
22929
22930 // where and how to display the hover label
22931 color: Color.defaultLine, // trace color
22932 name: trace.name,
22933 x0: undefined,
22934 x1: undefined,
22935 y0: undefined,
22936 y1: undefined,
22937 xLabelVal: undefined,
22938 yLabelVal: undefined,
22939 zLabelVal: undefined,
22940 text: undefined
22941 };
22942
22943 // add ref to subplot object (non-cartesian case)
22944 if(fullLayout[subplotId]) {
22945 pointData.subplot = fullLayout[subplotId]._subplot;
22946 }
22947 // add ref to splom scene
22948 if(fullLayout._splomScenes && fullLayout._splomScenes[trace.uid]) {
22949 pointData.scene = fullLayout._splomScenes[trace.uid];
22950 }
22951
22952 closedataPreviousLength = hoverData.length;
22953
22954 // for a highlighting array, figure out what
22955 // we're searching for with this element
22956 if(mode === 'array') {
22957 var selection = evt[curvenum];
22958 if('pointNumber' in selection) {
22959 pointData.index = selection.pointNumber;
22960 mode = 'closest';
22961 } else {
22962 mode = '';
22963 if('xval' in selection) {
22964 xval = selection.xval;
22965 mode = 'x';
22966 }
22967 if('yval' in selection) {
22968 yval = selection.yval;
22969 mode = mode ? 'closest' : 'y';
22970 }
22971 }
22972 } else if(customXVal !== undefined && customYVal !== undefined) {
22973 xval = customXVal;
22974 yval = customYVal;
22975 } else {
22976 xval = xvalArray[subploti];
22977 yval = yvalArray[subploti];
22978 }
22979
22980 // Now if there is range to look in, find the points to hover.
22981 if(hoverdistance !== 0) {
22982 if(trace._module && trace._module.hoverPoints) {
22983 var newPoints = trace._module.hoverPoints(pointData, xval, yval, mode, fullLayout._hoverlayer);
22984 if(newPoints) {
22985 var newPoint;
22986 for(var newPointNum = 0; newPointNum < newPoints.length; newPointNum++) {
22987 newPoint = newPoints[newPointNum];
22988 if(isNumeric(newPoint.x0) && isNumeric(newPoint.y0)) {
22989 hoverData.push(cleanPoint(newPoint, hovermode));
22990 }
22991 }
22992 }
22993 } else {
22994 Lib.log('Unrecognized trace type in hover:', trace);
22995 }
22996 }
22997
22998 // in closest mode, remove any existing (farther) points
22999 // and don't look any farther than this latest point (or points, some
23000 // traces like box & violin make multiple hover labels at once)
23001 if(hovermode === 'closest' && hoverData.length > closedataPreviousLength) {
23002 hoverData.splice(0, closedataPreviousLength);
23003 distance = hoverData[0].distance;
23004 }
23005
23006 // Now if there is range to look in, find the points to draw the spikelines
23007 // Do it only if there is no hoverData
23008 if(hasCartesian && (spikedistance !== 0)) {
23009 if(hoverData.length === 0) {
23010 pointData.distance = spikedistance;
23011 pointData.index = false;
23012 var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', fullLayout._hoverlayer);
23013 if(closestPoints) {
23014 closestPoints = closestPoints.filter(function(point) {
23015 // some hover points, like scatter fills, do not allow spikes,
23016 // so will generate a hover point but without a valid spikeDistance
23017 return point.spikeDistance <= spikedistance;
23018 });
23019 }
23020 if(closestPoints && closestPoints.length) {
23021 var tmpPoint;
23022 var closestVPoints = closestPoints.filter(function(point) {
23023 return point.xa.showspikes && point.xa.spikesnap !== 'hovered data';
23024 });
23025 if(closestVPoints.length) {
23026 var closestVPt = closestVPoints[0];
23027 if(isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
23028 tmpPoint = fillSpikePoint(closestVPt);
23029 if(!spikePoints.vLinePoint || (spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
23030 spikePoints.vLinePoint = tmpPoint;
23031 }
23032 }
23033 }
23034
23035 var closestHPoints = closestPoints.filter(function(point) {
23036 return point.ya.showspikes && point.ya.spikesnap !== 'hovered data';
23037 });
23038 if(closestHPoints.length) {
23039 var closestHPt = closestHPoints[0];
23040 if(isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
23041 tmpPoint = fillSpikePoint(closestHPt);
23042 if(!spikePoints.hLinePoint || (spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
23043 spikePoints.hLinePoint = tmpPoint;
23044 }
23045 }
23046 }
23047 }
23048 }
23049 }
23050 }
23051 }
23052
23053 findHoverPoints();
23054
23055 function selectClosestPoint(pointsData, spikedistance) {
23056 var resultPoint = null;
23057 var minDistance = Infinity;
23058 var thisSpikeDistance;
23059 for(var i = 0; i < pointsData.length; i++) {
23060 thisSpikeDistance = pointsData[i].spikeDistance;
23061 if(thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance) {
23062 resultPoint = pointsData[i];
23063 minDistance = thisSpikeDistance;
23064 }
23065 }
23066 return resultPoint;
23067 }
23068
23069 function fillSpikePoint(point) {
23070 if(!point) return null;
23071 return {
23072 xa: point.xa,
23073 ya: point.ya,
23074 x: point.xSpike !== undefined ? point.xSpike : (point.x0 + point.x1) / 2,
23075 y: point.ySpike !== undefined ? point.ySpike : (point.y0 + point.y1) / 2,
23076 distance: point.distance,
23077 spikeDistance: point.spikeDistance,
23078 curveNumber: point.trace.index,
23079 color: point.color,
23080 pointNumber: point.index
23081 };
23082 }
23083
23084 var spikelineOpts = {
23085 fullLayout: fullLayout,
23086 container: fullLayout._hoverlayer,
23087 outerContainer: fullLayout._paperdiv,
23088 event: evt
23089 };
23090 var oldspikepoints = gd._spikepoints;
23091 var newspikepoints = {
23092 vLinePoint: spikePoints.vLinePoint,
23093 hLinePoint: spikePoints.hLinePoint
23094 };
23095 gd._spikepoints = newspikepoints;
23096
23097 // Now if it is not restricted by spikedistance option, set the points to draw the spikelines
23098 if(hasCartesian && (spikedistance !== 0)) {
23099 if(hoverData.length !== 0) {
23100 var tmpHPointData = hoverData.filter(function(point) {
23101 return point.ya.showspikes;
23102 });
23103 var tmpHPoint = selectClosestPoint(tmpHPointData, spikedistance);
23104 spikePoints.hLinePoint = fillSpikePoint(tmpHPoint);
23105
23106 var tmpVPointData = hoverData.filter(function(point) {
23107 return point.xa.showspikes;
23108 });
23109 var tmpVPoint = selectClosestPoint(tmpVPointData, spikedistance);
23110 spikePoints.vLinePoint = fillSpikePoint(tmpVPoint);
23111 }
23112 }
23113
23114 // if hoverData is empty check for the spikes to draw and quit if there are none
23115 if(hoverData.length === 0) {
23116 var result = dragElement.unhoverRaw(gd, evt);
23117 if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) {
23118 if(spikesChanged(oldspikepoints)) {
23119 createSpikelines(gd, spikePoints, spikelineOpts);
23120 }
23121 }
23122 return result;
23123 }
23124
23125 if(hasCartesian) {
23126 if(spikesChanged(oldspikepoints)) {
23127 createSpikelines(gd, spikePoints, spikelineOpts);
23128 }
23129 }
23130
23131 hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
23132
23133 // If in compare mode, select every point at position
23134 if(
23135 helpers.isXYhover(mode) &&
23136 hoverData[0].length !== 0 &&
23137 hoverData[0].trace.type !== 'splom' // TODO: add support for splom
23138 ) {
23139 var hd = hoverData[0];
23140 var cd0 = hd.cd[hd.index];
23141 var isGrouped = (fullLayout.boxmode === 'group' || fullLayout.violinmode === 'group');
23142
23143 var xVal = hd.xVal;
23144 var ax = hd.xa;
23145 if(ax.type === 'category') xVal = ax._categoriesMap[xVal];
23146 if(ax.type === 'date') xVal = ax.d2c(xVal);
23147 if(cd0 && cd0.t && cd0.t.posLetter === ax._id && isGrouped) {
23148 xVal += cd0.t.dPos;
23149 }
23150
23151 var yVal = hd.yVal;
23152 ax = hd.ya;
23153 if(ax.type === 'category') yVal = ax._categoriesMap[yVal];
23154 if(ax.type === 'date') yVal = ax.d2c(yVal);
23155 if(cd0 && cd0.t && cd0.t.posLetter === ax._id && isGrouped) {
23156 yVal += cd0.t.dPos;
23157 }
23158
23159 findHoverPoints(xVal, yVal);
23160
23161 // Remove duplicated hoverData points
23162 // note that d3 also filters identical points in the rendering steps
23163 var repeated = {};
23164 hoverData = hoverData.filter(function(hd) {
23165 var key = hoverDataKey(hd);
23166 if(!repeated[key]) {
23167 repeated[key] = true;
23168 return repeated[key];
23169 }
23170 });
23171 }
23172
23173 // lastly, emit custom hover/unhover events
23174 var oldhoverdata = gd._hoverdata;
23175 var newhoverdata = [];
23176
23177 // pull out just the data that's useful to
23178 // other people and send it to the event
23179 for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
23180 var pt = hoverData[itemnum];
23181 var eventData = helpers.makeEventData(pt, pt.trace, pt.cd);
23182
23183 if(pt.hovertemplate !== false) {
23184 var ht = false;
23185 if(pt.cd[pt.index] && pt.cd[pt.index].ht) {
23186 ht = pt.cd[pt.index].ht;
23187 }
23188 pt.hovertemplate = ht || pt.trace.hovertemplate || false;
23189 }
23190
23191 pt.eventData = [eventData];
23192 newhoverdata.push(eventData);
23193 }
23194
23195 gd._hoverdata = newhoverdata;
23196
23197 var rotateLabels = (
23198 (hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) ||
23199 (hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1)
23200 );
23201
23202 var bgColor = Color.combine(
23203 fullLayout.plot_bgcolor || Color.background,
23204 fullLayout.paper_bgcolor
23205 );
23206
23207 var labelOpts = {
23208 hovermode: hovermode,
23209 rotateLabels: rotateLabels,
23210 bgColor: bgColor,
23211 container: fullLayout._hoverlayer,
23212 outerContainer: fullLayout._paperdiv,
23213 commonLabelOpts: fullLayout.hoverlabel,
23214 hoverdistance: fullLayout.hoverdistance
23215 };
23216
23217 var hoverLabels = createHoverText(hoverData, labelOpts, gd);
23218
23219 if(!helpers.isUnifiedHover(hovermode)) {
23220 hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout);
23221 alignHoverText(hoverLabels, rotateLabels);
23222 }
23223
23224 // TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true
23225 // we should improve the "fx" API so other plots can use it without these hack.
23226 if(evt.target && evt.target.tagName) {
23227 var hasClickToShow = Registry.getComponentMethod('annotations', 'hasClickToShow')(gd, newhoverdata);
23228 overrideCursor(d3.select(evt.target), hasClickToShow ? 'pointer' : '');
23229 }
23230
23231 // don't emit events if called manually
23232 if(!evt.target || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return;
23233
23234 if(oldhoverdata) {
23235 gd.emit('plotly_unhover', {
23236 event: evt,
23237 points: oldhoverdata
23238 });
23239 }
23240
23241 gd.emit('plotly_hover', {
23242 event: evt,
23243 points: gd._hoverdata,
23244 xaxes: xaArray,
23245 yaxes: yaArray,
23246 xvals: xvalArray,
23247 yvals: yvalArray
23248 });
23249}
23250
23251function hoverDataKey(d) {
23252 return [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa, d.ya || ''].join(',');
23253}
23254
23255var EXTRA_STRING_REGEX = /<extra>([\s\S]*)<\/extra>/;
23256
23257function createHoverText(hoverData, opts, gd) {
23258 var fullLayout = gd._fullLayout;
23259 var hovermode = opts.hovermode;
23260 var rotateLabels = opts.rotateLabels;
23261 var bgColor = opts.bgColor;
23262 var container = opts.container;
23263 var outerContainer = opts.outerContainer;
23264 var commonLabelOpts = opts.commonLabelOpts || {};
23265
23266 // opts.fontFamily/Size are used for the common label
23267 // and as defaults for each hover label, though the individual labels
23268 // can override this.
23269 var fontFamily = opts.fontFamily || constants.HOVERFONT;
23270 var fontSize = opts.fontSize || constants.HOVERFONTSIZE;
23271
23272 var c0 = hoverData[0];
23273 var xa = c0.xa;
23274 var ya = c0.ya;
23275 var commonAttr = hovermode.charAt(0) === 'y' ? 'yLabel' : 'xLabel';
23276 var t0 = c0[commonAttr];
23277 var t00 = (String(t0) || '').split(' ')[0];
23278 var outerContainerBB = outerContainer.node().getBoundingClientRect();
23279 var outerTop = outerContainerBB.top;
23280 var outerWidth = outerContainerBB.width;
23281 var outerHeight = outerContainerBB.height;
23282
23283 // show the common label, if any, on the axis
23284 // never show a common label in array mode,
23285 // even if sometimes there could be one
23286 var showCommonLabel = (
23287 (t0 !== undefined) &&
23288 (c0.distance <= opts.hoverdistance) &&
23289 (hovermode === 'x' || hovermode === 'y')
23290 );
23291
23292 // all hover traces hoverinfo must contain the hovermode
23293 // to have common labels
23294 if(showCommonLabel) {
23295 var allHaveZ = true;
23296 var i, traceHoverinfo;
23297 for(i = 0; i < hoverData.length; i++) {
23298 if(allHaveZ && hoverData[i].zLabel === undefined) allHaveZ = false;
23299
23300 traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
23301 if(traceHoverinfo) {
23302 var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+');
23303 if(parts.indexOf('all') === -1 &&
23304 parts.indexOf(hovermode) === -1) {
23305 showCommonLabel = false;
23306 break;
23307 }
23308 }
23309 }
23310
23311 // xyz labels put all info in their main label, so have no need of a common label
23312 if(allHaveZ) showCommonLabel = false;
23313 }
23314
23315 var commonLabel = container.selectAll('g.axistext')
23316 .data(showCommonLabel ? [0] : []);
23317 commonLabel.enter().append('g')
23318 .classed('axistext', true);
23319 commonLabel.exit().remove();
23320
23321 commonLabel.each(function() {
23322 var label = d3.select(this);
23323 var lpath = Lib.ensureSingle(label, 'path', '', function(s) {
23324 s.style({'stroke-width': '1px'});
23325 });
23326 var ltext = Lib.ensureSingle(label, 'text', '', function(s) {
23327 // prohibit tex interpretation until we can handle
23328 // tex and regular text together
23329 s.attr('data-notex', 1);
23330 });
23331
23332 var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine;
23333 var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor);
23334 var contrastColor = Color.contrast(commonBgColor);
23335 var commonLabelFont = {
23336 family: commonLabelOpts.font.family || fontFamily,
23337 size: commonLabelOpts.font.size || fontSize,
23338 color: commonLabelOpts.font.color || contrastColor
23339 };
23340
23341 lpath.style({
23342 fill: commonBgColor,
23343 stroke: commonStroke
23344 });
23345
23346 ltext.text(t0)
23347 .call(Drawing.font, commonLabelFont)
23348 .call(svgTextUtils.positionText, 0, 0)
23349 .call(svgTextUtils.convertToTspans, gd);
23350
23351 label.attr('transform', '');
23352
23353 var tbb = ltext.node().getBoundingClientRect();
23354 var lx, ly;
23355
23356 if(hovermode === 'x') {
23357 var topsign = xa.side === 'top' ? '-' : '';
23358
23359 ltext.attr('text-anchor', 'middle')
23360 .call(svgTextUtils.positionText, 0, (xa.side === 'top' ?
23361 (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) :
23362 (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD)));
23363
23364 lx = xa._offset + (c0.x0 + c0.x1) / 2;
23365 ly = ya._offset + (xa.side === 'top' ? 0 : ya._length);
23366
23367 var halfWidth = tbb.width / 2 + HOVERTEXTPAD;
23368
23369 if(lx < halfWidth) {
23370 lx = halfWidth;
23371
23372 lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' +
23373 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE +
23374 'H' + (HOVERTEXTPAD + tbb.width / 2) +
23375 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
23376 'H-' + halfWidth +
23377 'V' + topsign + HOVERARROWSIZE +
23378 'Z');
23379 } else if(lx > (fullLayout.width - halfWidth)) {
23380 lx = fullLayout.width - halfWidth;
23381
23382 lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' +
23383 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE +
23384 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
23385 'H-' + halfWidth +
23386 'V' + topsign + HOVERARROWSIZE +
23387 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z');
23388 } else {
23389 lpath.attr('d', 'M0,0' +
23390 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
23391 'H' + (HOVERTEXTPAD + tbb.width / 2) +
23392 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
23393 'H-' + (HOVERTEXTPAD + tbb.width / 2) +
23394 'V' + topsign + HOVERARROWSIZE +
23395 'H-' + HOVERARROWSIZE + 'Z');
23396 }
23397 } else {
23398 var anchor;
23399 var sgn;
23400 var leftsign;
23401 if(ya.side === 'right') {
23402 anchor = 'start';
23403 sgn = 1;
23404 leftsign = '';
23405 lx = xa._offset + xa._length;
23406 } else {
23407 anchor = 'end';
23408 sgn = -1;
23409 leftsign = '-';
23410 lx = xa._offset;
23411 }
23412
23413 ly = ya._offset + (c0.y0 + c0.y1) / 2;
23414
23415 ltext.attr('text-anchor', anchor);
23416
23417 lpath.attr('d', 'M0,0' +
23418 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE +
23419 'V' + (HOVERTEXTPAD + tbb.height / 2) +
23420 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) +
23421 'V-' + (HOVERTEXTPAD + tbb.height / 2) +
23422 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z');
23423
23424 var halfHeight = tbb.height / 2;
23425 var lty = outerTop - tbb.top - halfHeight;
23426 var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id;
23427 var clipPath;
23428
23429 if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) {
23430 clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight +
23431 'h-' + (tbb.width - HOVERTEXTPAD) +
23432 'V' + halfHeight +
23433 'h' + (tbb.width - HOVERTEXTPAD) + 'Z';
23434
23435 var ltx = tbb.width - lx + HOVERTEXTPAD;
23436 svgTextUtils.positionText(ltext, ltx, lty);
23437
23438 // shift each line (except the longest) so that start-of-line
23439 // is always visible
23440 if(anchor === 'end') {
23441 ltext.selectAll('tspan').each(function() {
23442 var s = d3.select(this);
23443 var dummy = Drawing.tester.append('text')
23444 .text(s.text())
23445 .call(Drawing.font, commonLabelFont);
23446 var dummyBB = dummy.node().getBoundingClientRect();
23447 if(Math.round(dummyBB.width) < Math.round(tbb.width)) {
23448 s.attr('x', ltx - dummyBB.width);
23449 }
23450 dummy.remove();
23451 });
23452 }
23453 } else {
23454 svgTextUtils.positionText(ltext, sgn * (HOVERTEXTPAD + HOVERARROWSIZE), lty);
23455 clipPath = null;
23456 }
23457
23458 var textClip = fullLayout._topclips.selectAll('#' + clipId).data(clipPath ? [0] : []);
23459 textClip.enter().append('clipPath').attr('id', clipId).append('path');
23460 textClip.exit().remove();
23461 textClip.select('path').attr('d', clipPath);
23462 Drawing.setClipUrl(ltext, clipPath ? clipId : null, gd);
23463 }
23464
23465 label.attr('transform', 'translate(' + lx + ',' + ly + ')');
23466
23467 // remove the "close but not quite" points
23468 // because of error bars, only take up to a space
23469 hoverData = filterClosePoints(hoverData);
23470 });
23471
23472 function filterClosePoints(hoverData) {
23473 return hoverData.filter(function(d) {
23474 return (d.zLabelVal !== undefined) ||
23475 (d[commonAttr] || '').split(' ')[0] === t00;
23476 });
23477 }
23478
23479 // Show a single hover label
23480 if(helpers.isUnifiedHover(hovermode)) {
23481 // Delete leftover hover labels from other hovermodes
23482 container.selectAll('g.hovertext').remove();
23483
23484 // similarly to compare mode, we remove the "close but not quite together" points
23485 if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData);
23486
23487 // Return early if nothing is hovered on
23488 if(hoverData.length === 0) return;
23489
23490 // mock legend
23491 var mockLayoutIn = {
23492 showlegend: true,
23493 legend: {
23494 title: {text: t0, font: fullLayout.hoverlabel.font},
23495 font: fullLayout.hoverlabel.font,
23496 bgcolor: fullLayout.hoverlabel.bgcolor,
23497 bordercolor: fullLayout.hoverlabel.bordercolor,
23498 borderwidth: 1,
23499 tracegroupgap: 7,
23500 traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined,
23501 orientation: 'v'
23502 }
23503 };
23504 var mockLayoutOut = {};
23505 legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData);
23506 var legendOpts = mockLayoutOut.legend;
23507
23508 // prepare items for the legend
23509 legendOpts.entries = [];
23510 for(var j = 0; j < hoverData.length; j++) {
23511 var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0);
23512 var text = texts[0];
23513 var name = texts[1];
23514 var pt = hoverData[j];
23515 pt.name = name;
23516 if(name !== '') {
23517 pt.text = name + ' : ' + text;
23518 } else {
23519 pt.text = text;
23520 }
23521
23522 // pass through marker's calcdata to style legend items
23523 var cd = pt.cd[pt.index];
23524 if(cd) {
23525 if(cd.mc) pt.mc = cd.mc;
23526 if(cd.mcc) pt.mc = cd.mcc;
23527 if(cd.mlc) pt.mlc = cd.mlc;
23528 if(cd.mlcc) pt.mlc = cd.mlcc;
23529 if(cd.mlw) pt.mlw = cd.mlw;
23530 if(cd.mrc) pt.mrc = cd.mrc;
23531 if(cd.dir) pt.dir = cd.dir;
23532 }
23533 pt._distinct = true;
23534
23535 legendOpts.entries.push([pt]);
23536 }
23537 legendOpts.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;});
23538 legendOpts.layer = container;
23539
23540 // Draw unified hover label
23541 legendDraw(gd, legendOpts);
23542
23543 // Position the hover
23544 var ly = Lib.mean(hoverData.map(function(c) {return (c.y0 + c.y1) / 2;}));
23545 var lx = Lib.mean(hoverData.map(function(c) {return (c.x0 + c.x1) / 2;}));
23546 var legendContainer = container.select('g.legend');
23547 var tbb = legendContainer.node().getBoundingClientRect();
23548 lx += xa._offset;
23549 ly += ya._offset - tbb.height / 2;
23550
23551 // Change horizontal alignment to end up on screen
23552 var txWidth = tbb.width + 2 * HOVERTEXTPAD;
23553 var anchorStartOK = lx + txWidth <= outerWidth;
23554 var anchorEndOK = lx - txWidth >= 0;
23555 if(!anchorStartOK && anchorEndOK) {
23556 lx -= txWidth;
23557 } else {
23558 lx += 2 * HOVERTEXTPAD;
23559 }
23560
23561 // Change vertical alignement to end up on screen
23562 var txHeight = tbb.height + 2 * HOVERTEXTPAD;
23563 var overflowTop = ly <= outerTop;
23564 var overflowBottom = ly + txHeight >= outerHeight;
23565 var canFit = txHeight <= outerHeight;
23566 if(canFit) {
23567 if(overflowTop) {
23568 ly = ya._offset + 2 * HOVERTEXTPAD;
23569 } else if(overflowBottom) {
23570 ly = outerHeight - txHeight;
23571 }
23572 }
23573 legendContainer.attr('transform', 'translate(' + lx + ',' + ly + ')');
23574
23575 return legendContainer;
23576 }
23577
23578 // show all the individual labels
23579
23580 // first create the objects
23581 var hoverLabels = container.selectAll('g.hovertext')
23582 .data(hoverData, function(d) {
23583 // N.B. when multiple items have the same result key-function value,
23584 // only the first of those items in hoverData gets rendered
23585 return hoverDataKey(d);
23586 });
23587 hoverLabels.enter().append('g')
23588 .classed('hovertext', true)
23589 .each(function() {
23590 var g = d3.select(this);
23591 // trace name label (rect and text.name)
23592 g.append('rect')
23593 .call(Color.fill, Color.addOpacity(bgColor, 0.8));
23594 g.append('text').classed('name', true);
23595 // trace data label (path and text.nums)
23596 g.append('path')
23597 .style('stroke-width', '1px');
23598 g.append('text').classed('nums', true)
23599 .call(Drawing.font, fontFamily, fontSize);
23600 });
23601 hoverLabels.exit().remove();
23602
23603 // then put the text in, position the pointer to the data,
23604 // and figure out sizes
23605 hoverLabels.each(function(d) {
23606 var g = d3.select(this).attr('transform', '');
23607
23608 // combine possible non-opaque trace color with bgColor
23609 var color0 = d.bgcolor || d.color;
23610 // color for 'nums' part of the label
23611 var numsColor = Color.combine(
23612 Color.opacity(color0) ? color0 : Color.defaultLine,
23613 bgColor
23614 );
23615 // color for 'name' part of the label
23616 var nameColor = Color.combine(
23617 Color.opacity(d.color) ? d.color : Color.defaultLine,
23618 bgColor
23619 );
23620 // find a contrasting color for border and text
23621 var contrastColor = d.borderColor || Color.contrast(numsColor);
23622
23623 var texts = getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g);
23624 var text = texts[0];
23625 var name = texts[1];
23626
23627 // main label
23628 var tx = g.select('text.nums')
23629 .call(Drawing.font,
23630 d.fontFamily || fontFamily,
23631 d.fontSize || fontSize,
23632 d.fontColor || contrastColor)
23633 .text(text)
23634 .attr('data-notex', 1)
23635 .call(svgTextUtils.positionText, 0, 0)
23636 .call(svgTextUtils.convertToTspans, gd);
23637
23638 var tx2 = g.select('text.name');
23639 var tx2width = 0;
23640 var tx2height = 0;
23641
23642 // secondary label for non-empty 'name'
23643 if(name && name !== text) {
23644 tx2.call(Drawing.font,
23645 d.fontFamily || fontFamily,
23646 d.fontSize || fontSize,
23647 nameColor)
23648 .text(name)
23649 .attr('data-notex', 1)
23650 .call(svgTextUtils.positionText, 0, 0)
23651 .call(svgTextUtils.convertToTspans, gd);
23652
23653 var t2bb = tx2.node().getBoundingClientRect();
23654 tx2width = t2bb.width + 2 * HOVERTEXTPAD;
23655 tx2height = t2bb.height + 2 * HOVERTEXTPAD;
23656 } else {
23657 tx2.remove();
23658 g.select('rect').remove();
23659 }
23660
23661 g.select('path').style({
23662 fill: numsColor,
23663 stroke: contrastColor
23664 });
23665
23666 var tbb = tx.node().getBoundingClientRect();
23667 var htx = d.xa._offset + (d.x0 + d.x1) / 2;
23668 var hty = d.ya._offset + (d.y0 + d.y1) / 2;
23669 var dx = Math.abs(d.x1 - d.x0);
23670 var dy = Math.abs(d.y1 - d.y0);
23671 var txTotalWidth = tbb.width + HOVERARROWSIZE + HOVERTEXTPAD + tx2width;
23672 var anchorStartOK, anchorEndOK;
23673
23674 d.ty0 = outerTop - tbb.top;
23675 d.bx = tbb.width + 2 * HOVERTEXTPAD;
23676 d.by = Math.max(tbb.height + 2 * HOVERTEXTPAD, tx2height);
23677 d.anchor = 'start';
23678 d.txwidth = tbb.width;
23679 d.tx2width = tx2width;
23680 d.offset = 0;
23681
23682 if(rotateLabels) {
23683 d.pos = htx;
23684 anchorStartOK = hty + dy / 2 + txTotalWidth <= outerHeight;
23685 anchorEndOK = hty - dy / 2 - txTotalWidth >= 0;
23686 if((d.idealAlign === 'top' || !anchorStartOK) && anchorEndOK) {
23687 hty -= dy / 2;
23688 d.anchor = 'end';
23689 } else if(anchorStartOK) {
23690 hty += dy / 2;
23691 d.anchor = 'start';
23692 } else d.anchor = 'middle';
23693 } else {
23694 d.pos = hty;
23695 anchorStartOK = htx + dx / 2 + txTotalWidth <= outerWidth;
23696 anchorEndOK = htx - dx / 2 - txTotalWidth >= 0;
23697
23698 if((d.idealAlign === 'left' || !anchorStartOK) && anchorEndOK) {
23699 htx -= dx / 2;
23700 d.anchor = 'end';
23701 } else if(anchorStartOK) {
23702 htx += dx / 2;
23703 d.anchor = 'start';
23704 } else {
23705 d.anchor = 'middle';
23706
23707 var txHalfWidth = txTotalWidth / 2;
23708 var overflowR = htx + txHalfWidth - outerWidth;
23709 var overflowL = htx - txHalfWidth;
23710 if(overflowR > 0) htx -= overflowR;
23711 if(overflowL < 0) htx += -overflowL;
23712 }
23713 }
23714
23715 tx.attr('text-anchor', d.anchor);
23716 if(tx2width) tx2.attr('text-anchor', d.anchor);
23717 g.attr('transform', 'translate(' + htx + ',' + hty + ')' +
23718 (rotateLabels ? 'rotate(' + YANGLE + ')' : ''));
23719 });
23720
23721 return hoverLabels;
23722}
23723
23724function getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g) {
23725 var name = '';
23726 var text = '';
23727 // to get custom 'name' labels pass cleanPoint
23728 if(d.nameOverride !== undefined) d.name = d.nameOverride;
23729
23730 if(d.name) {
23731 if(d.trace._meta) {
23732 d.name = Lib.templateString(d.name, d.trace._meta);
23733 }
23734 name = plainText(d.name, d.nameLength);
23735 }
23736
23737 if(d.zLabel !== undefined) {
23738 if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '<br>';
23739 if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '<br>';
23740 if(d.trace.type !== 'choropleth' && d.trace.type !== 'choroplethmapbox') {
23741 text += (text ? 'z: ' : '') + d.zLabel;
23742 }
23743 } else if(showCommonLabel && d[hovermode.charAt(0) + 'Label'] === t0) {
23744 text = d[(hovermode.charAt(0) === 'x' ? 'y' : 'x') + 'Label'] || '';
23745 } else if(d.xLabel === undefined) {
23746 if(d.yLabel !== undefined && d.trace.type !== 'scattercarpet') {
23747 text = d.yLabel;
23748 }
23749 } else if(d.yLabel === undefined) text = d.xLabel;
23750 else text = '(' + d.xLabel + ', ' + d.yLabel + ')';
23751
23752 if((d.text || d.text === 0) && !Array.isArray(d.text)) {
23753 text += (text ? '<br>' : '') + d.text;
23754 }
23755
23756 // used by other modules (initially just ternary) that
23757 // manage their own hoverinfo independent of cleanPoint
23758 // the rest of this will still apply, so such modules
23759 // can still put things in (x|y|z)Label, text, and name
23760 // and hoverinfo will still determine their visibility
23761 if(d.extraText !== undefined) text += (text ? '<br>' : '') + d.extraText;
23762
23763 // if 'text' is empty at this point,
23764 // and hovertemplate is not defined,
23765 // put 'name' in main label and don't show secondary label
23766 if(g && text === '' && !d.hovertemplate) {
23767 // if 'name' is also empty, remove entire label
23768 if(name === '') g.remove();
23769 text = name;
23770 }
23771
23772 // hovertemplate
23773 var d3locale = fullLayout._d3locale;
23774 var hovertemplate = d.hovertemplate || false;
23775 var hovertemplateLabels = d.hovertemplateLabels || d;
23776 var eventData = d.eventData[0] || {};
23777 if(hovertemplate) {
23778 text = Lib.hovertemplateString(
23779 hovertemplate,
23780 hovertemplateLabels,
23781 d3locale,
23782 eventData,
23783 d.trace._meta
23784 );
23785
23786 text = text.replace(EXTRA_STRING_REGEX, function(match, extra) {
23787 // assign name for secondary text label
23788 name = plainText(extra, d.nameLength);
23789 // remove from main text label
23790 return '';
23791 });
23792 }
23793 return [text, name];
23794}
23795
23796// Make groups of touching points, and within each group
23797// move each point so that no labels overlap, but the average
23798// label position is the same as it was before moving. Indicentally,
23799// this is equivalent to saying all the labels are on equal linear
23800// springs about their initial position. Initially, each point is
23801// its own group, but as we find overlaps we will clump the points.
23802//
23803// Also, there are hard constraints at the edges of the graphs,
23804// that push all groups to the middle so they are visible. I don't
23805// know what happens if the group spans all the way from one edge to
23806// the other, though it hardly matters - there's just too much
23807// information then.
23808function hoverAvoidOverlaps(hoverLabels, axKey, fullLayout) {
23809 var nummoves = 0;
23810 var axSign = 1;
23811 var nLabels = hoverLabels.size();
23812
23813 // make groups of touching points
23814 var pointgroups = new Array(nLabels);
23815 var k = 0;
23816
23817 hoverLabels.each(function(d) {
23818 var ax = d[axKey];
23819 var axIsX = ax._id.charAt(0) === 'x';
23820 var rng = ax.range;
23821
23822 if(k === 0 && rng && ((rng[0] > rng[1]) !== axIsX)) {
23823 axSign = -1;
23824 }
23825 pointgroups[k++] = [{
23826 datum: d,
23827 traceIndex: d.trace.index,
23828 dp: 0,
23829 pos: d.pos,
23830 posref: d.posref,
23831 size: d.by * (axIsX ? YFACTOR : 1) / 2,
23832 pmin: 0,
23833 pmax: (axIsX ? fullLayout.width : fullLayout.height)
23834 }];
23835 });
23836
23837 pointgroups.sort(function(a, b) {
23838 return (a[0].posref - b[0].posref) ||
23839 // for equal positions, sort trace indices increasing or decreasing
23840 // depending on whether the axis is reversed or not... so stacked
23841 // traces will generally keep their order even if one trace adds
23842 // nothing to the stack.
23843 (axSign * (b[0].traceIndex - a[0].traceIndex));
23844 });
23845
23846 var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp;
23847
23848 function constrainGroup(grp) {
23849 var minPt = grp[0];
23850 var maxPt = grp[grp.length - 1];
23851
23852 // overlap with the top - positive vals are overlaps
23853 topOverlap = minPt.pmin - minPt.pos - minPt.dp + minPt.size;
23854
23855 // overlap with the bottom - positive vals are overlaps
23856 bottomOverlap = maxPt.pos + maxPt.dp + maxPt.size - minPt.pmax;
23857
23858 // check for min overlap first, so that we always
23859 // see the largest labels
23860 // allow for .01px overlap, so we don't get an
23861 // infinite loop from rounding errors
23862 if(topOverlap > 0.01) {
23863 for(j = grp.length - 1; j >= 0; j--) grp[j].dp += topOverlap;
23864 donepositioning = false;
23865 }
23866 if(bottomOverlap < 0.01) return;
23867 if(topOverlap < -0.01) {
23868 // make sure we're not pushing back and forth
23869 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
23870 donepositioning = false;
23871 }
23872 if(!donepositioning) return;
23873
23874 // no room to fix positioning, delete off-screen points
23875
23876 // first see how many points we need to delete
23877 var deleteCount = 0;
23878 for(i = 0; i < grp.length; i++) {
23879 pti = grp[i];
23880 if(pti.pos + pti.dp + pti.size > minPt.pmax) deleteCount++;
23881 }
23882
23883 // start by deleting points whose data is off screen
23884 for(i = grp.length - 1; i >= 0; i--) {
23885 if(deleteCount <= 0) break;
23886 pti = grp[i];
23887
23888 // pos has already been constrained to [pmin,pmax]
23889 // so look for points close to that to delete
23890 if(pti.pos > minPt.pmax - 1) {
23891 pti.del = true;
23892 deleteCount--;
23893 }
23894 }
23895 for(i = 0; i < grp.length; i++) {
23896 if(deleteCount <= 0) break;
23897 pti = grp[i];
23898
23899 // pos has already been constrained to [pmin,pmax]
23900 // so look for points close to that to delete
23901 if(pti.pos < minPt.pmin + 1) {
23902 pti.del = true;
23903 deleteCount--;
23904
23905 // shift the whole group minus into this new space
23906 bottomOverlap = pti.size * 2;
23907 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
23908 }
23909 }
23910 // then delete points that go off the bottom
23911 for(i = grp.length - 1; i >= 0; i--) {
23912 if(deleteCount <= 0) break;
23913 pti = grp[i];
23914 if(pti.pos + pti.dp + pti.size > minPt.pmax) {
23915 pti.del = true;
23916 deleteCount--;
23917 }
23918 }
23919 }
23920
23921 // loop through groups, combining them if they overlap,
23922 // until nothing moves
23923 while(!donepositioning && nummoves <= nLabels) {
23924 // to avoid infinite loops, don't move more times
23925 // than there are traces
23926 nummoves++;
23927
23928 // assume nothing will move in this iteration,
23929 // reverse this if it does
23930 donepositioning = true;
23931 i = 0;
23932 while(i < pointgroups.length - 1) {
23933 // the higher (g0) and lower (g1) point group
23934 var g0 = pointgroups[i];
23935 var g1 = pointgroups[i + 1];
23936
23937 // the lowest point in the higher group (p0)
23938 // the highest point in the lower group (p1)
23939 var p0 = g0[g0.length - 1];
23940 var p1 = g1[0];
23941 topOverlap = p0.pos + p0.dp + p0.size - p1.pos - p1.dp + p1.size;
23942
23943 // Only group points that lie on the same axes
23944 if(topOverlap > 0.01 && (p0.pmin === p1.pmin) && (p0.pmax === p1.pmax)) {
23945 // push the new point(s) added to this group out of the way
23946 for(j = g1.length - 1; j >= 0; j--) g1[j].dp += topOverlap;
23947
23948 // add them to the group
23949 g0.push.apply(g0, g1);
23950 pointgroups.splice(i + 1, 1);
23951
23952 // adjust for minimum average movement
23953 sumdp = 0;
23954 for(j = g0.length - 1; j >= 0; j--) sumdp += g0[j].dp;
23955 bottomOverlap = sumdp / g0.length;
23956 for(j = g0.length - 1; j >= 0; j--) g0[j].dp -= bottomOverlap;
23957 donepositioning = false;
23958 } else i++;
23959 }
23960
23961 // check if we're going off the plot on either side and fix
23962 pointgroups.forEach(constrainGroup);
23963 }
23964
23965 // now put these offsets into hoverData
23966 for(i = pointgroups.length - 1; i >= 0; i--) {
23967 var grp = pointgroups[i];
23968 for(j = grp.length - 1; j >= 0; j--) {
23969 var pt = grp[j];
23970 var hoverPt = pt.datum;
23971 hoverPt.offset = pt.dp;
23972 hoverPt.del = pt.del;
23973 }
23974 }
23975}
23976
23977function alignHoverText(hoverLabels, rotateLabels) {
23978 // finally set the text positioning relative to the data and draw the
23979 // box around it
23980 hoverLabels.each(function(d) {
23981 var g = d3.select(this);
23982 if(d.del) return g.remove();
23983
23984 var tx = g.select('text.nums');
23985 var anchor = d.anchor;
23986 var horzSign = anchor === 'end' ? -1 : 1;
23987 var alignShift = {start: 1, end: -1, middle: 0}[anchor];
23988 var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD);
23989 var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD);
23990 var offsetX = 0;
23991 var offsetY = d.offset;
23992
23993 if(anchor === 'middle') {
23994 txx -= d.tx2width / 2;
23995 tx2x += d.txwidth / 2 + HOVERTEXTPAD;
23996 }
23997 if(rotateLabels) {
23998 offsetY *= -YSHIFTY;
23999 offsetX = d.offset * YSHIFTX;
24000 }
24001
24002 g.select('path').attr('d', anchor === 'middle' ?
24003 // middle aligned: rect centered on data
24004 ('M-' + (d.bx / 2 + d.tx2width / 2) + ',' + (offsetY - d.by / 2) +
24005 'h' + d.bx + 'v' + d.by + 'h-' + d.bx + 'Z') :
24006 // left or right aligned: side rect with arrow to data
24007 ('M0,0L' + (horzSign * HOVERARROWSIZE + offsetX) + ',' + (HOVERARROWSIZE + offsetY) +
24008 'v' + (d.by / 2 - HOVERARROWSIZE) +
24009 'h' + (horzSign * d.bx) +
24010 'v-' + d.by +
24011 'H' + (horzSign * HOVERARROWSIZE + offsetX) +
24012 'V' + (offsetY - HOVERARROWSIZE) +
24013 'Z'));
24014
24015 var posX = txx + offsetX;
24016 var posY = offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD;
24017 var textAlign = d.textAlign || 'auto';
24018
24019 if(textAlign !== 'auto') {
24020 if(textAlign === 'left' && anchor !== 'start') {
24021 tx.attr('text-anchor', 'start');
24022 posX = anchor === 'middle' ?
24023 -d.bx / 2 - d.tx2width / 2 + HOVERTEXTPAD :
24024 -d.bx - HOVERTEXTPAD;
24025 } else if(textAlign === 'right' && anchor !== 'end') {
24026 tx.attr('text-anchor', 'end');
24027 posX = anchor === 'middle' ?
24028 d.bx / 2 - d.tx2width / 2 - HOVERTEXTPAD :
24029 d.bx + HOVERTEXTPAD;
24030 }
24031 }
24032
24033 tx.call(svgTextUtils.positionText, posX, posY);
24034
24035 if(d.tx2width) {
24036 g.select('text.name')
24037 .call(svgTextUtils.positionText,
24038 tx2x + alignShift * HOVERTEXTPAD + offsetX,
24039 offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD);
24040 g.select('rect')
24041 .call(Drawing.setRect,
24042 tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX,
24043 offsetY - d.by / 2 - 1,
24044 d.tx2width, d.by + 2);
24045 }
24046 });
24047}
24048
24049function cleanPoint(d, hovermode) {
24050 var index = d.index;
24051 var trace = d.trace || {};
24052 var cd0 = d.cd[0];
24053 var cd = d.cd[index] || {};
24054
24055 function pass(v) {
24056 return v || (isNumeric(v) && v === 0);
24057 }
24058
24059 var getVal = Array.isArray(index) ?
24060 function(calcKey, traceKey) {
24061 var v = Lib.castOption(cd0, index, calcKey);
24062 return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey);
24063 } :
24064 function(calcKey, traceKey) {
24065 return Lib.extractOption(cd, trace, calcKey, traceKey);
24066 };
24067
24068 function fill(key, calcKey, traceKey) {
24069 var val = getVal(calcKey, traceKey);
24070 if(pass(val)) d[key] = val;
24071 }
24072
24073 fill('hoverinfo', 'hi', 'hoverinfo');
24074 fill('bgcolor', 'hbg', 'hoverlabel.bgcolor');
24075 fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
24076 fill('fontFamily', 'htf', 'hoverlabel.font.family');
24077 fill('fontSize', 'hts', 'hoverlabel.font.size');
24078 fill('fontColor', 'htc', 'hoverlabel.font.color');
24079 fill('nameLength', 'hnl', 'hoverlabel.namelength');
24080 fill('textAlign', 'hta', 'hoverlabel.align');
24081
24082 d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ?
24083 (d.xa._offset + (d.x0 + d.x1) / 2) :
24084 (d.ya._offset + (d.y0 + d.y1) / 2);
24085
24086 // then constrain all the positions to be on the plot
24087 d.x0 = Lib.constrain(d.x0, 0, d.xa._length);
24088 d.x1 = Lib.constrain(d.x1, 0, d.xa._length);
24089 d.y0 = Lib.constrain(d.y0, 0, d.ya._length);
24090 d.y1 = Lib.constrain(d.y1, 0, d.ya._length);
24091
24092 // and convert the x and y label values into formatted text
24093 if(d.xLabelVal !== undefined) {
24094 d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
24095 d.xVal = d.xa.c2d(d.xLabelVal);
24096 }
24097 if(d.yLabelVal !== undefined) {
24098 d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
24099 d.yVal = d.ya.c2d(d.yLabelVal);
24100 }
24101
24102 // Traces like heatmaps generate the zLabel in their hoverPoints function
24103 if(d.zLabelVal !== undefined && d.zLabel === undefined) {
24104 d.zLabel = String(d.zLabelVal);
24105 }
24106
24107 // for box means and error bars, add the range to the label
24108 if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) {
24109 var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text;
24110 if(d.xerrneg !== undefined) {
24111 d.xLabel += ' +' + xeText + ' / -' +
24112 Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text;
24113 } else d.xLabel += ' ± ' + xeText;
24114
24115 // small distance penalty for error bars, so that if there are
24116 // traces with errors and some without, the error bar label will
24117 // hoist up to the point
24118 if(hovermode === 'x') d.distance += 1;
24119 }
24120 if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) {
24121 var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text;
24122 if(d.yerrneg !== undefined) {
24123 d.yLabel += ' +' + yeText + ' / -' +
24124 Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text;
24125 } else d.yLabel += ' ± ' + yeText;
24126
24127 if(hovermode === 'y') d.distance += 1;
24128 }
24129
24130 var infomode = d.hoverinfo || d.trace.hoverinfo;
24131
24132 if(infomode && infomode !== 'all') {
24133 infomode = Array.isArray(infomode) ? infomode : infomode.split('+');
24134 if(infomode.indexOf('x') === -1) d.xLabel = undefined;
24135 if(infomode.indexOf('y') === -1) d.yLabel = undefined;
24136 if(infomode.indexOf('z') === -1) d.zLabel = undefined;
24137 if(infomode.indexOf('text') === -1) d.text = undefined;
24138 if(infomode.indexOf('name') === -1) d.name = undefined;
24139 }
24140
24141 return d;
24142}
24143
24144function createSpikelines(gd, closestPoints, opts) {
24145 var container = opts.container;
24146 var fullLayout = opts.fullLayout;
24147 var gs = fullLayout._size;
24148 var evt = opts.event;
24149 var showY = !!closestPoints.hLinePoint;
24150 var showX = !!closestPoints.vLinePoint;
24151
24152 var xa, ya;
24153
24154 // Remove old spikeline items
24155 container.selectAll('.spikeline').remove();
24156
24157 if(!(showX || showY)) return;
24158
24159 var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor);
24160
24161 // Horizontal line (to y-axis)
24162 if(showY) {
24163 var hLinePoint = closestPoints.hLinePoint;
24164 var hLinePointX, hLinePointY;
24165
24166 xa = hLinePoint && hLinePoint.xa;
24167 ya = hLinePoint && hLinePoint.ya;
24168 var ySnap = ya.spikesnap;
24169
24170 if(ySnap === 'cursor') {
24171 hLinePointX = evt.pointerX;
24172 hLinePointY = evt.pointerY;
24173 } else {
24174 hLinePointX = xa._offset + hLinePoint.x;
24175 hLinePointY = ya._offset + hLinePoint.y;
24176 }
24177 var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ?
24178 Color.contrast(contrastColor) : hLinePoint.color;
24179 var yMode = ya.spikemode;
24180 var yThickness = ya.spikethickness;
24181 var yColor = ya.spikecolor || dfltHLineColor;
24182 var xEdge = Axes.getPxPosition(gd, ya);
24183 var xBase, xEndSpike;
24184
24185 if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) {
24186 if(yMode.indexOf('toaxis') !== -1) {
24187 xBase = xEdge;
24188 xEndSpike = hLinePointX;
24189 }
24190 if(yMode.indexOf('across') !== -1) {
24191 var xAcross0 = ya._counterDomainMin;
24192 var xAcross1 = ya._counterDomainMax;
24193 if(ya.anchor === 'free') {
24194 xAcross0 = Math.min(xAcross0, ya.position);
24195 xAcross1 = Math.max(xAcross1, ya.position);
24196 }
24197 xBase = gs.l + xAcross0 * gs.w;
24198 xEndSpike = gs.l + xAcross1 * gs.w;
24199 }
24200
24201 // Foreground horizontal line (to y-axis)
24202 container.insert('line', ':first-child')
24203 .attr({
24204 x1: xBase,
24205 x2: xEndSpike,
24206 y1: hLinePointY,
24207 y2: hLinePointY,
24208 'stroke-width': yThickness,
24209 stroke: yColor,
24210 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness)
24211 })
24212 .classed('spikeline', true)
24213 .classed('crisp', true);
24214
24215 // Background horizontal Line (to y-axis)
24216 container.insert('line', ':first-child')
24217 .attr({
24218 x1: xBase,
24219 x2: xEndSpike,
24220 y1: hLinePointY,
24221 y2: hLinePointY,
24222 'stroke-width': yThickness + 2,
24223 stroke: contrastColor
24224 })
24225 .classed('spikeline', true)
24226 .classed('crisp', true);
24227 }
24228 // Y axis marker
24229 if(yMode.indexOf('marker') !== -1) {
24230 container.insert('circle', ':first-child')
24231 .attr({
24232 cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness),
24233 cy: hLinePointY,
24234 r: yThickness,
24235 fill: yColor
24236 })
24237 .classed('spikeline', true);
24238 }
24239 }
24240
24241 if(showX) {
24242 var vLinePoint = closestPoints.vLinePoint;
24243 var vLinePointX, vLinePointY;
24244
24245 xa = vLinePoint && vLinePoint.xa;
24246 ya = vLinePoint && vLinePoint.ya;
24247 var xSnap = xa.spikesnap;
24248
24249 if(xSnap === 'cursor') {
24250 vLinePointX = evt.pointerX;
24251 vLinePointY = evt.pointerY;
24252 } else {
24253 vLinePointX = xa._offset + vLinePoint.x;
24254 vLinePointY = ya._offset + vLinePoint.y;
24255 }
24256 var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ?
24257 Color.contrast(contrastColor) : vLinePoint.color;
24258 var xMode = xa.spikemode;
24259 var xThickness = xa.spikethickness;
24260 var xColor = xa.spikecolor || dfltVLineColor;
24261 var yEdge = Axes.getPxPosition(gd, xa);
24262 var yBase, yEndSpike;
24263
24264 if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) {
24265 if(xMode.indexOf('toaxis') !== -1) {
24266 yBase = yEdge;
24267 yEndSpike = vLinePointY;
24268 }
24269 if(xMode.indexOf('across') !== -1) {
24270 var yAcross0 = xa._counterDomainMin;
24271 var yAcross1 = xa._counterDomainMax;
24272 if(xa.anchor === 'free') {
24273 yAcross0 = Math.min(yAcross0, xa.position);
24274 yAcross1 = Math.max(yAcross1, xa.position);
24275 }
24276 yBase = gs.t + (1 - yAcross1) * gs.h;
24277 yEndSpike = gs.t + (1 - yAcross0) * gs.h;
24278 }
24279
24280 // Foreground vertical line (to x-axis)
24281 container.insert('line', ':first-child')
24282 .attr({
24283 x1: vLinePointX,
24284 x2: vLinePointX,
24285 y1: yBase,
24286 y2: yEndSpike,
24287 'stroke-width': xThickness,
24288 stroke: xColor,
24289 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness)
24290 })
24291 .classed('spikeline', true)
24292 .classed('crisp', true);
24293
24294 // Background vertical line (to x-axis)
24295 container.insert('line', ':first-child')
24296 .attr({
24297 x1: vLinePointX,
24298 x2: vLinePointX,
24299 y1: yBase,
24300 y2: yEndSpike,
24301 'stroke-width': xThickness + 2,
24302 stroke: contrastColor
24303 })
24304 .classed('spikeline', true)
24305 .classed('crisp', true);
24306 }
24307
24308 // X axis marker
24309 if(xMode.indexOf('marker') !== -1) {
24310 container.insert('circle', ':first-child')
24311 .attr({
24312 cx: vLinePointX,
24313 cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness),
24314 r: xThickness,
24315 fill: xColor
24316 })
24317 .classed('spikeline', true);
24318 }
24319 }
24320}
24321
24322function hoverChanged(gd, evt, oldhoverdata) {
24323 // don't emit any events if nothing changed
24324 if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true;
24325
24326 for(var i = oldhoverdata.length - 1; i >= 0; i--) {
24327 var oldPt = oldhoverdata[i];
24328 var newPt = gd._hoverdata[i];
24329
24330 if(oldPt.curveNumber !== newPt.curveNumber ||
24331 String(oldPt.pointNumber) !== String(newPt.pointNumber) ||
24332 String(oldPt.pointNumbers) !== String(newPt.pointNumbers)
24333 ) {
24334 return true;
24335 }
24336 }
24337 return false;
24338}
24339
24340function spikesChanged(gd, oldspikepoints) {
24341 // don't relayout the plot because of new spikelines if spikelines points didn't change
24342 if(!oldspikepoints) return true;
24343 if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint ||
24344 oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint
24345 ) return true;
24346 return false;
24347}
24348
24349function plainText(s, len) {
24350 return svgTextUtils.plainText(s || '', {
24351 len: len,
24352 allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em']
24353 });
24354}
24355
24356},{"../../lib":178,"../../lib/events":172,"../../lib/override_cursor":189,"../../lib/svg_text_utils":199,"../../plots/cartesian/axes":222,"../../registry":269,"../color":52,"../dragelement":71,"../drawing":74,"../legend/defaults":104,"../legend/draw":105,"./constants":86,"./helpers":88,"d3":16,"fast-isnumeric":18,"tinycolor2":35}],90:[function(_dereq_,module,exports){
24357/**
24358* Copyright 2012-2020, Plotly, Inc.
24359* All rights reserved.
24360*
24361* This source code is licensed under the MIT license found in the
24362* LICENSE file in the root directory of this source tree.
24363*/
24364
24365'use strict';
24366
24367var Lib = _dereq_('../../lib');
24368var Color = _dereq_('../color');
24369var isUnifiedHover = _dereq_('./helpers').isUnifiedHover;
24370
24371module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) {
24372 opts = opts || {};
24373
24374 function inheritFontAttr(attr) {
24375 if(!opts.font[attr]) {
24376 opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr];
24377 }
24378 }
24379
24380 // In unified hover, inherit from layout.legend if available or layout
24381 if(contOut && isUnifiedHover(contOut.hovermode)) {
24382 if(!opts.font) opts.font = {};
24383 inheritFontAttr('size');
24384 inheritFontAttr('family');
24385 inheritFontAttr('color');
24386
24387 if(contOut.legend) {
24388 if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor);
24389 if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor;
24390 } else {
24391 if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor;
24392 }
24393 }
24394
24395 coerce('hoverlabel.bgcolor', opts.bgcolor);
24396 coerce('hoverlabel.bordercolor', opts.bordercolor);
24397 coerce('hoverlabel.namelength', opts.namelength);
24398 Lib.coerceFont(coerce, 'hoverlabel.font', opts.font);
24399 coerce('hoverlabel.align', opts.align);
24400};
24401
24402},{"../../lib":178,"../color":52,"./helpers":88}],91:[function(_dereq_,module,exports){
24403/**
24404* Copyright 2012-2020, Plotly, Inc.
24405* All rights reserved.
24406*
24407* This source code is licensed under the MIT license found in the
24408* LICENSE file in the root directory of this source tree.
24409*/
24410
24411'use strict';
24412
24413var Lib = _dereq_('../../lib');
24414var layoutAttributes = _dereq_('./layout_attributes');
24415
24416module.exports = function handleHoverModeDefaults(layoutIn, layoutOut, fullData) {
24417 function coerce(attr, dflt) {
24418 // don't coerce if it is already coerced in other place e.g. in cartesian defaults
24419 if(layoutOut[attr] !== undefined) return layoutOut[attr];
24420
24421 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
24422 }
24423
24424 var clickmode = coerce('clickmode');
24425
24426 var hovermodeDflt;
24427 if(layoutOut._has('cartesian')) {
24428 if(clickmode.indexOf('select') > -1) {
24429 hovermodeDflt = 'closest';
24430 } else {
24431 // flag for 'horizontal' plots:
24432 // determines the state of the mode bar 'compare' hovermode button
24433 layoutOut._isHoriz = isHoriz(fullData, layoutOut);
24434 hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x';
24435 }
24436 } else hovermodeDflt = 'closest';
24437
24438 return coerce('hovermode', hovermodeDflt);
24439};
24440
24441function isHoriz(fullData, fullLayout) {
24442 var stackOpts = fullLayout._scatterStackOpts || {};
24443
24444 for(var i = 0; i < fullData.length; i++) {
24445 var trace = fullData[i];
24446 var subplot = trace.xaxis + trace.yaxis;
24447 var subplotStackOpts = stackOpts[subplot] || {};
24448 var groupOpts = subplotStackOpts[trace.stackgroup] || {};
24449
24450 if(trace.orientation !== 'h' && groupOpts.orientation !== 'h') {
24451 return false;
24452 }
24453 }
24454
24455 return true;
24456}
24457
24458},{"../../lib":178,"./layout_attributes":93}],92:[function(_dereq_,module,exports){
24459/**
24460* Copyright 2012-2020, Plotly, Inc.
24461* All rights reserved.
24462*
24463* This source code is licensed under the MIT license found in the
24464* LICENSE file in the root directory of this source tree.
24465*/
24466
24467'use strict';
24468
24469var d3 = _dereq_('d3');
24470var Lib = _dereq_('../../lib');
24471var dragElement = _dereq_('../dragelement');
24472var helpers = _dereq_('./helpers');
24473var layoutAttributes = _dereq_('./layout_attributes');
24474var hoverModule = _dereq_('./hover');
24475
24476module.exports = {
24477 moduleType: 'component',
24478 name: 'fx',
24479
24480 constants: _dereq_('./constants'),
24481 schema: {
24482 layout: layoutAttributes
24483 },
24484
24485 attributes: _dereq_('./attributes'),
24486 layoutAttributes: layoutAttributes,
24487
24488 supplyLayoutGlobalDefaults: _dereq_('./layout_global_defaults'),
24489 supplyDefaults: _dereq_('./defaults'),
24490 supplyLayoutDefaults: _dereq_('./layout_defaults'),
24491
24492 calc: _dereq_('./calc'),
24493
24494 getDistanceFunction: helpers.getDistanceFunction,
24495 getClosest: helpers.getClosest,
24496 inbox: helpers.inbox,
24497 quadrature: helpers.quadrature,
24498 appendArrayPointValue: helpers.appendArrayPointValue,
24499
24500 castHoverOption: castHoverOption,
24501 castHoverinfo: castHoverinfo,
24502
24503 hover: hoverModule.hover,
24504 unhover: dragElement.unhover,
24505
24506 loneHover: hoverModule.loneHover,
24507 loneUnhover: loneUnhover,
24508
24509 click: _dereq_('./click')
24510};
24511
24512function loneUnhover(containerOrSelection) {
24513 // duck type whether the arg is a d3 selection because ie9 doesn't
24514 // handle instanceof like modern browsers do.
24515 var selection = Lib.isD3Selection(containerOrSelection) ?
24516 containerOrSelection :
24517 d3.select(containerOrSelection);
24518
24519 selection.selectAll('g.hovertext').remove();
24520 selection.selectAll('.spikeline').remove();
24521}
24522
24523// helpers for traces that use Fx.loneHover
24524
24525function castHoverOption(trace, ptNumber, attr) {
24526 return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr);
24527}
24528
24529function castHoverinfo(trace, fullLayout, ptNumber) {
24530 function _coerce(val) {
24531 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
24532 }
24533
24534 return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce);
24535}
24536
24537},{"../../lib":178,"../dragelement":71,"./attributes":83,"./calc":84,"./click":85,"./constants":86,"./defaults":87,"./helpers":88,"./hover":89,"./layout_attributes":93,"./layout_defaults":94,"./layout_global_defaults":95,"d3":16}],93:[function(_dereq_,module,exports){
24538/**
24539* Copyright 2012-2020, Plotly, Inc.
24540* All rights reserved.
24541*
24542* This source code is licensed under the MIT license found in the
24543* LICENSE file in the root directory of this source tree.
24544*/
24545
24546'use strict';
24547
24548var constants = _dereq_('./constants');
24549
24550var fontAttrs = _dereq_('../../plots/font_attributes')({
24551 editType: 'none',
24552
24553});
24554fontAttrs.family.dflt = constants.HOVERFONT;
24555fontAttrs.size.dflt = constants.HOVERFONTSIZE;
24556
24557module.exports = {
24558 clickmode: {
24559 valType: 'flaglist',
24560
24561 flags: ['event', 'select'],
24562 dflt: 'event',
24563 editType: 'plot',
24564 extras: ['none'],
24565
24566 },
24567 dragmode: {
24568 valType: 'enumerated',
24569
24570 values: [
24571 'zoom',
24572 'pan',
24573 'select',
24574 'lasso',
24575 'drawclosedpath',
24576 'drawopenpath',
24577 'drawline',
24578 'drawrect',
24579 'drawcircle',
24580 'orbit',
24581 'turntable',
24582 false
24583 ],
24584 dflt: 'zoom',
24585 editType: 'modebar',
24586
24587 },
24588 hovermode: {
24589 valType: 'enumerated',
24590
24591 values: ['x', 'y', 'closest', false, 'x unified', 'y unified'],
24592 editType: 'modebar',
24593
24594 },
24595 hoverdistance: {
24596 valType: 'integer',
24597 min: -1,
24598 dflt: 20,
24599
24600 editType: 'none',
24601
24602 },
24603 spikedistance: {
24604 valType: 'integer',
24605 min: -1,
24606 dflt: 20,
24607
24608 editType: 'none',
24609
24610 },
24611 hoverlabel: {
24612 bgcolor: {
24613 valType: 'color',
24614
24615 editType: 'none',
24616
24617 },
24618 bordercolor: {
24619 valType: 'color',
24620
24621 editType: 'none',
24622
24623 },
24624 font: fontAttrs,
24625 align: {
24626 valType: 'enumerated',
24627 values: ['left', 'right', 'auto'],
24628 dflt: 'auto',
24629
24630 editType: 'none',
24631
24632 },
24633 namelength: {
24634 valType: 'integer',
24635 min: -1,
24636 dflt: 15,
24637
24638 editType: 'none',
24639
24640 },
24641 editType: 'none'
24642 },
24643 selectdirection: {
24644 valType: 'enumerated',
24645
24646 values: ['h', 'v', 'd', 'any'],
24647 dflt: 'any',
24648
24649 editType: 'none'
24650 }
24651};
24652
24653},{"../../plots/font_attributes":250,"./constants":86}],94:[function(_dereq_,module,exports){
24654/**
24655* Copyright 2012-2020, Plotly, Inc.
24656* All rights reserved.
24657*
24658* This source code is licensed under the MIT license found in the
24659* LICENSE file in the root directory of this source tree.
24660*/
24661
24662'use strict';
24663
24664var Lib = _dereq_('../../lib');
24665var isUnifiedHover = _dereq_('./helpers').isUnifiedHover;
24666var layoutAttributes = _dereq_('./layout_attributes');
24667var handleHoverModeDefaults = _dereq_('./hovermode_defaults');
24668var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
24669
24670module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
24671 function coerce(attr, dflt) {
24672 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
24673 }
24674
24675 var hoverMode = handleHoverModeDefaults(layoutIn, layoutOut, fullData);
24676 if(hoverMode) {
24677 coerce('hoverdistance');
24678 coerce('spikedistance', isUnifiedHover(hoverMode) ? -1 : undefined);
24679 }
24680
24681 var dragMode = coerce('dragmode');
24682 if(dragMode === 'select') coerce('selectdirection');
24683
24684 // if only mapbox or geo subplots is present on graph,
24685 // reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,
24686 // so that the correct modebar button is active
24687 var hasMapbox = layoutOut._has('mapbox');
24688 var hasGeo = layoutOut._has('geo');
24689 var len = layoutOut._basePlotModules.length;
24690
24691 if(layoutOut.dragmode === 'zoom' && (
24692 ((hasMapbox || hasGeo) && len === 1) ||
24693 (hasMapbox && hasGeo && len === 2)
24694 )) {
24695 layoutOut.dragmode = 'pan';
24696 }
24697
24698 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
24699};
24700
24701},{"../../lib":178,"./helpers":88,"./hoverlabel_defaults":90,"./hovermode_defaults":91,"./layout_attributes":93}],95:[function(_dereq_,module,exports){
24702/**
24703* Copyright 2012-2020, Plotly, Inc.
24704* All rights reserved.
24705*
24706* This source code is licensed under the MIT license found in the
24707* LICENSE file in the root directory of this source tree.
24708*/
24709
24710'use strict';
24711
24712var Lib = _dereq_('../../lib');
24713var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
24714var layoutAttributes = _dereq_('./layout_attributes');
24715
24716module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) {
24717 function coerce(attr, dflt) {
24718 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
24719 }
24720
24721 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
24722};
24723
24724},{"../../lib":178,"./hoverlabel_defaults":90,"./layout_attributes":93}],96:[function(_dereq_,module,exports){
24725/**
24726* Copyright 2012-2020, Plotly, Inc.
24727* All rights reserved.
24728*
24729* This source code is licensed under the MIT license found in the
24730* LICENSE file in the root directory of this source tree.
24731*/
24732
24733'use strict';
24734
24735var Lib = _dereq_('../../lib');
24736var counterRegex = _dereq_('../../lib/regex').counter;
24737var domainAttrs = _dereq_('../../plots/domain').attributes;
24738var cartesianIdRegex = _dereq_('../../plots/cartesian/constants').idRegex;
24739var Template = _dereq_('../../plot_api/plot_template');
24740
24741var gridAttrs = {
24742 rows: {
24743 valType: 'integer',
24744 min: 1,
24745
24746 editType: 'plot',
24747
24748 },
24749 roworder: {
24750 valType: 'enumerated',
24751 values: ['top to bottom', 'bottom to top'],
24752 dflt: 'top to bottom',
24753
24754 editType: 'plot',
24755
24756 },
24757 columns: {
24758 valType: 'integer',
24759 min: 1,
24760
24761 editType: 'plot',
24762
24763 },
24764 subplots: {
24765 valType: 'info_array',
24766 freeLength: true,
24767 dimensions: 2,
24768 items: {valType: 'enumerated', values: [counterRegex('xy').toString(), ''], editType: 'plot'},
24769
24770 editType: 'plot',
24771
24772 },
24773 xaxes: {
24774 valType: 'info_array',
24775 freeLength: true,
24776 items: {valType: 'enumerated', values: [cartesianIdRegex.x.toString(), ''], editType: 'plot'},
24777
24778 editType: 'plot',
24779
24780 },
24781 yaxes: {
24782 valType: 'info_array',
24783 freeLength: true,
24784 items: {valType: 'enumerated', values: [cartesianIdRegex.y.toString(), ''], editType: 'plot'},
24785
24786 editType: 'plot',
24787
24788 },
24789 pattern: {
24790 valType: 'enumerated',
24791 values: ['independent', 'coupled'],
24792 dflt: 'coupled',
24793
24794 editType: 'plot',
24795
24796 },
24797 xgap: {
24798 valType: 'number',
24799 min: 0,
24800 max: 1,
24801
24802 editType: 'plot',
24803
24804 },
24805 ygap: {
24806 valType: 'number',
24807 min: 0,
24808 max: 1,
24809
24810 editType: 'plot',
24811
24812 },
24813 domain: domainAttrs({name: 'grid', editType: 'plot', noGridCell: true}, {
24814
24815 }),
24816 xside: {
24817 valType: 'enumerated',
24818 values: ['bottom', 'bottom plot', 'top plot', 'top'],
24819 dflt: 'bottom plot',
24820
24821 editType: 'plot',
24822
24823 },
24824 yside: {
24825 valType: 'enumerated',
24826 values: ['left', 'left plot', 'right plot', 'right'],
24827 dflt: 'left plot',
24828
24829 editType: 'plot',
24830
24831 },
24832 editType: 'plot'
24833};
24834
24835function getAxes(layout, grid, axLetter) {
24836 var gridVal = grid[axLetter + 'axes'];
24837 var splomVal = Object.keys((layout._splomAxes || {})[axLetter] || {});
24838
24839 if(Array.isArray(gridVal)) return gridVal;
24840 if(splomVal.length) return splomVal;
24841}
24842
24843// the shape of the grid - this needs to be done BEFORE supplyDataDefaults
24844// so that non-subplot traces can place themselves in the grid
24845function sizeDefaults(layoutIn, layoutOut) {
24846 var gridIn = layoutIn.grid || {};
24847 var xAxes = getAxes(layoutOut, gridIn, 'x');
24848 var yAxes = getAxes(layoutOut, gridIn, 'y');
24849
24850 if(!layoutIn.grid && !xAxes && !yAxes) return;
24851
24852 var hasSubplotGrid = Array.isArray(gridIn.subplots) && Array.isArray(gridIn.subplots[0]);
24853 var hasXaxes = Array.isArray(xAxes);
24854 var hasYaxes = Array.isArray(yAxes);
24855 var isSplomGenerated = (
24856 hasXaxes && xAxes !== gridIn.xaxes &&
24857 hasYaxes && yAxes !== gridIn.yaxes
24858 );
24859
24860 var dfltRows, dfltColumns;
24861
24862 if(hasSubplotGrid) {
24863 dfltRows = gridIn.subplots.length;
24864 dfltColumns = gridIn.subplots[0].length;
24865 } else {
24866 if(hasYaxes) dfltRows = yAxes.length;
24867 if(hasXaxes) dfltColumns = xAxes.length;
24868 }
24869
24870 var gridOut = Template.newContainer(layoutOut, 'grid');
24871
24872 function coerce(attr, dflt) {
24873 return Lib.coerce(gridIn, gridOut, gridAttrs, attr, dflt);
24874 }
24875
24876 var rows = coerce('rows', dfltRows);
24877 var columns = coerce('columns', dfltColumns);
24878
24879 if(!(rows * columns > 1)) {
24880 delete layoutOut.grid;
24881 return;
24882 }
24883
24884 if(!hasSubplotGrid && !hasXaxes && !hasYaxes) {
24885 var useDefaultSubplots = coerce('pattern') === 'independent';
24886 if(useDefaultSubplots) hasSubplotGrid = true;
24887 }
24888 gridOut._hasSubplotGrid = hasSubplotGrid;
24889
24890 var rowOrder = coerce('roworder');
24891 var reversed = rowOrder === 'top to bottom';
24892
24893 var dfltGapX = hasSubplotGrid ? 0.2 : 0.1;
24894 var dfltGapY = hasSubplotGrid ? 0.3 : 0.1;
24895
24896 var dfltSideX, dfltSideY;
24897 if(isSplomGenerated && layoutOut._splomGridDflt) {
24898 dfltSideX = layoutOut._splomGridDflt.xside;
24899 dfltSideY = layoutOut._splomGridDflt.yside;
24900 }
24901
24902 gridOut._domains = {
24903 x: fillGridPositions('x', coerce, dfltGapX, dfltSideX, columns),
24904 y: fillGridPositions('y', coerce, dfltGapY, dfltSideY, rows, reversed)
24905 };
24906}
24907
24908// coerce x or y sizing attributes and return an array of domains for this direction
24909function fillGridPositions(axLetter, coerce, dfltGap, dfltSide, len, reversed) {
24910 var dirGap = coerce(axLetter + 'gap', dfltGap);
24911 var domain = coerce('domain.' + axLetter);
24912 coerce(axLetter + 'side', dfltSide);
24913
24914 var out = new Array(len);
24915 var start = domain[0];
24916 var step = (domain[1] - start) / (len - dirGap);
24917 var cellDomain = step * (1 - dirGap);
24918 for(var i = 0; i < len; i++) {
24919 var cellStart = start + step * i;
24920 out[reversed ? (len - 1 - i) : i] = [cellStart, cellStart + cellDomain];
24921 }
24922 return out;
24923}
24924
24925// the (cartesian) contents of the grid - this needs to happen AFTER supplyDataDefaults
24926// so that we know what cartesian subplots are available
24927function contentDefaults(layoutIn, layoutOut) {
24928 var gridOut = layoutOut.grid;
24929 // make sure we got to the end of handleGridSizing
24930 if(!gridOut || !gridOut._domains) return;
24931
24932 var gridIn = layoutIn.grid || {};
24933 var subplots = layoutOut._subplots;
24934 var hasSubplotGrid = gridOut._hasSubplotGrid;
24935 var rows = gridOut.rows;
24936 var columns = gridOut.columns;
24937 var useDefaultSubplots = gridOut.pattern === 'independent';
24938
24939 var i, j, xId, yId, subplotId, subplotsOut, yPos;
24940
24941 var axisMap = gridOut._axisMap = {};
24942
24943 if(hasSubplotGrid) {
24944 var subplotsIn = gridIn.subplots || [];
24945 subplotsOut = gridOut.subplots = new Array(rows);
24946 var index = 1;
24947
24948 for(i = 0; i < rows; i++) {
24949 var rowOut = subplotsOut[i] = new Array(columns);
24950 var rowIn = subplotsIn[i] || [];
24951 for(j = 0; j < columns; j++) {
24952 if(useDefaultSubplots) {
24953 subplotId = (index === 1) ? 'xy' : ('x' + index + 'y' + index);
24954 index++;
24955 } else subplotId = rowIn[j];
24956
24957 rowOut[j] = '';
24958
24959 if(subplots.cartesian.indexOf(subplotId) !== -1) {
24960 yPos = subplotId.indexOf('y');
24961 xId = subplotId.slice(0, yPos);
24962 yId = subplotId.slice(yPos);
24963 if((axisMap[xId] !== undefined && axisMap[xId] !== j) ||
24964 (axisMap[yId] !== undefined && axisMap[yId] !== i)
24965 ) {
24966 continue;
24967 }
24968
24969 rowOut[j] = subplotId;
24970 axisMap[xId] = j;
24971 axisMap[yId] = i;
24972 }
24973 }
24974 }
24975 } else {
24976 var xAxes = getAxes(layoutOut, gridIn, 'x');
24977 var yAxes = getAxes(layoutOut, gridIn, 'y');
24978 gridOut.xaxes = fillGridAxes(xAxes, subplots.xaxis, columns, axisMap, 'x');
24979 gridOut.yaxes = fillGridAxes(yAxes, subplots.yaxis, rows, axisMap, 'y');
24980 }
24981
24982 var anchors = gridOut._anchors = {};
24983 var reversed = gridOut.roworder === 'top to bottom';
24984
24985 for(var axisId in axisMap) {
24986 var axLetter = axisId.charAt(0);
24987 var side = gridOut[axLetter + 'side'];
24988
24989 var i0, inc, iFinal;
24990
24991 if(side.length < 8) {
24992 // grid edge - ie not "* plot" - make these as free axes
24993 // since we're not guaranteed to have a subplot there at all
24994 anchors[axisId] = 'free';
24995 } else if(axLetter === 'x') {
24996 if((side.charAt(0) === 't') === reversed) {
24997 i0 = 0;
24998 inc = 1;
24999 iFinal = rows;
25000 } else {
25001 i0 = rows - 1;
25002 inc = -1;
25003 iFinal = -1;
25004 }
25005 if(hasSubplotGrid) {
25006 var column = axisMap[axisId];
25007 for(i = i0; i !== iFinal; i += inc) {
25008 subplotId = subplotsOut[i][column];
25009 if(!subplotId) continue;
25010 yPos = subplotId.indexOf('y');
25011 if(subplotId.slice(0, yPos) === axisId) {
25012 anchors[axisId] = subplotId.slice(yPos);
25013 break;
25014 }
25015 }
25016 } else {
25017 for(i = i0; i !== iFinal; i += inc) {
25018 yId = gridOut.yaxes[i];
25019 if(subplots.cartesian.indexOf(axisId + yId) !== -1) {
25020 anchors[axisId] = yId;
25021 break;
25022 }
25023 }
25024 }
25025 } else {
25026 if((side.charAt(0) === 'l')) {
25027 i0 = 0;
25028 inc = 1;
25029 iFinal = columns;
25030 } else {
25031 i0 = columns - 1;
25032 inc = -1;
25033 iFinal = -1;
25034 }
25035 if(hasSubplotGrid) {
25036 var row = axisMap[axisId];
25037 for(i = i0; i !== iFinal; i += inc) {
25038 subplotId = subplotsOut[row][i];
25039 if(!subplotId) continue;
25040 yPos = subplotId.indexOf('y');
25041 if(subplotId.slice(yPos) === axisId) {
25042 anchors[axisId] = subplotId.slice(0, yPos);
25043 break;
25044 }
25045 }
25046 } else {
25047 for(i = i0; i !== iFinal; i += inc) {
25048 xId = gridOut.xaxes[i];
25049 if(subplots.cartesian.indexOf(xId + axisId) !== -1) {
25050 anchors[axisId] = xId;
25051 break;
25052 }
25053 }
25054 }
25055 }
25056 }
25057}
25058
25059function fillGridAxes(axesIn, axesAllowed, len, axisMap, axLetter) {
25060 var out = new Array(len);
25061 var i;
25062
25063 function fillOneAxis(i, axisId) {
25064 if(axesAllowed.indexOf(axisId) !== -1 && axisMap[axisId] === undefined) {
25065 out[i] = axisId;
25066 axisMap[axisId] = i;
25067 } else out[i] = '';
25068 }
25069
25070 if(Array.isArray(axesIn)) {
25071 for(i = 0; i < len; i++) {
25072 fillOneAxis(i, axesIn[i]);
25073 }
25074 } else {
25075 // default axis list is the first `len` axis ids
25076 fillOneAxis(0, axLetter);
25077 for(i = 1; i < len; i++) {
25078 fillOneAxis(i, axLetter + (i + 1));
25079 }
25080 }
25081
25082 return out;
25083}
25084
25085module.exports = {
25086 moduleType: 'component',
25087 name: 'grid',
25088
25089 schema: {
25090 layout: {grid: gridAttrs}
25091 },
25092
25093 layoutAttributes: gridAttrs,
25094 sizeDefaults: sizeDefaults,
25095 contentDefaults: contentDefaults
25096};
25097
25098},{"../../lib":178,"../../lib/regex":193,"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228,"../../plots/domain":249}],97:[function(_dereq_,module,exports){
25099/**
25100* Copyright 2012-2020, Plotly, Inc.
25101* All rights reserved.
25102*
25103* This source code is licensed under the MIT license found in the
25104* LICENSE file in the root directory of this source tree.
25105*/
25106
25107'use strict';
25108
25109var cartesianConstants = _dereq_('../../plots/cartesian/constants');
25110var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
25111
25112
25113module.exports = templatedArray('image', {
25114 visible: {
25115 valType: 'boolean',
25116
25117 dflt: true,
25118 editType: 'arraydraw',
25119
25120 },
25121
25122 source: {
25123 valType: 'string',
25124
25125 editType: 'arraydraw',
25126
25127 },
25128
25129 layer: {
25130 valType: 'enumerated',
25131 values: ['below', 'above'],
25132 dflt: 'above',
25133
25134 editType: 'arraydraw',
25135
25136 },
25137
25138 sizex: {
25139 valType: 'number',
25140
25141 dflt: 0,
25142 editType: 'arraydraw',
25143
25144 },
25145
25146 sizey: {
25147 valType: 'number',
25148
25149 dflt: 0,
25150 editType: 'arraydraw',
25151
25152 },
25153
25154 sizing: {
25155 valType: 'enumerated',
25156 values: ['fill', 'contain', 'stretch'],
25157 dflt: 'contain',
25158
25159 editType: 'arraydraw',
25160
25161 },
25162
25163 opacity: {
25164 valType: 'number',
25165
25166 min: 0,
25167 max: 1,
25168 dflt: 1,
25169 editType: 'arraydraw',
25170
25171 },
25172
25173 x: {
25174 valType: 'any',
25175
25176 dflt: 0,
25177 editType: 'arraydraw',
25178
25179 },
25180
25181 y: {
25182 valType: 'any',
25183
25184 dflt: 0,
25185 editType: 'arraydraw',
25186
25187 },
25188
25189 xanchor: {
25190 valType: 'enumerated',
25191 values: ['left', 'center', 'right'],
25192 dflt: 'left',
25193
25194 editType: 'arraydraw',
25195
25196 },
25197
25198 yanchor: {
25199 valType: 'enumerated',
25200 values: ['top', 'middle', 'bottom'],
25201 dflt: 'top',
25202
25203 editType: 'arraydraw',
25204
25205 },
25206
25207 xref: {
25208 valType: 'enumerated',
25209 values: [
25210 'paper',
25211 cartesianConstants.idRegex.x.toString()
25212 ],
25213 dflt: 'paper',
25214
25215 editType: 'arraydraw',
25216
25217 },
25218
25219 yref: {
25220 valType: 'enumerated',
25221 values: [
25222 'paper',
25223 cartesianConstants.idRegex.y.toString()
25224 ],
25225 dflt: 'paper',
25226
25227 editType: 'arraydraw',
25228
25229 },
25230 editType: 'arraydraw'
25231});
25232
25233},{"../../plot_api/plot_template":212,"../../plots/cartesian/constants":228}],98:[function(_dereq_,module,exports){
25234/**
25235* Copyright 2012-2020, Plotly, Inc.
25236* All rights reserved.
25237*
25238* This source code is licensed under the MIT license found in the
25239* LICENSE file in the root directory of this source tree.
25240*/
25241
25242
25243'use strict';
25244
25245var isNumeric = _dereq_('fast-isnumeric');
25246var toLogRange = _dereq_('../../lib/to_log_range');
25247
25248/*
25249 * convertCoords: when converting an axis between log and linear
25250 * you need to alter any images on that axis to keep them
25251 * pointing at the same data point.
25252 * In v2.0 this will become obsolete (or perhaps size will still need conversion?)
25253 * we convert size by declaring that the maximum extent *in data units* should be
25254 * the same, assuming the image is anchored by its center (could remove that restriction
25255 * if we think it's important) even though the actual left and right values will not be
25256 * quite the same since the scale becomes nonlinear (and central anchor means the pixel
25257 * center of the image, not the data units center)
25258 *
25259 * gd: the plot div
25260 * ax: the axis being changed
25261 * newType: the type it's getting
25262 * doExtra: function(attr, val) from inside relayout that sets the attribute.
25263 * Use this to make the changes as it's aware if any other changes in the
25264 * same relayout call should override this conversion.
25265 */
25266module.exports = function convertCoords(gd, ax, newType, doExtra) {
25267 ax = ax || {};
25268
25269 var toLog = (newType === 'log') && (ax.type === 'linear');
25270 var fromLog = (newType === 'linear') && (ax.type === 'log');
25271
25272 if(!(toLog || fromLog)) return;
25273
25274 var images = gd._fullLayout.images;
25275 var axLetter = ax._id.charAt(0);
25276 var image;
25277 var attrPrefix;
25278
25279 for(var i = 0; i < images.length; i++) {
25280 image = images[i];
25281 attrPrefix = 'images[' + i + '].';
25282
25283 if(image[axLetter + 'ref'] === ax._id) {
25284 var currentPos = image[axLetter];
25285 var currentSize = image['size' + axLetter];
25286 var newPos = null;
25287 var newSize = null;
25288
25289 if(toLog) {
25290 newPos = toLogRange(currentPos, ax.range);
25291
25292 // this is the inverse of the conversion we do in fromLog below
25293 // so that the conversion is reversible (notice the fromLog conversion
25294 // is like sinh, and this one looks like arcsinh)
25295 var dx = currentSize / Math.pow(10, newPos) / 2;
25296 newSize = 2 * Math.log(dx + Math.sqrt(1 + dx * dx)) / Math.LN10;
25297 } else {
25298 newPos = Math.pow(10, currentPos);
25299 newSize = newPos * (Math.pow(10, currentSize / 2) - Math.pow(10, -currentSize / 2));
25300 }
25301
25302 // if conversion failed, delete the value so it can get a default later on
25303 if(!isNumeric(newPos)) {
25304 newPos = null;
25305 newSize = null;
25306 } else if(!isNumeric(newSize)) newSize = null;
25307
25308 doExtra(attrPrefix + axLetter, newPos);
25309 doExtra(attrPrefix + 'size' + axLetter, newSize);
25310 }
25311 }
25312};
25313
25314},{"../../lib/to_log_range":201,"fast-isnumeric":18}],99:[function(_dereq_,module,exports){
25315/**
25316* Copyright 2012-2020, Plotly, Inc.
25317* All rights reserved.
25318*
25319* This source code is licensed under the MIT license found in the
25320* LICENSE file in the root directory of this source tree.
25321*/
25322
25323'use strict';
25324
25325var Lib = _dereq_('../../lib');
25326var Axes = _dereq_('../../plots/cartesian/axes');
25327var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
25328
25329var attributes = _dereq_('./attributes');
25330var name = 'images';
25331
25332module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
25333 var opts = {
25334 name: name,
25335 handleItemDefaults: imageDefaults
25336 };
25337
25338 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
25339};
25340
25341
25342function imageDefaults(imageIn, imageOut, fullLayout) {
25343 function coerce(attr, dflt) {
25344 return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
25345 }
25346
25347 var source = coerce('source');
25348 var visible = coerce('visible', !!source);
25349
25350 if(!visible) return imageOut;
25351
25352 coerce('layer');
25353 coerce('xanchor');
25354 coerce('yanchor');
25355 coerce('sizex');
25356 coerce('sizey');
25357 coerce('sizing');
25358 coerce('opacity');
25359
25360 var gdMock = { _fullLayout: fullLayout };
25361 var axLetters = ['x', 'y'];
25362
25363 for(var i = 0; i < 2; i++) {
25364 // 'paper' is the fallback axref
25365 var axLetter = axLetters[i];
25366 var axRef = Axes.coerceRef(imageIn, imageOut, gdMock, axLetter, 'paper');
25367
25368 if(axRef !== 'paper') {
25369 var ax = Axes.getFromId(gdMock, axRef);
25370 ax._imgIndices.push(imageOut._index);
25371 }
25372
25373 Axes.coercePosition(imageOut, gdMock, coerce, axRef, axLetter, 0);
25374 }
25375
25376 return imageOut;
25377}
25378
25379},{"../../lib":178,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":97}],100:[function(_dereq_,module,exports){
25380/**
25381* Copyright 2012-2020, Plotly, Inc.
25382* All rights reserved.
25383*
25384* This source code is licensed under the MIT license found in the
25385* LICENSE file in the root directory of this source tree.
25386*/
25387
25388'use strict';
25389
25390var d3 = _dereq_('d3');
25391var Drawing = _dereq_('../drawing');
25392var Axes = _dereq_('../../plots/cartesian/axes');
25393var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
25394
25395module.exports = function draw(gd) {
25396 var fullLayout = gd._fullLayout;
25397 var imageDataAbove = [];
25398 var imageDataSubplot = {};
25399 var imageDataBelow = [];
25400 var subplot;
25401 var i;
25402
25403 // Sort into top, subplot, and bottom layers
25404 for(i = 0; i < fullLayout.images.length; i++) {
25405 var img = fullLayout.images[i];
25406
25407 if(img.visible) {
25408 if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
25409 subplot = img.xref + img.yref;
25410
25411 var plotinfo = fullLayout._plots[subplot];
25412
25413 if(!plotinfo) {
25414 // Fall back to _imageLowerLayer in case the requested subplot doesn't exist.
25415 // This can happen if you reference the image to an x / y axis combination
25416 // that doesn't have any data on it (and layer is below)
25417 imageDataBelow.push(img);
25418 continue;
25419 }
25420
25421 if(plotinfo.mainplot) {
25422 subplot = plotinfo.mainplot.id;
25423 }
25424
25425 if(!imageDataSubplot[subplot]) {
25426 imageDataSubplot[subplot] = [];
25427 }
25428 imageDataSubplot[subplot].push(img);
25429 } else if(img.layer === 'above') {
25430 imageDataAbove.push(img);
25431 } else {
25432 imageDataBelow.push(img);
25433 }
25434 }
25435 }
25436
25437
25438 var anchors = {
25439 x: {
25440 left: { sizing: 'xMin', offset: 0 },
25441 center: { sizing: 'xMid', offset: -1 / 2 },
25442 right: { sizing: 'xMax', offset: -1 }
25443 },
25444 y: {
25445 top: { sizing: 'YMin', offset: 0 },
25446 middle: { sizing: 'YMid', offset: -1 / 2 },
25447 bottom: { sizing: 'YMax', offset: -1 }
25448 }
25449 };
25450
25451
25452 // Images must be converted to dataURL's for exporting.
25453 function setImage(d) {
25454 var thisImage = d3.select(this);
25455
25456 if(this._imgSrc === d.source) {
25457 return;
25458 }
25459
25460 thisImage.attr('xmlns', xmlnsNamespaces.svg);
25461
25462 if(d.source && d.source.slice(0, 5) === 'data:') {
25463 thisImage.attr('xlink:href', d.source);
25464 this._imgSrc = d.source;
25465 } else {
25466 var imagePromise = new Promise(function(resolve) {
25467 var img = new Image();
25468 this.img = img;
25469
25470 // If not set, a `tainted canvas` error is thrown
25471 img.setAttribute('crossOrigin', 'anonymous');
25472 img.onerror = errorHandler;
25473 img.onload = function() {
25474 var canvas = document.createElement('canvas');
25475 canvas.width = this.width;
25476 canvas.height = this.height;
25477
25478 var ctx = canvas.getContext('2d');
25479 ctx.drawImage(this, 0, 0);
25480
25481 var dataURL = canvas.toDataURL('image/png');
25482
25483 thisImage.attr('xlink:href', dataURL);
25484
25485 // resolve promise in onload handler instead of on 'load' to support IE11
25486 // see https://github.com/plotly/plotly.js/issues/1685
25487 // for more details
25488 resolve();
25489 };
25490
25491 thisImage.on('error', errorHandler);
25492
25493 img.src = d.source;
25494 this._imgSrc = d.source;
25495
25496 function errorHandler() {
25497 thisImage.remove();
25498 resolve();
25499 }
25500 }.bind(this));
25501
25502 gd._promises.push(imagePromise);
25503 }
25504 }
25505
25506 function applyAttributes(d) {
25507 var thisImage = d3.select(this);
25508
25509 // Axes if specified
25510 var xa = Axes.getFromId(gd, d.xref);
25511 var ya = Axes.getFromId(gd, d.yref);
25512
25513 var size = fullLayout._size;
25514 var width = xa ? Math.abs(xa.l2p(d.sizex) - xa.l2p(0)) : d.sizex * size.w;
25515 var height = ya ? Math.abs(ya.l2p(d.sizey) - ya.l2p(0)) : d.sizey * size.h;
25516
25517 // Offsets for anchor positioning
25518 var xOffset = width * anchors.x[d.xanchor].offset;
25519 var yOffset = height * anchors.y[d.yanchor].offset;
25520
25521 var sizing = anchors.x[d.xanchor].sizing + anchors.y[d.yanchor].sizing;
25522
25523 // Final positions
25524 var xPos = (xa ? xa.r2p(d.x) + xa._offset : d.x * size.w + size.l) + xOffset;
25525 var yPos = (ya ? ya.r2p(d.y) + ya._offset : size.h - d.y * size.h + size.t) + yOffset;
25526
25527 // Construct the proper aspectRatio attribute
25528 switch(d.sizing) {
25529 case 'fill':
25530 sizing += ' slice';
25531 break;
25532
25533 case 'stretch':
25534 sizing = 'none';
25535 break;
25536 }
25537
25538 thisImage.attr({
25539 x: xPos,
25540 y: yPos,
25541 width: width,
25542 height: height,
25543 preserveAspectRatio: sizing,
25544 opacity: d.opacity
25545 });
25546
25547
25548 // Set proper clipping on images
25549 var xId = xa ? xa._id : '';
25550 var yId = ya ? ya._id : '';
25551 var clipAxes = xId + yId;
25552
25553 Drawing.setClipUrl(
25554 thisImage,
25555 clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
25556 gd
25557 );
25558 }
25559
25560 var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
25561 .data(imageDataBelow);
25562 var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
25563 .data(imageDataAbove);
25564
25565 imagesBelow.enter().append('image');
25566 imagesAbove.enter().append('image');
25567
25568 imagesBelow.exit().remove();
25569 imagesAbove.exit().remove();
25570
25571 imagesBelow.each(function(d) {
25572 setImage.bind(this)(d);
25573 applyAttributes.bind(this)(d);
25574 });
25575 imagesAbove.each(function(d) {
25576 setImage.bind(this)(d);
25577 applyAttributes.bind(this)(d);
25578 });
25579
25580 var allSubplots = Object.keys(fullLayout._plots);
25581 for(i = 0; i < allSubplots.length; i++) {
25582 subplot = allSubplots[i];
25583 var subplotObj = fullLayout._plots[subplot];
25584
25585 // filter out overlaid plots (which havd their images on the main plot)
25586 // and gl2d plots (which don't support below images, at least not yet)
25587 if(!subplotObj.imagelayer) continue;
25588
25589 var imagesOnSubplot = subplotObj.imagelayer.selectAll('image')
25590 // even if there are no images on this subplot, we need to run
25591 // enter and exit in case there were previously
25592 .data(imageDataSubplot[subplot] || []);
25593
25594 imagesOnSubplot.enter().append('image');
25595 imagesOnSubplot.exit().remove();
25596
25597 imagesOnSubplot.each(function(d) {
25598 setImage.bind(this)(d);
25599 applyAttributes.bind(this)(d);
25600 });
25601 }
25602};
25603
25604},{"../../constants/xmlns_namespaces":159,"../../plots/cartesian/axes":222,"../drawing":74,"d3":16}],101:[function(_dereq_,module,exports){
25605/**
25606* Copyright 2012-2020, Plotly, Inc.
25607* All rights reserved.
25608*
25609* This source code is licensed under the MIT license found in the
25610* LICENSE file in the root directory of this source tree.
25611*/
25612
25613'use strict';
25614
25615module.exports = {
25616 moduleType: 'component',
25617 name: 'images',
25618
25619 layoutAttributes: _dereq_('./attributes'),
25620 supplyLayoutDefaults: _dereq_('./defaults'),
25621 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('images'),
25622
25623 draw: _dereq_('./draw'),
25624
25625 convertCoords: _dereq_('./convert_coords')
25626};
25627
25628},{"../../plots/cartesian/include_components":234,"./attributes":97,"./convert_coords":98,"./defaults":99,"./draw":100}],102:[function(_dereq_,module,exports){
25629/**
25630* Copyright 2012-2020, Plotly, Inc.
25631* All rights reserved.
25632*
25633* This source code is licensed under the MIT license found in the
25634* LICENSE file in the root directory of this source tree.
25635*/
25636
25637'use strict';
25638
25639var fontAttrs = _dereq_('../../plots/font_attributes');
25640var colorAttrs = _dereq_('../color/attributes');
25641
25642
25643module.exports = {
25644 bgcolor: {
25645 valType: 'color',
25646
25647 editType: 'legend',
25648
25649 },
25650 bordercolor: {
25651 valType: 'color',
25652 dflt: colorAttrs.defaultLine,
25653
25654 editType: 'legend',
25655
25656 },
25657 borderwidth: {
25658 valType: 'number',
25659 min: 0,
25660 dflt: 0,
25661
25662 editType: 'legend',
25663
25664 },
25665 font: fontAttrs({
25666 editType: 'legend',
25667
25668 }),
25669 orientation: {
25670 valType: 'enumerated',
25671 values: ['v', 'h'],
25672 dflt: 'v',
25673
25674 editType: 'legend',
25675
25676 },
25677 traceorder: {
25678 valType: 'flaglist',
25679 flags: ['reversed', 'grouped'],
25680 extras: ['normal'],
25681
25682 editType: 'legend',
25683
25684 },
25685 tracegroupgap: {
25686 valType: 'number',
25687 min: 0,
25688 dflt: 10,
25689
25690 editType: 'legend',
25691
25692 },
25693 itemsizing: {
25694 valType: 'enumerated',
25695 values: ['trace', 'constant'],
25696 dflt: 'trace',
25697
25698 editType: 'legend',
25699
25700 },
25701
25702 itemclick: {
25703 valType: 'enumerated',
25704 values: ['toggle', 'toggleothers', false],
25705 dflt: 'toggle',
25706
25707 editType: 'legend',
25708
25709 },
25710 itemdoubleclick: {
25711 valType: 'enumerated',
25712 values: ['toggle', 'toggleothers', false],
25713 dflt: 'toggleothers',
25714
25715 editType: 'legend',
25716
25717 },
25718
25719 x: {
25720 valType: 'number',
25721 min: -2,
25722 max: 3,
25723
25724 editType: 'legend',
25725
25726 },
25727 xanchor: {
25728 valType: 'enumerated',
25729 values: ['auto', 'left', 'center', 'right'],
25730 dflt: 'left',
25731
25732 editType: 'legend',
25733
25734 },
25735 y: {
25736 valType: 'number',
25737 min: -2,
25738 max: 3,
25739
25740 editType: 'legend',
25741
25742 },
25743 yanchor: {
25744 valType: 'enumerated',
25745 values: ['auto', 'top', 'middle', 'bottom'],
25746
25747 editType: 'legend',
25748
25749 },
25750 uirevision: {
25751 valType: 'any',
25752
25753 editType: 'none',
25754
25755 },
25756 valign: {
25757 valType: 'enumerated',
25758 values: ['top', 'middle', 'bottom'],
25759 dflt: 'middle',
25760
25761 editType: 'legend',
25762
25763 },
25764 title: {
25765 text: {
25766 valType: 'string',
25767 dflt: '',
25768
25769 editType: 'legend',
25770
25771 },
25772 font: fontAttrs({
25773 editType: 'legend',
25774
25775 }),
25776 side: {
25777 valType: 'enumerated',
25778 values: ['top', 'left', 'top left'],
25779
25780 editType: 'legend',
25781
25782 },
25783 editType: 'legend',
25784 },
25785
25786 editType: 'legend'
25787};
25788
25789},{"../../plots/font_attributes":250,"../color/attributes":51}],103:[function(_dereq_,module,exports){
25790/**
25791* Copyright 2012-2020, Plotly, Inc.
25792* All rights reserved.
25793*
25794* This source code is licensed under the MIT license found in the
25795* LICENSE file in the root directory of this source tree.
25796*/
25797
25798'use strict';
25799
25800module.exports = {
25801 scrollBarWidth: 6,
25802 scrollBarMinHeight: 20,
25803 scrollBarColor: '#808BA4',
25804 scrollBarMargin: 4,
25805 scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0},
25806
25807 // number of px between legend title and (left) side of legend (always in x direction and from inner border)
25808 titlePad: 2,
25809 // number of px between legend symbol and legend text (always in x direction)
25810 textGap: 40,
25811 // number of px between each legend item (x and/or y direction)
25812 itemGap: 5
25813};
25814
25815},{}],104:[function(_dereq_,module,exports){
25816/**
25817* Copyright 2012-2020, Plotly, Inc.
25818* All rights reserved.
25819*
25820* This source code is licensed under the MIT license found in the
25821* LICENSE file in the root directory of this source tree.
25822*/
25823
25824'use strict';
25825
25826var Registry = _dereq_('../../registry');
25827var Lib = _dereq_('../../lib');
25828var Template = _dereq_('../../plot_api/plot_template');
25829
25830var attributes = _dereq_('./attributes');
25831var basePlotLayoutAttributes = _dereq_('../../plots/layout_attributes');
25832var helpers = _dereq_('./helpers');
25833
25834
25835module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
25836 var containerIn = layoutIn.legend || {};
25837
25838 var legendTraceCount = 0;
25839 var legendReallyHasATrace = false;
25840 var defaultOrder = 'normal';
25841
25842 for(var i = 0; i < fullData.length; i++) {
25843 var trace = fullData[i];
25844
25845 if(!trace.visible) continue;
25846
25847 // Note that we explicitly count any trace that is either shown or
25848 // *would* be shown by default, toward the two traces you need to
25849 // ensure the legend is shown by default, because this can still help
25850 // disambiguate.
25851 if(trace.showlegend || (
25852 trace._dfltShowLegend && !(
25853 trace._module &&
25854 trace._module.attributes &&
25855 trace._module.attributes.showlegend &&
25856 trace._module.attributes.showlegend.dflt === false
25857 )
25858 )) {
25859 legendTraceCount++;
25860 if(trace.showlegend) {
25861 legendReallyHasATrace = true;
25862 // Always show the legend by default if there's a pie,
25863 // or if there's only one trace but it's explicitly shown
25864 if(Registry.traceIs(trace, 'pie-like') ||
25865 trace._input.showlegend === true
25866 ) {
25867 legendTraceCount++;
25868 }
25869 }
25870 }
25871
25872 if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') ||
25873 ['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) {
25874 defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ?
25875 'grouped+reversed' : 'reversed';
25876 }
25877
25878 if(trace.legendgroup !== undefined && trace.legendgroup !== '') {
25879 defaultOrder = helpers.isReversed({traceorder: defaultOrder}) ?
25880 'reversed+grouped' : 'grouped';
25881 }
25882 }
25883
25884 var showLegend = Lib.coerce(layoutIn, layoutOut,
25885 basePlotLayoutAttributes, 'showlegend',
25886 legendReallyHasATrace && legendTraceCount > 1);
25887
25888 if(showLegend === false && !containerIn.uirevision) return;
25889
25890 var containerOut = Template.newContainer(layoutOut, 'legend');
25891
25892 function coerce(attr, dflt) {
25893 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
25894 }
25895
25896 coerce('uirevision', layoutOut.uirevision);
25897
25898 if(showLegend === false) return;
25899
25900 coerce('bgcolor', layoutOut.paper_bgcolor);
25901 coerce('bordercolor');
25902 coerce('borderwidth');
25903 Lib.coerceFont(coerce, 'font', layoutOut.font);
25904
25905 var orientation = coerce('orientation');
25906 var defaultX, defaultY, defaultYAnchor;
25907
25908 if(orientation === 'h') {
25909 defaultX = 0;
25910
25911 if(Registry.getComponentMethod('rangeslider', 'isVisible')(layoutIn.xaxis)) {
25912 defaultY = 1.1;
25913 defaultYAnchor = 'bottom';
25914 } else {
25915 // maybe use y=1.1 / yanchor=bottom as above
25916 // to avoid https://github.com/plotly/plotly.js/issues/1199
25917 // in v2
25918 defaultY = -0.1;
25919 defaultYAnchor = 'top';
25920 }
25921 } else {
25922 defaultX = 1.02;
25923 defaultY = 1;
25924 defaultYAnchor = 'auto';
25925 }
25926
25927 coerce('traceorder', defaultOrder);
25928 if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
25929
25930 coerce('itemsizing');
25931
25932 coerce('itemclick');
25933 coerce('itemdoubleclick');
25934
25935 coerce('x', defaultX);
25936 coerce('xanchor');
25937 coerce('y', defaultY);
25938 coerce('yanchor', defaultYAnchor);
25939 coerce('valign');
25940 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
25941
25942 var titleText = coerce('title.text');
25943 if(titleText) {
25944 coerce('title.side', orientation === 'h' ? 'left' : 'top');
25945 Lib.coerceFont(coerce, 'title.font', layoutOut.font);
25946 }
25947};
25948
25949},{"../../lib":178,"../../plot_api/plot_template":212,"../../plots/layout_attributes":254,"../../registry":269,"./attributes":102,"./helpers":108}],105:[function(_dereq_,module,exports){
25950/**
25951* Copyright 2012-2020, Plotly, Inc.
25952* All rights reserved.
25953*
25954* This source code is licensed under the MIT license found in the
25955* LICENSE file in the root directory of this source tree.
25956*/
25957
25958'use strict';
25959
25960var d3 = _dereq_('d3');
25961
25962var Lib = _dereq_('../../lib');
25963var Plots = _dereq_('../../plots/plots');
25964var Registry = _dereq_('../../registry');
25965var Events = _dereq_('../../lib/events');
25966var dragElement = _dereq_('../dragelement');
25967var Drawing = _dereq_('../drawing');
25968var Color = _dereq_('../color');
25969var svgTextUtils = _dereq_('../../lib/svg_text_utils');
25970var handleClick = _dereq_('./handle_click');
25971
25972var constants = _dereq_('./constants');
25973var alignmentConstants = _dereq_('../../constants/alignment');
25974var LINE_SPACING = alignmentConstants.LINE_SPACING;
25975var FROM_TL = alignmentConstants.FROM_TL;
25976var FROM_BR = alignmentConstants.FROM_BR;
25977
25978var getLegendData = _dereq_('./get_legend_data');
25979var style = _dereq_('./style');
25980var helpers = _dereq_('./helpers');
25981
25982module.exports = function draw(gd, opts) {
25983 var fullLayout = gd._fullLayout;
25984 var clipId = 'legend' + fullLayout._uid;
25985 var layer;
25986
25987 // Check whether this is the main legend (ie. called without any opts)
25988 if(!opts) {
25989 opts = fullLayout.legend || {};
25990 opts._main = true;
25991 layer = fullLayout._infolayer;
25992 } else {
25993 layer = opts.layer;
25994 clipId += '-hover';
25995 }
25996
25997 if(!layer) return;
25998
25999 if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
26000
26001 var legendData;
26002 if(opts._main) {
26003 if(!gd.calcdata) return;
26004 legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
26005 } else {
26006 if(!opts.entries) return;
26007 legendData = getLegendData(opts.entries, opts);
26008 }
26009
26010 var hiddenSlices = fullLayout.hiddenlabels || [];
26011
26012 if(opts._main && (!fullLayout.showlegend || !legendData.length)) {
26013 layer.selectAll('.legend').remove();
26014 fullLayout._topdefs.select('#' + clipId).remove();
26015 return Plots.autoMargin(gd, 'legend');
26016 }
26017
26018 var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) {
26019 if(opts._main) s.attr('pointer-events', 'all');
26020 });
26021
26022 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
26023 s.append('rect');
26024 });
26025
26026 var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
26027 s.attr('shape-rendering', 'crispEdges');
26028 });
26029 bg.call(Color.stroke, opts.bordercolor)
26030 .call(Color.fill, opts.bgcolor)
26031 .style('stroke-width', opts.borderwidth + 'px');
26032
26033 var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
26034
26035 var title = opts.title;
26036 opts._titleWidth = 0;
26037 opts._titleHeight = 0;
26038 if(title.text) {
26039 var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext');
26040 titleEl.attr('text-anchor', 'start')
26041 .classed('user-select-none', true)
26042 .call(Drawing.font, title.font)
26043 .text(title.text);
26044
26045 textLayout(titleEl, scrollBox, gd, opts); // handle mathjax or multi-line text and compute title height
26046 }
26047
26048 var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) {
26049 s.attr(constants.scrollBarEnterAttrs)
26050 .call(Color.fill, constants.scrollBarColor);
26051 });
26052
26053 var groups = scrollBox.selectAll('g.groups').data(legendData);
26054 groups.enter().append('g').attr('class', 'groups');
26055 groups.exit().remove();
26056
26057 var traces = groups.selectAll('g.traces').data(Lib.identity);
26058 traces.enter().append('g').attr('class', 'traces');
26059 traces.exit().remove();
26060
26061 traces.style('opacity', function(d) {
26062 var trace = d[0].trace;
26063 if(Registry.traceIs(trace, 'pie-like')) {
26064 return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1;
26065 } else {
26066 return trace.visible === 'legendonly' ? 0.5 : 1;
26067 }
26068 })
26069 .each(function() { d3.select(this).call(drawTexts, gd, opts); })
26070 .call(style, gd, opts)
26071 .each(function() { if(opts._main) d3.select(this).call(setupTraceToggle, gd); });
26072
26073 Lib.syncOrAsync([
26074 Plots.previousPromises,
26075 function() { return computeLegendDimensions(gd, groups, traces, opts); },
26076 function() {
26077 // IF expandMargin return a Promise (which is truthy),
26078 // we're under a doAutoMargin redraw, so we don't have to
26079 // draw the remaining pieces below
26080 if(opts._main && expandMargin(gd)) return;
26081
26082 var gs = fullLayout._size;
26083 var bw = opts.borderwidth;
26084
26085 var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
26086 var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
26087
26088 if(opts._main && fullLayout.margin.autoexpand) {
26089 var lx0 = lx;
26090 var ly0 = ly;
26091
26092 lx = Lib.constrain(lx, 0, fullLayout.width - opts._width);
26093 ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight);
26094
26095 if(lx !== lx0) {
26096 Lib.log('Constrain legend.x to make legend fit inside graph');
26097 }
26098 if(ly !== ly0) {
26099 Lib.log('Constrain legend.y to make legend fit inside graph');
26100 }
26101 }
26102
26103 // Set size and position of all the elements that make up a legend:
26104 // legend, background and border, scroll box and scroll bar as well as title
26105 if(opts._main) Drawing.setTranslate(legend, lx, ly);
26106
26107 // to be safe, remove previous listeners
26108 scrollBar.on('.drag', null);
26109 legend.on('wheel', null);
26110
26111 if(!opts._main || opts._height <= opts._maxHeight || gd._context.staticPlot) {
26112 // if scrollbar should not be shown.
26113 var height = opts._effHeight;
26114
26115 // if not the main legend, let it be its full size
26116 if(!opts._main) height = opts._height;
26117
26118 bg.attr({
26119 width: opts._width - bw,
26120 height: height - bw,
26121 x: bw / 2,
26122 y: bw / 2
26123 });
26124
26125 Drawing.setTranslate(scrollBox, 0, 0);
26126
26127 clipPath.select('rect').attr({
26128 width: opts._width - 2 * bw,
26129 height: height - 2 * bw,
26130 x: bw,
26131 y: bw
26132 });
26133
26134 Drawing.setClipUrl(scrollBox, clipId, gd);
26135
26136 Drawing.setRect(scrollBar, 0, 0, 0, 0);
26137 delete opts._scrollY;
26138 } else {
26139 var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
26140 opts._effHeight * opts._effHeight / opts._height);
26141 var scrollBarYMax = opts._effHeight -
26142 scrollBarHeight -
26143 2 * constants.scrollBarMargin;
26144 var scrollBoxYMax = opts._height - opts._effHeight;
26145 var scrollRatio = scrollBarYMax / scrollBoxYMax;
26146
26147 var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax);
26148
26149 // increase the background and clip-path width
26150 // by the scrollbar width and margin
26151 bg.attr({
26152 width: opts._width -
26153 2 * bw +
26154 constants.scrollBarWidth +
26155 constants.scrollBarMargin,
26156 height: opts._effHeight - bw,
26157 x: bw / 2,
26158 y: bw / 2
26159 });
26160
26161 clipPath.select('rect').attr({
26162 width: opts._width -
26163 2 * bw +
26164 constants.scrollBarWidth +
26165 constants.scrollBarMargin,
26166 height: opts._effHeight - 2 * bw,
26167 x: bw,
26168 y: bw + scrollBoxY
26169 });
26170
26171 Drawing.setClipUrl(scrollBox, clipId, gd);
26172
26173 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
26174
26175 // scroll legend by mousewheel or touchpad swipe up/down
26176 legend.on('wheel', function() {
26177 scrollBoxY = Lib.constrain(
26178 opts._scrollY +
26179 ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax),
26180 0, scrollBoxYMax);
26181 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
26182 if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) {
26183 d3.event.preventDefault();
26184 }
26185 });
26186
26187 var eventY0, eventY1, scrollBoxY0;
26188
26189 var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) {
26190 var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0;
26191 return Lib.constrain(y, 0, scrollBoxYMax);
26192 };
26193
26194 var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) {
26195 var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0;
26196 return Lib.constrain(y, 0, scrollBoxYMax);
26197 };
26198
26199 // scroll legend by dragging scrollBAR
26200 var scrollBarDrag = d3.behavior.drag()
26201 .on('dragstart', function() {
26202 var e = d3.event.sourceEvent;
26203 if(e.type === 'touchstart') {
26204 eventY0 = e.changedTouches[0].clientY;
26205 } else {
26206 eventY0 = e.clientY;
26207 }
26208 scrollBoxY0 = scrollBoxY;
26209 })
26210 .on('drag', function() {
26211 var e = d3.event.sourceEvent;
26212 if(e.buttons === 2 || e.ctrlKey) return;
26213 if(e.type === 'touchmove') {
26214 eventY1 = e.changedTouches[0].clientY;
26215 } else {
26216 eventY1 = e.clientY;
26217 }
26218 scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1);
26219 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
26220 });
26221 scrollBar.call(scrollBarDrag);
26222
26223 // scroll legend by touch-dragging scrollBOX
26224 var scrollBoxTouchDrag = d3.behavior.drag()
26225 .on('dragstart', function() {
26226 var e = d3.event.sourceEvent;
26227 if(e.type === 'touchstart') {
26228 eventY0 = e.changedTouches[0].clientY;
26229 scrollBoxY0 = scrollBoxY;
26230 }
26231 })
26232 .on('drag', function() {
26233 var e = d3.event.sourceEvent;
26234 if(e.type === 'touchmove') {
26235 eventY1 = e.changedTouches[0].clientY;
26236 scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1);
26237 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
26238 }
26239 });
26240 scrollBox.call(scrollBoxTouchDrag);
26241 }
26242
26243 function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
26244 opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
26245 Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
26246
26247 Drawing.setRect(
26248 scrollBar,
26249 opts._width,
26250 constants.scrollBarMargin + scrollBoxY * scrollRatio,
26251 constants.scrollBarWidth,
26252 scrollBarHeight
26253 );
26254 clipPath.select('rect').attr('y', bw + scrollBoxY);
26255 }
26256
26257 if(gd._context.edits.legendPosition) {
26258 var xf, yf, x0, y0;
26259
26260 legend.classed('cursor-move', true);
26261
26262 dragElement.init({
26263 element: legend.node(),
26264 gd: gd,
26265 prepFn: function() {
26266 var transform = Drawing.getTranslate(legend);
26267 x0 = transform.x;
26268 y0 = transform.y;
26269 },
26270 moveFn: function(dx, dy) {
26271 var newX = x0 + dx;
26272 var newY = y0 + dy;
26273
26274 Drawing.setTranslate(legend, newX, newY);
26275
26276 xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
26277 yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
26278 },
26279 doneFn: function() {
26280 if(xf !== undefined && yf !== undefined) {
26281 Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf});
26282 }
26283 },
26284 clickFn: function(numClicks, e) {
26285 var clickedTrace = layer.selectAll('g.traces').filter(function() {
26286 var bbox = this.getBoundingClientRect();
26287 return (
26288 e.clientX >= bbox.left && e.clientX <= bbox.right &&
26289 e.clientY >= bbox.top && e.clientY <= bbox.bottom
26290 );
26291 });
26292 if(clickedTrace.size() > 0) {
26293 clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e);
26294 }
26295 }
26296 });
26297 }
26298 }], gd);
26299};
26300
26301function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
26302 var trace = legendItem.data()[0][0].trace;
26303 var evtData = {
26304 event: evt,
26305 node: legendItem.node(),
26306 curveNumber: trace.index,
26307 expandedIndex: trace._expandedIndex,
26308 data: gd.data,
26309 layout: gd.layout,
26310 frames: gd._transitionData._frames,
26311 config: gd._context,
26312 fullData: gd._fullData,
26313 fullLayout: gd._fullLayout
26314 };
26315
26316 if(trace._group) {
26317 evtData.group = trace._group;
26318 }
26319 if(Registry.traceIs(trace, 'pie-like')) {
26320 evtData.label = legendItem.datum()[0].label;
26321 }
26322
26323 var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData);
26324 if(clickVal === false) return;
26325
26326 if(numClicks === 1) {
26327 legend._clickTimeout = setTimeout(function() {
26328 handleClick(legendItem, gd, numClicks);
26329 }, gd._context.doubleClickDelay);
26330 } else if(numClicks === 2) {
26331 if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
26332 gd._legendMouseDownTime = 0;
26333
26334 var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
26335 if(dblClickVal !== false) handleClick(legendItem, gd, numClicks);
26336 }
26337}
26338
26339function drawTexts(g, gd, opts) {
26340 var legendItem = g.data()[0][0];
26341 var trace = legendItem.trace;
26342 var isPieLike = Registry.traceIs(trace, 'pie-like');
26343 var traceIndex = trace.index;
26344 var isEditable = opts._main && gd._context.edits.legendText && !isPieLike;
26345 var maxNameLength = opts._maxNameLength;
26346
26347 var name;
26348 if(!opts.entries) {
26349 name = isPieLike ? legendItem.label : trace.name;
26350 if(trace._meta) {
26351 name = Lib.templateString(name, trace._meta);
26352 }
26353 } else {
26354 name = legendItem.text;
26355 }
26356
26357 var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
26358
26359 textEl.attr('text-anchor', 'start')
26360 .classed('user-select-none', true)
26361 .call(Drawing.font, opts.font)
26362 .text(isEditable ? ensureLength(name, maxNameLength) : name);
26363
26364 svgTextUtils.positionText(textEl, constants.textGap, 0);
26365
26366 if(isEditable) {
26367 textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
26368 .call(textLayout, g, gd, opts)
26369 .on('edit', function(newName) {
26370 this.text(ensureLength(newName, maxNameLength))
26371 .call(textLayout, g, gd, opts);
26372
26373 var fullInput = legendItem.trace._fullInput || {};
26374 var update = {};
26375
26376 if(Registry.hasTransform(fullInput, 'groupby')) {
26377 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
26378 var index = groupbyIndices[groupbyIndices.length - 1];
26379
26380 var kcont = Lib.keyedContainer(fullInput, 'transforms[' + index + '].styles', 'target', 'value.name');
26381
26382 kcont.set(legendItem.trace._group, newName);
26383
26384 update = kcont.constructUpdate();
26385 } else {
26386 update.name = newName;
26387 }
26388
26389 return Registry.call('_guiRestyle', gd, update, traceIndex);
26390 });
26391 } else {
26392 textLayout(textEl, g, gd, opts);
26393 }
26394}
26395
26396/*
26397 * Make sure we have a reasonably clickable region.
26398 * If this string is missing or very short, pad it with spaces out to at least
26399 * 4 characters, up to the max length of other labels, on the assumption that
26400 * most characters are wider than spaces so a string of spaces will usually be
26401 * no wider than the real labels.
26402 */
26403function ensureLength(str, maxLength) {
26404 var targetLength = Math.max(4, maxLength);
26405 if(str && str.trim().length >= targetLength / 2) return str;
26406 str = str || '';
26407 for(var i = targetLength - str.length; i > 0; i--) str += ' ';
26408 return str;
26409}
26410
26411function setupTraceToggle(g, gd) {
26412 var doubleClickDelay = gd._context.doubleClickDelay;
26413 var newMouseDownTime;
26414 var numClicks = 1;
26415
26416 var traceToggle = Lib.ensureSingle(g, 'rect', 'legendtoggle', function(s) {
26417 s.style('cursor', 'pointer')
26418 .attr('pointer-events', 'all')
26419 .call(Color.fill, 'rgba(0,0,0,0)');
26420 });
26421
26422 traceToggle.on('mousedown', function() {
26423 newMouseDownTime = (new Date()).getTime();
26424 if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) {
26425 // in a click train
26426 numClicks += 1;
26427 } else {
26428 // new click train
26429 numClicks = 1;
26430 gd._legendMouseDownTime = newMouseDownTime;
26431 }
26432 });
26433 traceToggle.on('mouseup', function() {
26434 if(gd._dragged || gd._editing) return;
26435 var legend = gd._fullLayout.legend;
26436
26437 if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) {
26438 numClicks = Math.max(numClicks - 1, 1);
26439 }
26440
26441 clickOrDoubleClick(gd, legend, g, numClicks, d3.event);
26442 });
26443}
26444
26445function textLayout(s, g, gd, opts) {
26446 if(!opts._main) s.attr('data-notex', true); // do not process MathJax if not main
26447 svgTextUtils.convertToTspans(s, gd, function() {
26448 computeTextDimensions(g, gd, opts);
26449 });
26450}
26451
26452function computeTextDimensions(g, gd, opts) {
26453 var legendItem = g.data()[0][0];
26454 if(opts._main && legendItem && !legendItem.trace.showlegend) {
26455 g.remove();
26456 return;
26457 }
26458
26459 var mathjaxGroup = g.select('g[class*=math-group]');
26460 var mathjaxNode = mathjaxGroup.node();
26461 if(!opts) opts = gd._fullLayout.legend;
26462 var bw = opts.borderwidth;
26463 var lineHeight = (legendItem ? opts : opts.title).font.size * LINE_SPACING;
26464 var height, width;
26465
26466 if(mathjaxNode) {
26467 var mathjaxBB = Drawing.bBox(mathjaxNode);
26468
26469 height = mathjaxBB.height;
26470 width = mathjaxBB.width;
26471
26472 if(legendItem) {
26473 Drawing.setTranslate(mathjaxGroup, 0, height * 0.25);
26474 } else { // case of title
26475 Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw);
26476 }
26477 } else {
26478 var textEl = g.select(legendItem ?
26479 '.legendtext' : '.legendtitletext'
26480 );
26481 var textLines = svgTextUtils.lineCount(textEl);
26482 var textNode = textEl.node();
26483
26484 height = lineHeight * textLines;
26485 width = textNode ? Drawing.bBox(textNode).width : 0;
26486
26487 // approximation to height offset to center the font
26488 // to avoid getBoundingClientRect
26489 var textY = lineHeight * ((textLines - 1) / 2 - 0.3);
26490 if(legendItem) {
26491 svgTextUtils.positionText(textEl, constants.textGap, -textY);
26492 } else { // case of title
26493 svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw);
26494 }
26495 }
26496
26497 if(legendItem) {
26498 legendItem.lineHeight = lineHeight;
26499 legendItem.height = Math.max(height, 16) + 3;
26500 legendItem.width = width;
26501 } else { // case of title
26502 opts._titleWidth = width;
26503 opts._titleHeight = height;
26504 }
26505}
26506
26507function getTitleSize(opts) {
26508 var w = 0;
26509 var h = 0;
26510
26511 var side = opts.title.side;
26512 if(side) {
26513 if(side.indexOf('left') !== -1) {
26514 w = opts._titleWidth;
26515 }
26516 if(side.indexOf('top') !== -1) {
26517 h = opts._titleHeight;
26518 }
26519 }
26520
26521 return [w, h];
26522}
26523
26524/*
26525 * Computes in fullLayout.legend:
26526 *
26527 * - _height: legend height including items past scrollbox height
26528 * - _maxHeight: maximum legend height before scrollbox is required
26529 * - _effHeight: legend height w/ or w/o scrollbox
26530 *
26531 * - _width: legend width
26532 * - _maxWidth (for orientation:h only): maximum width before starting new row
26533 */
26534function computeLegendDimensions(gd, groups, traces, opts) {
26535 var fullLayout = gd._fullLayout;
26536 if(!opts) opts = fullLayout.legend;
26537 var gs = fullLayout._size;
26538
26539 var isVertical = helpers.isVertical(opts);
26540 var isGrouped = helpers.isGrouped(opts);
26541
26542 var bw = opts.borderwidth;
26543 var bw2 = 2 * bw;
26544 var textGap = constants.textGap;
26545 var itemGap = constants.itemGap;
26546 var endPad = 2 * (bw + itemGap);
26547
26548 var yanchor = getYanchor(opts);
26549 var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top');
26550 var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom');
26551
26552 // - if below/above plot area, give it the maximum potential margin-push value
26553 // - otherwise, extend the height of the plot area
26554 opts._maxHeight = Math.max(
26555 (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h,
26556 30
26557 );
26558
26559 var toggleRectWidth = 0;
26560 opts._width = 0;
26561 opts._height = 0;
26562 var titleSize = getTitleSize(opts);
26563
26564 if(isVertical) {
26565 traces.each(function(d) {
26566 var h = d[0].height;
26567 Drawing.setTranslate(this,
26568 bw + titleSize[0],
26569 bw + titleSize[1] + opts._height + h / 2 + itemGap
26570 );
26571 opts._height += h;
26572 opts._width = Math.max(opts._width, d[0].width);
26573 });
26574
26575 toggleRectWidth = textGap + opts._width;
26576 opts._width += itemGap + textGap + bw2;
26577 opts._height += endPad;
26578
26579 if(isGrouped) {
26580 groups.each(function(d, i) {
26581 Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
26582 });
26583 opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap;
26584 }
26585 } else {
26586 var xanchor = getXanchor(opts);
26587 var isLeftOfPlotArea = opts.x < 0 || (opts.x === 0 && xanchor === 'right');
26588 var isRightOfPlotArea = opts.x > 1 || (opts.x === 1 && xanchor === 'left');
26589 var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea;
26590 var hw = fullLayout.width / 2;
26591
26592 // - if placed within x-margins, extend the width of the plot area
26593 // - else if below/above plot area and anchored in the margin, extend to opposite margin,
26594 // - otherwise give it the maximum potential margin-push value
26595 opts._maxWidth = Math.max(
26596 isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) :
26597 isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) :
26598 gs.w,
26599 2 * textGap);
26600 var maxItemWidth = 0;
26601 var combinedItemWidth = 0;
26602 traces.each(function(d) {
26603 var w = d[0].width + textGap;
26604 maxItemWidth = Math.max(maxItemWidth, w);
26605 combinedItemWidth += w;
26606 });
26607
26608 toggleRectWidth = null;
26609 var maxRowWidth = 0;
26610
26611 if(isGrouped) {
26612 var maxGroupHeightInRow = 0;
26613 var groupOffsetX = 0;
26614 var groupOffsetY = 0;
26615 groups.each(function() {
26616 var maxWidthInGroup = 0;
26617 var offsetY = 0;
26618 d3.select(this).selectAll('g.traces').each(function(d) {
26619 var h = d[0].height;
26620 Drawing.setTranslate(this,
26621 titleSize[0],
26622 titleSize[1] + bw + itemGap + h / 2 + offsetY
26623 );
26624 offsetY += h;
26625 maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width);
26626 });
26627 maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY);
26628
26629 var next = maxWidthInGroup + itemGap;
26630
26631 if((next + bw + groupOffsetX) > opts._maxWidth) {
26632 maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
26633 groupOffsetX = 0;
26634 groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap;
26635 maxGroupHeightInRow = offsetY;
26636 }
26637
26638 Drawing.setTranslate(this, groupOffsetX, groupOffsetY);
26639
26640 groupOffsetX += next;
26641 });
26642
26643 opts._width = Math.max(maxRowWidth, groupOffsetX) + bw;
26644 opts._height = groupOffsetY + maxGroupHeightInRow + endPad;
26645 } else {
26646 var nTraces = traces.size();
26647 var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth;
26648
26649 var maxItemHeightInRow = 0;
26650 var offsetX = 0;
26651 var offsetY = 0;
26652 var rowWidth = 0;
26653 traces.each(function(d) {
26654 var h = d[0].height;
26655 var w = textGap + d[0].width;
26656 var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
26657
26658 if((next + bw + offsetX) > opts._maxWidth) {
26659 maxRowWidth = Math.max(maxRowWidth, rowWidth);
26660 offsetX = 0;
26661 offsetY += maxItemHeightInRow;
26662 opts._height += maxItemHeightInRow;
26663 maxItemHeightInRow = 0;
26664 }
26665
26666 Drawing.setTranslate(this,
26667 titleSize[0] + bw + offsetX,
26668 titleSize[1] + bw + offsetY + h / 2 + itemGap
26669 );
26670
26671 rowWidth = offsetX + w + itemGap;
26672 offsetX += next;
26673 maxItemHeightInRow = Math.max(maxItemHeightInRow, h);
26674 });
26675
26676 if(oneRowLegend) {
26677 opts._width = offsetX + bw2;
26678 opts._height = maxItemHeightInRow + endPad;
26679 } else {
26680 opts._width = Math.max(maxRowWidth, rowWidth) + bw2;
26681 opts._height += maxItemHeightInRow + endPad;
26682 }
26683 }
26684 }
26685
26686 opts._width = Math.ceil(
26687 Math.max(
26688 opts._width + titleSize[0],
26689 opts._titleWidth + 2 * (bw + constants.titlePad)
26690 )
26691 );
26692
26693 opts._height = Math.ceil(
26694 Math.max(
26695 opts._height + titleSize[1],
26696 opts._titleHeight + 2 * (bw + constants.itemGap)
26697 )
26698 );
26699
26700 opts._effHeight = Math.min(opts._height, opts._maxHeight);
26701
26702 var edits = gd._context.edits;
26703 var isEditable = edits.legendText || edits.legendPosition;
26704 traces.each(function(d) {
26705 var traceToggle = d3.select(this).select('.legendtoggle');
26706 var h = d[0].height;
26707 var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width));
26708 if(!isVertical) w += itemGap / 2;
26709 Drawing.setRect(traceToggle, 0, -h / 2, w, h);
26710 });
26711}
26712
26713function expandMargin(gd) {
26714 var fullLayout = gd._fullLayout;
26715 var opts = fullLayout.legend;
26716 var xanchor = getXanchor(opts);
26717 var yanchor = getYanchor(opts);
26718
26719 return Plots.autoMargin(gd, 'legend', {
26720 x: opts.x,
26721 y: opts.y,
26722 l: opts._width * (FROM_TL[xanchor]),
26723 r: opts._width * (FROM_BR[xanchor]),
26724 b: opts._effHeight * (FROM_BR[yanchor]),
26725 t: opts._effHeight * (FROM_TL[yanchor])
26726 });
26727}
26728
26729function getXanchor(opts) {
26730 return Lib.isRightAnchor(opts) ? 'right' :
26731 Lib.isCenterAnchor(opts) ? 'center' :
26732 'left';
26733}
26734
26735function getYanchor(opts) {
26736 return Lib.isBottomAnchor(opts) ? 'bottom' :
26737 Lib.isMiddleAnchor(opts) ? 'middle' :
26738 'top';
26739}
26740
26741},{"../../constants/alignment":154,"../../lib":178,"../../lib/events":172,"../../lib/svg_text_utils":199,"../../plots/plots":256,"../../registry":269,"../color":52,"../dragelement":71,"../drawing":74,"./constants":103,"./get_legend_data":106,"./handle_click":107,"./helpers":108,"./style":110,"d3":16}],106:[function(_dereq_,module,exports){
26742/**
26743* Copyright 2012-2020, Plotly, Inc.
26744* All rights reserved.
26745*
26746* This source code is licensed under the MIT license found in the
26747* LICENSE file in the root directory of this source tree.
26748*/
26749
26750'use strict';
26751
26752var Registry = _dereq_('../../registry');
26753var helpers = _dereq_('./helpers');
26754
26755module.exports = function getLegendData(calcdata, opts) {
26756 var lgroupToTraces = {};
26757 var lgroups = [];
26758 var hasOneNonBlankGroup = false;
26759 var slicesShown = {};
26760 var lgroupi = 0;
26761 var maxNameLength = 0;
26762 var i, j;
26763 var main = opts._main;
26764
26765 function addOneItem(legendGroup, legendItem) {
26766 // each '' legend group is treated as a separate group
26767 if(legendGroup === '' || !helpers.isGrouped(opts)) {
26768 // TODO: check this against fullData legendgroups?
26769 var uniqueGroup = '~~i' + lgroupi;
26770 lgroups.push(uniqueGroup);
26771 lgroupToTraces[uniqueGroup] = [[legendItem]];
26772 lgroupi++;
26773 } else if(lgroups.indexOf(legendGroup) === -1) {
26774 lgroups.push(legendGroup);
26775 hasOneNonBlankGroup = true;
26776 lgroupToTraces[legendGroup] = [[legendItem]];
26777 } else {
26778 lgroupToTraces[legendGroup].push([legendItem]);
26779 }
26780 }
26781
26782 // build an { legendgroup: [cd0, cd0], ... } object
26783 for(i = 0; i < calcdata.length; i++) {
26784 var cd = calcdata[i];
26785 var cd0 = cd[0];
26786 var trace = cd0.trace;
26787 var lgroup = trace.legendgroup;
26788
26789 if(main && (!trace.visible || !trace.showlegend)) continue;
26790
26791 if(Registry.traceIs(trace, 'pie-like')) {
26792 if(!slicesShown[lgroup]) slicesShown[lgroup] = {};
26793
26794 for(j = 0; j < cd.length; j++) {
26795 var labelj = cd[j].label;
26796
26797 if(!slicesShown[lgroup][labelj]) {
26798 addOneItem(lgroup, {
26799 label: labelj,
26800 color: cd[j].color,
26801 i: cd[j].i,
26802 trace: trace,
26803 pts: cd[j].pts
26804 });
26805
26806 slicesShown[lgroup][labelj] = true;
26807 maxNameLength = Math.max(maxNameLength, (labelj || '').length);
26808 }
26809 }
26810 } else {
26811 addOneItem(lgroup, cd0);
26812 maxNameLength = Math.max(maxNameLength, (trace.name || '').length);
26813 }
26814 }
26815
26816 // won't draw a legend in this case
26817 if(!lgroups.length) return [];
26818
26819 // rearrange lgroupToTraces into a d3-friendly array of arrays
26820 var lgroupsLength = lgroups.length;
26821 var ltraces;
26822 var legendData;
26823
26824 if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
26825 legendData = new Array(lgroupsLength);
26826
26827 for(i = 0; i < lgroupsLength; i++) {
26828 ltraces = lgroupToTraces[lgroups[i]];
26829 legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
26830 }
26831 } else {
26832 // collapse all groups into one if all groups are blank
26833 legendData = [new Array(lgroupsLength)];
26834
26835 for(i = 0; i < lgroupsLength; i++) {
26836 ltraces = lgroupToTraces[lgroups[i]][0];
26837 legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
26838 }
26839 lgroupsLength = 1;
26840 }
26841
26842 // number of legend groups - needed in legend/draw.js
26843 opts._lgroupsLength = lgroupsLength;
26844 // maximum name/label length - needed in legend/draw.js
26845 opts._maxNameLength = maxNameLength;
26846
26847 return legendData;
26848};
26849
26850},{"../../registry":269,"./helpers":108}],107:[function(_dereq_,module,exports){
26851/**
26852* Copyright 2012-2020, Plotly, Inc.
26853* All rights reserved.
26854*
26855* This source code is licensed under the MIT license found in the
26856* LICENSE file in the root directory of this source tree.
26857*/
26858
26859'use strict';
26860
26861var Lib = _dereq_('../../lib');
26862var Registry = _dereq_('../../registry');
26863
26864var SHOWISOLATETIP = true;
26865
26866module.exports = function handleClick(g, gd, numClicks) {
26867 var fullLayout = gd._fullLayout;
26868
26869 if(gd._dragged || gd._editing) return;
26870
26871 var itemClick = fullLayout.legend.itemclick;
26872 var itemDoubleClick = fullLayout.legend.itemdoubleclick;
26873
26874 if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'toggleothers' &&
26875 SHOWISOLATETIP && gd.data && gd._context.showTips
26876 ) {
26877 Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long');
26878 SHOWISOLATETIP = false;
26879 } else {
26880 SHOWISOLATETIP = false;
26881 }
26882
26883 var mode;
26884 if(numClicks === 1) mode = itemClick;
26885 else if(numClicks === 2) mode = itemDoubleClick;
26886 if(!mode) return;
26887
26888 var hiddenSlices = fullLayout.hiddenlabels ?
26889 fullLayout.hiddenlabels.slice() :
26890 [];
26891
26892 var legendItem = g.data()[0][0];
26893 var fullData = gd._fullData;
26894 var fullTrace = legendItem.trace;
26895 var legendgroup = fullTrace.legendgroup;
26896
26897 var i, j, kcont, key, keys, val;
26898 var attrUpdate = {};
26899 var attrIndices = [];
26900 var carrs = [];
26901 var carrIdx = [];
26902
26903 function insertUpdate(traceIndex, key, value) {
26904 var attrIndex = attrIndices.indexOf(traceIndex);
26905 var valueArray = attrUpdate[key];
26906 if(!valueArray) {
26907 valueArray = attrUpdate[key] = [];
26908 }
26909
26910 if(attrIndices.indexOf(traceIndex) === -1) {
26911 attrIndices.push(traceIndex);
26912 attrIndex = attrIndices.length - 1;
26913 }
26914
26915 valueArray[attrIndex] = value;
26916
26917 return attrIndex;
26918 }
26919
26920 function setVisibility(fullTrace, visibility) {
26921 var fullInput = fullTrace._fullInput;
26922 if(Registry.hasTransform(fullInput, 'groupby')) {
26923 var kcont = carrs[fullInput.index];
26924 if(!kcont) {
26925 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
26926 var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1];
26927 kcont = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible');
26928 carrs[fullInput.index] = kcont;
26929 }
26930
26931 var curState = kcont.get(fullTrace._group);
26932
26933 // If not specified, assume visible. This happens if there are other style
26934 // properties set for a group but not the visibility. There are many similar
26935 // ways to do this (e.g. why not just `curState = fullTrace.visible`??? The
26936 // answer is: because it breaks other things like groupby trace names in
26937 // subtle ways.)
26938 if(curState === undefined) {
26939 curState = true;
26940 }
26941
26942 if(curState !== false) {
26943 // true -> legendonly. All others toggle to true:
26944 kcont.set(fullTrace._group, visibility);
26945 }
26946 carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true);
26947 } else {
26948 // false -> false (not possible since will not be visible in legend)
26949 // true -> legendonly
26950 // legendonly -> true
26951 var nextVisibility = fullInput.visible === false ? false : visibility;
26952
26953 insertUpdate(fullInput.index, 'visible', nextVisibility);
26954 }
26955 }
26956
26957 if(Registry.traceIs(fullTrace, 'pie-like')) {
26958 var thisLabel = legendItem.label;
26959 var thisLabelIndex = hiddenSlices.indexOf(thisLabel);
26960
26961 if(mode === 'toggle') {
26962 if(thisLabelIndex === -1) hiddenSlices.push(thisLabel);
26963 else hiddenSlices.splice(thisLabelIndex, 1);
26964 } else if(mode === 'toggleothers') {
26965 hiddenSlices = [];
26966 gd.calcdata[0].forEach(function(d) {
26967 if(thisLabel !== d.label) {
26968 hiddenSlices.push(d.label);
26969 }
26970 });
26971 if(gd._fullLayout.hiddenlabels && gd._fullLayout.hiddenlabels.length === hiddenSlices.length && thisLabelIndex === -1) {
26972 hiddenSlices = [];
26973 }
26974 }
26975
26976 Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices);
26977 } else {
26978 var hasLegendgroup = legendgroup && legendgroup.length;
26979 var traceIndicesInGroup = [];
26980 var tracei;
26981 if(hasLegendgroup) {
26982 for(i = 0; i < fullData.length; i++) {
26983 tracei = fullData[i];
26984 if(!tracei.visible) continue;
26985 if(tracei.legendgroup === legendgroup) {
26986 traceIndicesInGroup.push(i);
26987 }
26988 }
26989 }
26990
26991 if(mode === 'toggle') {
26992 var nextVisibility;
26993
26994 switch(fullTrace.visible) {
26995 case true:
26996 nextVisibility = 'legendonly';
26997 break;
26998 case false:
26999 nextVisibility = false;
27000 break;
27001 case 'legendonly':
27002 nextVisibility = true;
27003 break;
27004 }
27005
27006 if(hasLegendgroup) {
27007 for(i = 0; i < fullData.length; i++) {
27008 if(fullData[i].visible !== false && fullData[i].legendgroup === legendgroup) {
27009 setVisibility(fullData[i], nextVisibility);
27010 }
27011 }
27012 } else {
27013 setVisibility(fullTrace, nextVisibility);
27014 }
27015 } else if(mode === 'toggleothers') {
27016 // Compute the clicked index. expandedIndex does what we want for expanded traces
27017 // but also culls hidden traces. That means we have some work to do.
27018 var isClicked, isInGroup, notInLegend, otherState;
27019 var isIsolated = true;
27020 for(i = 0; i < fullData.length; i++) {
27021 isClicked = fullData[i] === fullTrace;
27022 notInLegend = fullData[i].showlegend !== true;
27023 if(isClicked || notInLegend) continue;
27024
27025 isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup);
27026
27027 if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
27028 isIsolated = false;
27029 break;
27030 }
27031 }
27032
27033 for(i = 0; i < fullData.length; i++) {
27034 // False is sticky; we don't change it.
27035 if(fullData[i].visible === false) continue;
27036
27037 if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
27038 continue;
27039 }
27040
27041 switch(fullTrace.visible) {
27042 case 'legendonly':
27043 setVisibility(fullData[i], true);
27044 break;
27045 case true:
27046 otherState = isIsolated ? true : 'legendonly';
27047 isClicked = fullData[i] === fullTrace;
27048 // N.B. consider traces that have a set legendgroup as toggleable
27049 notInLegend = (fullData[i].showlegend !== true && !fullData[i].legendgroup);
27050 isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup);
27051 setVisibility(fullData[i], (isInGroup || notInLegend) ? true : otherState);
27052 break;
27053 }
27054 }
27055 }
27056
27057 for(i = 0; i < carrs.length; i++) {
27058 kcont = carrs[i];
27059 if(!kcont) continue;
27060 var update = kcont.constructUpdate();
27061
27062 var updateKeys = Object.keys(update);
27063 for(j = 0; j < updateKeys.length; j++) {
27064 key = updateKeys[j];
27065 val = attrUpdate[key] = attrUpdate[key] || [];
27066 val[carrIdx[i]] = update[key];
27067 }
27068 }
27069
27070 // The length of the value arrays should be equal and any unspecified
27071 // values should be explicitly undefined for them to get properly culled
27072 // as updates and not accidentally reset to the default value. This fills
27073 // out sparse arrays with the required number of undefined values:
27074 keys = Object.keys(attrUpdate);
27075 for(i = 0; i < keys.length; i++) {
27076 key = keys[i];
27077 for(j = 0; j < attrIndices.length; j++) {
27078 // Use hasOwnPropety to protect against falsey values:
27079 if(!attrUpdate[key].hasOwnProperty(j)) {
27080 attrUpdate[key][j] = undefined;
27081 }
27082 }
27083 }
27084
27085 Registry.call('_guiRestyle', gd, attrUpdate, attrIndices);
27086 }
27087};
27088
27089},{"../../lib":178,"../../registry":269}],108:[function(_dereq_,module,exports){
27090/**
27091* Copyright 2012-2020, Plotly, Inc.
27092* All rights reserved.
27093*
27094* This source code is licensed under the MIT license found in the
27095* LICENSE file in the root directory of this source tree.
27096*/
27097
27098
27099'use strict';
27100
27101exports.isGrouped = function isGrouped(legendLayout) {
27102 return (legendLayout.traceorder || '').indexOf('grouped') !== -1;
27103};
27104
27105exports.isVertical = function isVertical(legendLayout) {
27106 return legendLayout.orientation !== 'h';
27107};
27108
27109exports.isReversed = function isReversed(legendLayout) {
27110 return (legendLayout.traceorder || '').indexOf('reversed') !== -1;
27111};
27112
27113},{}],109:[function(_dereq_,module,exports){
27114/**
27115* Copyright 2012-2020, Plotly, Inc.
27116* All rights reserved.
27117*
27118* This source code is licensed under the MIT license found in the
27119* LICENSE file in the root directory of this source tree.
27120*/
27121
27122
27123'use strict';
27124
27125
27126module.exports = {
27127 moduleType: 'component',
27128 name: 'legend',
27129
27130 layoutAttributes: _dereq_('./attributes'),
27131 supplyLayoutDefaults: _dereq_('./defaults'),
27132
27133 draw: _dereq_('./draw'),
27134 style: _dereq_('./style')
27135};
27136
27137},{"./attributes":102,"./defaults":104,"./draw":105,"./style":110}],110:[function(_dereq_,module,exports){
27138/**
27139* Copyright 2012-2020, Plotly, Inc.
27140* All rights reserved.
27141*
27142* This source code is licensed under the MIT license found in the
27143* LICENSE file in the root directory of this source tree.
27144*/
27145
27146'use strict';
27147
27148var d3 = _dereq_('d3');
27149
27150var Registry = _dereq_('../../registry');
27151var Lib = _dereq_('../../lib');
27152var Drawing = _dereq_('../drawing');
27153var Color = _dereq_('../color');
27154var extractOpts = _dereq_('../colorscale/helpers').extractOpts;
27155
27156var subTypes = _dereq_('../../traces/scatter/subtypes');
27157var stylePie = _dereq_('../../traces/pie/style_one');
27158var pieCastOption = _dereq_('../../traces/pie/helpers').castOption;
27159
27160var CST_MARKER_SIZE = 12;
27161var CST_LINE_WIDTH = 5;
27162var CST_MARKER_LINE_WIDTH = 2;
27163var MAX_LINE_WIDTH = 10;
27164var MAX_MARKER_LINE_WIDTH = 5;
27165
27166module.exports = function style(s, gd, legend) {
27167 var fullLayout = gd._fullLayout;
27168 if(!legend) legend = fullLayout.legend;
27169 var constantItemSizing = legend.itemsizing === 'constant';
27170
27171 var boundLineWidth = function(mlw, cont, max, cst) {
27172 var v;
27173 if(mlw + 1) {
27174 v = mlw;
27175 } else if(cont && cont.width > 0) {
27176 v = cont.width;
27177 } else {
27178 return 0;
27179 }
27180 return constantItemSizing ? cst : Math.min(v, max);
27181 };
27182
27183 s.each(function(d) {
27184 var traceGroup = d3.select(this);
27185
27186 var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
27187 layers.style('opacity', d[0].trace.opacity);
27188
27189 var valign = legend.valign;
27190 var lineHeight = d[0].lineHeight;
27191 var height = d[0].height;
27192
27193 if(valign === 'middle' || !lineHeight || !height) {
27194 layers.attr('transform', null);
27195 } else {
27196 var factor = {top: 1, bottom: -1}[valign];
27197 var markerOffsetY = factor * (0.5 * (lineHeight - height + 3));
27198 layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
27199 }
27200
27201 var fill = layers
27202 .selectAll('g.legendfill')
27203 .data([d]);
27204 fill.enter().append('g')
27205 .classed('legendfill', true);
27206
27207 var line = layers
27208 .selectAll('g.legendlines')
27209 .data([d]);
27210 line.enter().append('g')
27211 .classed('legendlines', true);
27212
27213 var symbol = layers
27214 .selectAll('g.legendsymbols')
27215 .data([d]);
27216 symbol.enter().append('g')
27217 .classed('legendsymbols', true);
27218
27219 symbol.selectAll('g.legendpoints')
27220 .data([d])
27221 .enter().append('g')
27222 .classed('legendpoints', true);
27223 })
27224 .each(styleSpatial)
27225 .each(styleWaterfalls)
27226 .each(styleFunnels)
27227 .each(styleBars)
27228 .each(styleBoxes)
27229 .each(styleFunnelareas)
27230 .each(stylePies)
27231 .each(styleLines)
27232 .each(stylePoints)
27233 .each(styleCandles)
27234 .each(styleOHLC);
27235
27236 function styleLines(d) {
27237 var d0 = d[0];
27238 var trace = d0.trace;
27239 var showFill = trace.visible && trace.fill && trace.fill !== 'none';
27240 var showLine = subTypes.hasLines(trace);
27241 var contours = trace.contours;
27242 var showGradientLine = false;
27243 var showGradientFill = false;
27244 var dMod, tMod;
27245
27246 var cOpts = extractOpts(trace);
27247 var colorscale = cOpts.colorscale;
27248 var reversescale = cOpts.reversescale;
27249
27250 var fillGradient = function(s) {
27251 if(s.size()) {
27252 var gradientID = 'legendfill-' + trace.uid;
27253 Drawing.gradient(s, gd, gradientID,
27254 getGradientDirection(reversescale),
27255 colorscale, 'fill');
27256 }
27257 };
27258
27259 var lineGradient = function(s) {
27260 if(s.size()) {
27261 var gradientID = 'legendline-' + trace.uid;
27262 Drawing.lineGroupStyle(s);
27263 Drawing.gradient(s, gd, gradientID,
27264 getGradientDirection(reversescale),
27265 colorscale, 'stroke');
27266 }
27267 };
27268
27269 if(contours) {
27270 var coloring = contours.coloring;
27271
27272 if(coloring === 'lines') {
27273 showGradientLine = true;
27274 } else {
27275 showLine = coloring === 'none' || coloring === 'heatmap' || contours.showlines;
27276 }
27277
27278 if(contours.type === 'constraint') {
27279 showFill = contours._operation !== '=';
27280 } else if(coloring === 'fill' || coloring === 'heatmap') {
27281 showGradientFill = true;
27282 }
27283 }
27284
27285 // with fill and no markers or text, move the line and fill up a bit
27286 // so it's more centered
27287 var markersOrText = subTypes.hasMarkers(trace) || subTypes.hasText(trace);
27288 var anyFill = showFill || showGradientFill;
27289 var anyLine = showLine || showGradientLine;
27290 var pathStart = (markersOrText || !anyFill) ? 'M5,0' :
27291 // with a line leave it slightly below center, to leave room for the
27292 // line thickness and because the line is usually more prominent
27293 anyLine ? 'M5,-2' : 'M5,-3';
27294
27295 var this3 = d3.select(this);
27296
27297 var fill = this3.select('.legendfill').selectAll('path')
27298 .data(showFill || showGradientFill ? [d] : []);
27299 fill.enter().append('path').classed('js-fill', true);
27300 fill.exit().remove();
27301 fill.attr('d', pathStart + 'h30v6h-30z')
27302 .call(showFill ? Drawing.fillGroupStyle : fillGradient);
27303
27304 if(showLine || showGradientLine) {
27305 var lw = boundLineWidth(undefined, trace.line, MAX_LINE_WIDTH, CST_LINE_WIDTH);
27306 tMod = Lib.minExtend(trace, {line: {width: lw}});
27307 dMod = [Lib.minExtend(d0, {trace: tMod})];
27308 }
27309
27310 var line = this3.select('.legendlines').selectAll('path')
27311 .data(showLine || showGradientLine ? [dMod] : []);
27312 line.enter().append('path').classed('js-line', true);
27313 line.exit().remove();
27314
27315 // this is ugly... but you can't apply a gradient to a perfectly
27316 // horizontal or vertical line. Presumably because then
27317 // the system doesn't know how to scale vertical variation, even
27318 // though there *is* no vertical variation in this case.
27319 // so add an invisibly small angle to the line
27320 // This issue (and workaround) exist across (Mac) Chrome, FF, and Safari
27321 line.attr('d', pathStart + (showGradientLine ? 'l30,0.0001' : 'h30'))
27322 .call(showLine ? Drawing.lineGroupStyle : lineGradient);
27323 }
27324
27325 function stylePoints(d) {
27326 var d0 = d[0];
27327 var trace = d0.trace;
27328 var showMarkers = subTypes.hasMarkers(trace);
27329 var showText = subTypes.hasText(trace);
27330 var showLines = subTypes.hasLines(trace);
27331 var dMod, tMod;
27332
27333 // 'scatter3d' don't use gd.calcdata,
27334 // use d0.trace to infer arrayOk attributes
27335
27336 function boundVal(attrIn, arrayToValFn, bounds, cst) {
27337 var valIn = Lib.nestedProperty(trace, attrIn).get();
27338 var valToBound = (Lib.isArrayOrTypedArray(valIn) && arrayToValFn) ?
27339 arrayToValFn(valIn) :
27340 valIn;
27341
27342 if(constantItemSizing && valToBound && cst !== undefined) {
27343 valToBound = cst;
27344 }
27345
27346 if(bounds) {
27347 if(valToBound < bounds[0]) return bounds[0];
27348 else if(valToBound > bounds[1]) return bounds[1];
27349 }
27350 return valToBound;
27351 }
27352
27353 function pickFirst(array) {
27354 if(d0._distinct && d0.index && array[d0.index]) return array[d0.index];
27355 return array[0];
27356 }
27357
27358 // constrain text, markers, etc so they'll fit on the legend
27359 if(showMarkers || showText || showLines) {
27360 var dEdit = {};
27361 var tEdit = {};
27362
27363 if(showMarkers) {
27364 dEdit.mc = boundVal('marker.color', pickFirst);
27365 dEdit.mx = boundVal('marker.symbol', pickFirst);
27366 dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
27367 dEdit.mlc = boundVal('marker.line.color', pickFirst);
27368 dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5], CST_MARKER_LINE_WIDTH);
27369 tEdit.marker = {
27370 sizeref: 1,
27371 sizemin: 1,
27372 sizemode: 'diameter'
27373 };
27374
27375 var ms = boundVal('marker.size', Lib.mean, [2, 16], CST_MARKER_SIZE);
27376 dEdit.ms = ms;
27377 tEdit.marker.size = ms;
27378 }
27379
27380 if(showLines) {
27381 tEdit.line = {
27382 width: boundVal('line.width', pickFirst, [0, 10], CST_LINE_WIDTH)
27383 };
27384 }
27385
27386 if(showText) {
27387 dEdit.tx = 'Aa';
27388 dEdit.tp = boundVal('textposition', pickFirst);
27389 dEdit.ts = 10;
27390 dEdit.tc = boundVal('textfont.color', pickFirst);
27391 dEdit.tf = boundVal('textfont.family', pickFirst);
27392 }
27393
27394 dMod = [Lib.minExtend(d0, dEdit)];
27395 tMod = Lib.minExtend(trace, tEdit);
27396
27397 // always show legend items in base state
27398 tMod.selectedpoints = null;
27399
27400 // never show texttemplate
27401 tMod.texttemplate = null;
27402 }
27403
27404 var ptgroup = d3.select(this).select('g.legendpoints');
27405
27406 var pts = ptgroup.selectAll('path.scatterpts')
27407 .data(showMarkers ? dMod : []);
27408 // make sure marker is on the bottom, in case it enters after text
27409 pts.enter().insert('path', ':first-child')
27410 .classed('scatterpts', true)
27411 .attr('transform', 'translate(20,0)');
27412 pts.exit().remove();
27413 pts.call(Drawing.pointStyle, tMod, gd);
27414
27415 // 'mrc' is set in pointStyle and used in textPointStyle:
27416 // constrain it here
27417 if(showMarkers) dMod[0].mrc = 3;
27418
27419 var txt = ptgroup.selectAll('g.pointtext')
27420 .data(showText ? dMod : []);
27421 txt.enter()
27422 .append('g').classed('pointtext', true)
27423 .append('text').attr('transform', 'translate(20,0)');
27424 txt.exit().remove();
27425 txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
27426 }
27427
27428 function styleWaterfalls(d) {
27429 var trace = d[0].trace;
27430 var isWaterfall = trace.type === 'waterfall';
27431
27432 if(d[0]._distinct && isWaterfall) {
27433 var cont = d[0].trace[d[0].dir].marker;
27434 d[0].mc = cont.color;
27435 d[0].mlw = cont.line.width;
27436 d[0].mlc = cont.line.color;
27437 return styleBarLike(d, this, 'waterfall');
27438 }
27439
27440 var ptsData = [];
27441 if(trace.visible && isWaterfall) {
27442 ptsData = d[0].hasTotals ?
27443 [['increasing', 'M-6,-6V6H0Z'], ['totals', 'M6,6H0L-6,-6H-0Z'], ['decreasing', 'M6,6V-6H0Z']] :
27444 [['increasing', 'M-6,-6V6H6Z'], ['decreasing', 'M6,6V-6H-6Z']];
27445 }
27446
27447 var pts = d3.select(this).select('g.legendpoints')
27448 .selectAll('path.legendwaterfall')
27449 .data(ptsData);
27450 pts.enter().append('path').classed('legendwaterfall', true)
27451 .attr('transform', 'translate(20,0)')
27452 .style('stroke-miterlimit', 1);
27453 pts.exit().remove();
27454
27455 pts.each(function(dd) {
27456 var pt = d3.select(this);
27457 var cont = trace[dd[0]].marker;
27458 var lw = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27459
27460 pt.attr('d', dd[1])
27461 .style('stroke-width', lw + 'px')
27462 .call(Color.fill, cont.color);
27463
27464 if(lw) {
27465 pt.call(Color.stroke, cont.line.color);
27466 }
27467 });
27468 }
27469
27470 function styleBars(d) {
27471 styleBarLike(d, this);
27472 }
27473
27474 function styleFunnels(d) {
27475 styleBarLike(d, this, 'funnel');
27476 }
27477
27478 function styleBarLike(d, lThis, desiredType) {
27479 var trace = d[0].trace;
27480 var marker = trace.marker || {};
27481 var markerLine = marker.line || {};
27482
27483 var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') :
27484 (trace.visible && trace.type === desiredType);
27485
27486 var barpath = d3.select(lThis).select('g.legendpoints')
27487 .selectAll('path.legend' + desiredType)
27488 .data(isVisible ? [d] : []);
27489 barpath.enter().append('path').classed('legend' + desiredType, true)
27490 .attr('d', 'M6,6H-6V-6H6Z')
27491 .attr('transform', 'translate(20,0)');
27492 barpath.exit().remove();
27493
27494 barpath.each(function(d) {
27495 var p = d3.select(this);
27496 var d0 = d[0];
27497 var w = boundLineWidth(d0.mlw, marker.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27498
27499 p.style('stroke-width', w + 'px')
27500 .call(Color.fill, d0.mc || marker.color);
27501
27502 if(w) Color.stroke(p, d0.mlc || markerLine.color);
27503 });
27504 }
27505
27506 function styleBoxes(d) {
27507 var trace = d[0].trace;
27508
27509 var pts = d3.select(this).select('g.legendpoints')
27510 .selectAll('path.legendbox')
27511 .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []);
27512 pts.enter().append('path').classed('legendbox', true)
27513 // if we want the median bar, prepend M6,0H-6
27514 .attr('d', 'M6,6H-6V-6H6Z')
27515 .attr('transform', 'translate(20,0)');
27516 pts.exit().remove();
27517
27518 pts.each(function() {
27519 var p = d3.select(this);
27520
27521 if((trace.boxpoints === 'all' || trace.points === 'all') &&
27522 Color.opacity(trace.fillcolor) === 0 && Color.opacity((trace.line || {}).color) === 0
27523 ) {
27524 var tMod = Lib.minExtend(trace, {
27525 marker: {
27526 size: constantItemSizing ? CST_MARKER_SIZE : Lib.constrain(trace.marker.size, 2, 16),
27527 sizeref: 1,
27528 sizemin: 1,
27529 sizemode: 'diameter'
27530 }
27531 });
27532 pts.call(Drawing.pointStyle, tMod, gd);
27533 } else {
27534 var w = boundLineWidth(undefined, trace.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27535
27536 p.style('stroke-width', w + 'px')
27537 .call(Color.fill, trace.fillcolor);
27538
27539 if(w) Color.stroke(p, trace.line.color);
27540 }
27541 });
27542 }
27543
27544 function styleCandles(d) {
27545 var trace = d[0].trace;
27546
27547 var pts = d3.select(this).select('g.legendpoints')
27548 .selectAll('path.legendcandle')
27549 .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []);
27550 pts.enter().append('path').classed('legendcandle', true)
27551 .attr('d', function(_, i) {
27552 if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
27553 return 'M15,0H8M8,-6V6H-8Z'; // decreasing
27554 })
27555 .attr('transform', 'translate(20,0)')
27556 .style('stroke-miterlimit', 1);
27557 pts.exit().remove();
27558
27559 pts.each(function(_, i) {
27560 var p = d3.select(this);
27561 var cont = trace[i ? 'increasing' : 'decreasing'];
27562 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27563
27564 p.style('stroke-width', w + 'px')
27565 .call(Color.fill, cont.fillcolor);
27566
27567 if(w) Color.stroke(p, cont.line.color);
27568 });
27569 }
27570
27571 function styleOHLC(d) {
27572 var trace = d[0].trace;
27573
27574 var pts = d3.select(this).select('g.legendpoints')
27575 .selectAll('path.legendohlc')
27576 .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []);
27577 pts.enter().append('path').classed('legendohlc', true)
27578 .attr('d', function(_, i) {
27579 if(i) return 'M-15,0H0M-8,-6V0'; // increasing
27580 return 'M15,0H0M8,6V0'; // decreasing
27581 })
27582 .attr('transform', 'translate(20,0)')
27583 .style('stroke-miterlimit', 1);
27584 pts.exit().remove();
27585
27586 pts.each(function(_, i) {
27587 var p = d3.select(this);
27588 var cont = trace[i ? 'increasing' : 'decreasing'];
27589 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27590
27591 p.style('fill', 'none')
27592 .call(Drawing.dashLine, cont.line.dash, w);
27593
27594 if(w) Color.stroke(p, cont.line.color);
27595 });
27596 }
27597
27598 function stylePies(d) {
27599 stylePieLike(d, this, 'pie');
27600 }
27601
27602 function styleFunnelareas(d) {
27603 stylePieLike(d, this, 'funnelarea');
27604 }
27605
27606 function stylePieLike(d, lThis, desiredType) {
27607 var d0 = d[0];
27608 var trace = d0.trace;
27609
27610 var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) :
27611 (trace.visible && trace.type === desiredType);
27612
27613 var pts = d3.select(lThis).select('g.legendpoints')
27614 .selectAll('path.legend' + desiredType)
27615 .data(isVisible ? [d] : []);
27616 pts.enter().append('path').classed('legend' + desiredType, true)
27617 .attr('d', 'M6,6H-6V-6H6Z')
27618 .attr('transform', 'translate(20,0)');
27619 pts.exit().remove();
27620
27621 if(pts.size()) {
27622 var cont = (trace.marker || {}).line;
27623 var lw = boundLineWidth(pieCastOption(cont.width, d0.pts), cont, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
27624
27625 var tMod = Lib.minExtend(trace, {marker: {line: {width: lw}}});
27626 // since minExtend do not slice more than 3 items we need to patch line.color here
27627 tMod.marker.line.color = cont.color;
27628
27629 var d0Mod = Lib.minExtend(d0, {trace: tMod});
27630
27631 stylePie(pts, d0Mod, tMod);
27632 }
27633 }
27634
27635 function styleSpatial(d) { // i.e. maninly traces having z and colorscale
27636 var trace = d[0].trace;
27637
27638 var useGradient;
27639 var ptsData = [];
27640 if(trace.visible) {
27641 switch(trace.type) {
27642 case 'histogram2d' :
27643 case 'heatmap' :
27644 ptsData = [
27645 ['M-15,-2V4H15V-2Z'] // similar to contour
27646 ];
27647 useGradient = true;
27648 break;
27649 case 'choropleth' :
27650 case 'choroplethmapbox' :
27651 ptsData = [
27652 ['M-6,-6V6H6V-6Z']
27653 ];
27654 useGradient = true;
27655 break;
27656 case 'densitymapbox' :
27657 ptsData = [
27658 ['M-6,0 a6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0']
27659 ];
27660 useGradient = 'radial';
27661 break;
27662 case 'cone' :
27663 ptsData = [
27664 ['M-6,2 A2,2 0 0,0 -6,6 V6L6,4Z'],
27665 ['M-6,-6 A2,2 0 0,0 -6,-2 L6,-4Z'],
27666 ['M-6,-2 A2,2 0 0,0 -6,2 L6,0Z']
27667 ];
27668 useGradient = false;
27669 break;
27670 case 'streamtube' :
27671 ptsData = [
27672 ['M-6,2 A2,2 0 0,0 -6,6 H6 A2,2 0 0,1 6,2 Z'],
27673 ['M-6,-6 A2,2 0 0,0 -6,-2 H6 A2,2 0 0,1 6,-6 Z'],
27674 ['M-6,-2 A2,2 0 0,0 -6,2 H6 A2,2 0 0,1 6,-2 Z']
27675 ];
27676 useGradient = false;
27677 break;
27678 case 'surface' :
27679 ptsData = [
27680 ['M-6,-6 A2,3 0 0,0 -6,0 H6 A2,3 0 0,1 6,-6 Z'],
27681 ['M-6,1 A2,3 0 0,1 -6,6 H6 A2,3 0 0,0 6,0 Z']
27682 ];
27683 useGradient = true;
27684 break;
27685 case 'mesh3d' :
27686 ptsData = [
27687 ['M-6,6H0L-6,-6Z'],
27688 ['M6,6H0L6,-6Z'],
27689 ['M-6,-6H6L0,6Z']
27690 ];
27691 useGradient = false;
27692 break;
27693 case 'volume' :
27694 ptsData = [
27695 ['M-6,6H0L-6,-6Z'],
27696 ['M6,6H0L6,-6Z'],
27697 ['M-6,-6H6L0,6Z']
27698 ];
27699 useGradient = true;
27700 break;
27701 case 'isosurface':
27702 ptsData = [
27703 ['M-6,6H0L-6,-6Z'],
27704 ['M6,6H0L6,-6Z'],
27705 ['M-6,-6 A12,24 0 0,0 6,-6 L0,6Z']
27706 ];
27707 useGradient = false;
27708 break;
27709 }
27710 }
27711
27712 var pts = d3.select(this).select('g.legendpoints')
27713 .selectAll('path.legend3dandfriends')
27714 .data(ptsData);
27715 pts.enter().append('path').classed('legend3dandfriends', true)
27716 .attr('transform', 'translate(20,0)')
27717 .style('stroke-miterlimit', 1);
27718 pts.exit().remove();
27719
27720 pts.each(function(dd, i) {
27721 var pt = d3.select(this);
27722
27723 var cOpts = extractOpts(trace);
27724 var colorscale = cOpts.colorscale;
27725 var reversescale = cOpts.reversescale;
27726 var fillGradient = function(s) {
27727 if(s.size()) {
27728 var gradientID = 'legendfill-' + trace.uid;
27729 Drawing.gradient(s, gd, gradientID,
27730 getGradientDirection(reversescale, useGradient === 'radial'),
27731 colorscale, 'fill');
27732 }
27733 };
27734
27735 var fillColor;
27736 if(!colorscale) {
27737 var color = trace.vertexcolor || trace.facecolor || trace.color;
27738 fillColor = Lib.isArrayOrTypedArray(color) ? (color[i] || color[0]) : color;
27739 } else {
27740 if(!useGradient) {
27741 var len = colorscale.length;
27742 fillColor =
27743 i === 0 ? colorscale[reversescale ? len - 1 : 0][1] : // minimum
27744 i === 1 ? colorscale[reversescale ? 0 : len - 1][1] : // maximum
27745 colorscale[Math.floor((len - 1) / 2)][1]; // middle
27746 }
27747 }
27748
27749 pt.attr('d', dd[0]);
27750 if(fillColor) {
27751 pt.call(Color.fill, fillColor);
27752 } else {
27753 pt.call(fillGradient);
27754 }
27755 });
27756 }
27757};
27758
27759function getGradientDirection(reversescale, isRadial) {
27760 var str = isRadial ? 'radial' : 'horizontal';
27761 return str + (reversescale ? '' : 'reversed');
27762}
27763
27764},{"../../lib":178,"../../registry":269,"../../traces/pie/helpers":381,"../../traces/pie/style_one":387,"../../traces/scatter/subtypes":413,"../color":52,"../colorscale/helpers":63,"../drawing":74,"d3":16}],111:[function(_dereq_,module,exports){
27765/**
27766* Copyright 2012-2020, Plotly, Inc.
27767* All rights reserved.
27768*
27769* This source code is licensed under the MIT license found in the
27770* LICENSE file in the root directory of this source tree.
27771*/
27772
27773'use strict';
27774
27775var Registry = _dereq_('../../registry');
27776var Plots = _dereq_('../../plots/plots');
27777var axisIds = _dereq_('../../plots/cartesian/axis_ids');
27778var Icons = _dereq_('../../fonts/ploticon');
27779var eraseActiveShape = _dereq_('../shapes/draw').eraseActiveShape;
27780var Lib = _dereq_('../../lib');
27781var _ = Lib._;
27782
27783var modeBarButtons = module.exports = {};
27784
27785/**
27786 * ModeBar buttons configuration
27787 *
27788 * @param {string} name
27789 * name / id of the buttons (for tracking)
27790 * @param {string} title
27791 * text that appears while hovering over the button,
27792 * enter null, false or '' for no hover text
27793 * @param {string} icon
27794 * svg icon object associated with the button
27795 * can be linked to Plotly.Icons to use the default plotly icons
27796 * @param {string} [gravity]
27797 * icon positioning
27798 * @param {function} click
27799 * click handler associated with the button, a function of
27800 * 'gd' (the main graph object) and
27801 * 'ev' (the event object)
27802 * @param {string} [attr]
27803 * attribute associated with button,
27804 * use this with 'val' to keep track of the state
27805 * @param {*} [val]
27806 * initial 'attr' value, can be a function of gd
27807 * @param {boolean} [toggle]
27808 * is the button a toggle button?
27809 */
27810modeBarButtons.toImage = {
27811 name: 'toImage',
27812 title: function(gd) {
27813 var opts = gd._context.toImageButtonOptions || {};
27814 var format = opts.format || 'png';
27815 return format === 'png' ?
27816 _(gd, 'Download plot as a png') : // legacy text
27817 _(gd, 'Download plot'); // generic non-PNG text
27818 },
27819 icon: Icons.camera,
27820 click: function(gd) {
27821 var toImageButtonOptions = gd._context.toImageButtonOptions;
27822 var opts = {format: toImageButtonOptions.format || 'png'};
27823
27824 Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long');
27825
27826 if(opts.format !== 'svg' && Lib.isIE()) {
27827 Lib.notifier(_(gd, 'IE only supports svg. Changing format to svg.'), 'long');
27828 opts.format = 'svg';
27829 }
27830
27831 ['filename', 'width', 'height', 'scale'].forEach(function(key) {
27832 if(key in toImageButtonOptions) {
27833 opts[key] = toImageButtonOptions[key];
27834 }
27835 });
27836
27837 Registry.call('downloadImage', gd, opts)
27838 .then(function(filename) {
27839 Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long');
27840 })
27841 .catch(function() {
27842 Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long');
27843 });
27844 }
27845};
27846
27847modeBarButtons.sendDataToCloud = {
27848 name: 'sendDataToCloud',
27849 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
27850 icon: Icons.disk,
27851 click: function(gd) {
27852 Plots.sendDataToCloud(gd);
27853 }
27854};
27855
27856modeBarButtons.editInChartStudio = {
27857 name: 'editInChartStudio',
27858 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
27859 icon: Icons.pencil,
27860 click: function(gd) {
27861 Plots.sendDataToCloud(gd);
27862 }
27863};
27864
27865modeBarButtons.zoom2d = {
27866 name: 'zoom2d',
27867 title: function(gd) { return _(gd, 'Zoom'); },
27868 attr: 'dragmode',
27869 val: 'zoom',
27870 icon: Icons.zoombox,
27871 click: handleCartesian
27872};
27873
27874modeBarButtons.pan2d = {
27875 name: 'pan2d',
27876 title: function(gd) { return _(gd, 'Pan'); },
27877 attr: 'dragmode',
27878 val: 'pan',
27879 icon: Icons.pan,
27880 click: handleCartesian
27881};
27882
27883modeBarButtons.select2d = {
27884 name: 'select2d',
27885 title: function(gd) { return _(gd, 'Box Select'); },
27886 attr: 'dragmode',
27887 val: 'select',
27888 icon: Icons.selectbox,
27889 click: handleCartesian
27890};
27891
27892modeBarButtons.lasso2d = {
27893 name: 'lasso2d',
27894 title: function(gd) { return _(gd, 'Lasso Select'); },
27895 attr: 'dragmode',
27896 val: 'lasso',
27897 icon: Icons.lasso,
27898 click: handleCartesian
27899};
27900
27901modeBarButtons.drawclosedpath = {
27902 name: 'drawclosedpath',
27903 title: function(gd) { return _(gd, 'Draw closed freeform'); },
27904 attr: 'dragmode',
27905 val: 'drawclosedpath',
27906 icon: Icons.drawclosedpath,
27907 click: handleCartesian
27908};
27909
27910modeBarButtons.drawopenpath = {
27911 name: 'drawopenpath',
27912 title: function(gd) { return _(gd, 'Draw open freeform'); },
27913 attr: 'dragmode',
27914 val: 'drawopenpath',
27915 icon: Icons.drawopenpath,
27916 click: handleCartesian
27917};
27918
27919modeBarButtons.drawline = {
27920 name: 'drawline',
27921 title: function(gd) { return _(gd, 'Draw line'); },
27922 attr: 'dragmode',
27923 val: 'drawline',
27924 icon: Icons.drawline,
27925 click: handleCartesian
27926};
27927
27928modeBarButtons.drawrect = {
27929 name: 'drawrect',
27930 title: function(gd) { return _(gd, 'Draw rectangle'); },
27931 attr: 'dragmode',
27932 val: 'drawrect',
27933 icon: Icons.drawrect,
27934 click: handleCartesian
27935};
27936
27937modeBarButtons.drawcircle = {
27938 name: 'drawcircle',
27939 title: function(gd) { return _(gd, 'Draw circle'); },
27940 attr: 'dragmode',
27941 val: 'drawcircle',
27942 icon: Icons.drawcircle,
27943 click: handleCartesian
27944};
27945
27946modeBarButtons.eraseshape = {
27947 name: 'eraseshape',
27948 title: function(gd) { return _(gd, 'Erase active shape'); },
27949 icon: Icons.eraseshape,
27950 click: eraseActiveShape
27951};
27952
27953modeBarButtons.zoomIn2d = {
27954 name: 'zoomIn2d',
27955 title: function(gd) { return _(gd, 'Zoom in'); },
27956 attr: 'zoom',
27957 val: 'in',
27958 icon: Icons.zoom_plus,
27959 click: handleCartesian
27960};
27961
27962modeBarButtons.zoomOut2d = {
27963 name: 'zoomOut2d',
27964 title: function(gd) { return _(gd, 'Zoom out'); },
27965 attr: 'zoom',
27966 val: 'out',
27967 icon: Icons.zoom_minus,
27968 click: handleCartesian
27969};
27970
27971modeBarButtons.autoScale2d = {
27972 name: 'autoScale2d',
27973 title: function(gd) { return _(gd, 'Autoscale'); },
27974 attr: 'zoom',
27975 val: 'auto',
27976 icon: Icons.autoscale,
27977 click: handleCartesian
27978};
27979
27980modeBarButtons.resetScale2d = {
27981 name: 'resetScale2d',
27982 title: function(gd) { return _(gd, 'Reset axes'); },
27983 attr: 'zoom',
27984 val: 'reset',
27985 icon: Icons.home,
27986 click: handleCartesian
27987};
27988
27989modeBarButtons.hoverClosestCartesian = {
27990 name: 'hoverClosestCartesian',
27991 title: function(gd) { return _(gd, 'Show closest data on hover'); },
27992 attr: 'hovermode',
27993 val: 'closest',
27994 icon: Icons.tooltip_basic,
27995 gravity: 'ne',
27996 click: handleCartesian
27997};
27998
27999modeBarButtons.hoverCompareCartesian = {
28000 name: 'hoverCompareCartesian',
28001 title: function(gd) { return _(gd, 'Compare data on hover'); },
28002 attr: 'hovermode',
28003 val: function(gd) {
28004 return gd._fullLayout._isHoriz ? 'y' : 'x';
28005 },
28006 icon: Icons.tooltip_compare,
28007 gravity: 'ne',
28008 click: handleCartesian
28009};
28010
28011function handleCartesian(gd, ev) {
28012 var button = ev.currentTarget;
28013 var astr = button.getAttribute('data-attr');
28014 var val = button.getAttribute('data-val') || true;
28015 var fullLayout = gd._fullLayout;
28016 var aobj = {};
28017 var axList = axisIds.list(gd, null, true);
28018 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
28019
28020 var ax, i;
28021
28022 if(astr === 'zoom') {
28023 var mag = (val === 'in') ? 0.5 : 2;
28024 var r0 = (1 + mag) / 2;
28025 var r1 = (1 - mag) / 2;
28026 var axName;
28027
28028 for(i = 0; i < axList.length; i++) {
28029 ax = axList[i];
28030
28031 if(!ax.fixedrange) {
28032 axName = ax._name;
28033 if(val === 'auto') {
28034 aobj[axName + '.autorange'] = true;
28035 } else if(val === 'reset') {
28036 if(ax._rangeInitial === undefined) {
28037 aobj[axName + '.autorange'] = true;
28038 } else {
28039 var rangeInitial = ax._rangeInitial.slice();
28040 aobj[axName + '.range[0]'] = rangeInitial[0];
28041 aobj[axName + '.range[1]'] = rangeInitial[1];
28042 }
28043
28044 // N.B. "reset" also resets showspikes
28045 if(ax._showSpikeInitial !== undefined) {
28046 aobj[axName + '.showspikes'] = ax._showSpikeInitial;
28047 if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
28048 allSpikesEnabled = 'off';
28049 }
28050 }
28051 } else {
28052 var rangeNow = [
28053 ax.r2l(ax.range[0]),
28054 ax.r2l(ax.range[1]),
28055 ];
28056
28057 var rangeNew = [
28058 r0 * rangeNow[0] + r1 * rangeNow[1],
28059 r0 * rangeNow[1] + r1 * rangeNow[0]
28060 ];
28061
28062 aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]);
28063 aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]);
28064 }
28065 }
28066 }
28067 } else {
28068 // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
28069 if(astr === 'hovermode' && (val === 'x' || val === 'y')) {
28070 val = fullLayout._isHoriz ? 'y' : 'x';
28071 button.setAttribute('data-val', val);
28072 }
28073
28074 aobj[astr] = val;
28075 }
28076
28077 fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
28078
28079 Registry.call('_guiRelayout', gd, aobj);
28080}
28081
28082modeBarButtons.zoom3d = {
28083 name: 'zoom3d',
28084 title: function(gd) { return _(gd, 'Zoom'); },
28085 attr: 'scene.dragmode',
28086 val: 'zoom',
28087 icon: Icons.zoombox,
28088 click: handleDrag3d
28089};
28090
28091modeBarButtons.pan3d = {
28092 name: 'pan3d',
28093 title: function(gd) { return _(gd, 'Pan'); },
28094 attr: 'scene.dragmode',
28095 val: 'pan',
28096 icon: Icons.pan,
28097 click: handleDrag3d
28098};
28099
28100modeBarButtons.orbitRotation = {
28101 name: 'orbitRotation',
28102 title: function(gd) { return _(gd, 'Orbital rotation'); },
28103 attr: 'scene.dragmode',
28104 val: 'orbit',
28105 icon: Icons['3d_rotate'],
28106 click: handleDrag3d
28107};
28108
28109modeBarButtons.tableRotation = {
28110 name: 'tableRotation',
28111 title: function(gd) { return _(gd, 'Turntable rotation'); },
28112 attr: 'scene.dragmode',
28113 val: 'turntable',
28114 icon: Icons['z-axis'],
28115 click: handleDrag3d
28116};
28117
28118function handleDrag3d(gd, ev) {
28119 var button = ev.currentTarget;
28120 var attr = button.getAttribute('data-attr');
28121 var val = button.getAttribute('data-val') || true;
28122 var sceneIds = gd._fullLayout._subplots.gl3d || [];
28123 var layoutUpdate = {};
28124
28125 var parts = attr.split('.');
28126
28127 for(var i = 0; i < sceneIds.length; i++) {
28128 layoutUpdate[sceneIds[i] + '.' + parts[1]] = val;
28129 }
28130
28131 // for multi-type subplots
28132 var val2d = (val === 'pan') ? val : 'zoom';
28133 layoutUpdate.dragmode = val2d;
28134
28135 Registry.call('_guiRelayout', gd, layoutUpdate);
28136}
28137
28138modeBarButtons.resetCameraDefault3d = {
28139 name: 'resetCameraDefault3d',
28140 title: function(gd) { return _(gd, 'Reset camera to default'); },
28141 attr: 'resetDefault',
28142 icon: Icons.home,
28143 click: handleCamera3d
28144};
28145
28146modeBarButtons.resetCameraLastSave3d = {
28147 name: 'resetCameraLastSave3d',
28148 title: function(gd) { return _(gd, 'Reset camera to last save'); },
28149 attr: 'resetLastSave',
28150 icon: Icons.movie,
28151 click: handleCamera3d
28152};
28153
28154function handleCamera3d(gd, ev) {
28155 var button = ev.currentTarget;
28156 var attr = button.getAttribute('data-attr');
28157 var fullLayout = gd._fullLayout;
28158 var sceneIds = fullLayout._subplots.gl3d || [];
28159 var aobj = {};
28160
28161 for(var i = 0; i < sceneIds.length; i++) {
28162 var sceneId = sceneIds[i];
28163 var camera = sceneId + '.camera';
28164 var aspectratio = sceneId + '.aspectratio';
28165 var aspectmode = sceneId + '.aspectmode';
28166 var scene = fullLayout[sceneId]._scene;
28167 var didUpdate;
28168
28169 if(attr === 'resetLastSave') {
28170 aobj[camera + '.up'] = scene.viewInitial.up;
28171 aobj[camera + '.eye'] = scene.viewInitial.eye;
28172 aobj[camera + '.center'] = scene.viewInitial.center;
28173 didUpdate = true;
28174 } else if(attr === 'resetDefault') {
28175 aobj[camera + '.up'] = null;
28176 aobj[camera + '.eye'] = null;
28177 aobj[camera + '.center'] = null;
28178 didUpdate = true;
28179 }
28180
28181 if(didUpdate) {
28182 aobj[aspectratio + '.x'] = scene.viewInitial.aspectratio.x;
28183 aobj[aspectratio + '.y'] = scene.viewInitial.aspectratio.y;
28184 aobj[aspectratio + '.z'] = scene.viewInitial.aspectratio.z;
28185 aobj[aspectmode] = scene.viewInitial.aspectmode;
28186 }
28187 }
28188
28189 Registry.call('_guiRelayout', gd, aobj);
28190}
28191
28192modeBarButtons.hoverClosest3d = {
28193 name: 'hoverClosest3d',
28194 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
28195 attr: 'hovermode',
28196 val: null,
28197 toggle: true,
28198 icon: Icons.tooltip_basic,
28199 gravity: 'ne',
28200 click: handleHover3d
28201};
28202
28203function getNextHover3d(gd, ev) {
28204 var button = ev.currentTarget;
28205 var val = button._previousVal;
28206 var fullLayout = gd._fullLayout;
28207 var sceneIds = fullLayout._subplots.gl3d || [];
28208
28209 var axes = ['xaxis', 'yaxis', 'zaxis'];
28210
28211 // initialize 'current spike' object to be stored in the DOM
28212 var currentSpikes = {};
28213 var layoutUpdate = {};
28214
28215 if(val) {
28216 layoutUpdate = val;
28217 button._previousVal = null;
28218 } else {
28219 for(var i = 0; i < sceneIds.length; i++) {
28220 var sceneId = sceneIds[i];
28221 var sceneLayout = fullLayout[sceneId];
28222
28223 var hovermodeAStr = sceneId + '.hovermode';
28224 currentSpikes[hovermodeAStr] = sceneLayout.hovermode;
28225 layoutUpdate[hovermodeAStr] = false;
28226
28227 // copy all the current spike attrs
28228 for(var j = 0; j < 3; j++) {
28229 var axis = axes[j];
28230 var spikeAStr = sceneId + '.' + axis + '.showspikes';
28231 layoutUpdate[spikeAStr] = false;
28232 currentSpikes[spikeAStr] = sceneLayout[axis].showspikes;
28233 }
28234 }
28235
28236 button._previousVal = currentSpikes;
28237 }
28238 return layoutUpdate;
28239}
28240
28241function handleHover3d(gd, ev) {
28242 var layoutUpdate = getNextHover3d(gd, ev);
28243 Registry.call('_guiRelayout', gd, layoutUpdate);
28244}
28245
28246modeBarButtons.zoomInGeo = {
28247 name: 'zoomInGeo',
28248 title: function(gd) { return _(gd, 'Zoom in'); },
28249 attr: 'zoom',
28250 val: 'in',
28251 icon: Icons.zoom_plus,
28252 click: handleGeo
28253};
28254
28255modeBarButtons.zoomOutGeo = {
28256 name: 'zoomOutGeo',
28257 title: function(gd) { return _(gd, 'Zoom out'); },
28258 attr: 'zoom',
28259 val: 'out',
28260 icon: Icons.zoom_minus,
28261 click: handleGeo
28262};
28263
28264modeBarButtons.resetGeo = {
28265 name: 'resetGeo',
28266 title: function(gd) { return _(gd, 'Reset'); },
28267 attr: 'reset',
28268 val: null,
28269 icon: Icons.autoscale,
28270 click: handleGeo
28271};
28272
28273modeBarButtons.hoverClosestGeo = {
28274 name: 'hoverClosestGeo',
28275 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
28276 attr: 'hovermode',
28277 val: null,
28278 toggle: true,
28279 icon: Icons.tooltip_basic,
28280 gravity: 'ne',
28281 click: toggleHover
28282};
28283
28284function handleGeo(gd, ev) {
28285 var button = ev.currentTarget;
28286 var attr = button.getAttribute('data-attr');
28287 var val = button.getAttribute('data-val') || true;
28288 var fullLayout = gd._fullLayout;
28289 var geoIds = fullLayout._subplots.geo || [];
28290
28291 for(var i = 0; i < geoIds.length; i++) {
28292 var id = geoIds[i];
28293 var geoLayout = fullLayout[id];
28294
28295 if(attr === 'zoom') {
28296 var scale = geoLayout.projection.scale;
28297 var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
28298
28299 Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
28300 }
28301 }
28302
28303 if(attr === 'reset') {
28304 resetView(gd, 'geo');
28305 }
28306}
28307
28308modeBarButtons.hoverClosestGl2d = {
28309 name: 'hoverClosestGl2d',
28310 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
28311 attr: 'hovermode',
28312 val: null,
28313 toggle: true,
28314 icon: Icons.tooltip_basic,
28315 gravity: 'ne',
28316 click: toggleHover
28317};
28318
28319modeBarButtons.hoverClosestPie = {
28320 name: 'hoverClosestPie',
28321 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
28322 attr: 'hovermode',
28323 val: 'closest',
28324 icon: Icons.tooltip_basic,
28325 gravity: 'ne',
28326 click: toggleHover
28327};
28328
28329function getNextHover(gd) {
28330 var fullLayout = gd._fullLayout;
28331
28332 if(fullLayout.hovermode) return false;
28333
28334 if(fullLayout._has('cartesian')) {
28335 return fullLayout._isHoriz ? 'y' : 'x';
28336 }
28337 return 'closest';
28338}
28339
28340function toggleHover(gd) {
28341 var newHover = getNextHover(gd);
28342 Registry.call('_guiRelayout', gd, 'hovermode', newHover);
28343}
28344
28345modeBarButtons.resetViewSankey = {
28346 name: 'resetSankeyGroup',
28347 title: function(gd) { return _(gd, 'Reset view'); },
28348 icon: Icons.home,
28349 click: function(gd) {
28350 var aObj = {
28351 'node.groups': [],
28352 'node.x': [],
28353 'node.y': []
28354 };
28355 for(var i = 0; i < gd._fullData.length; i++) {
28356 var viewInitial = gd._fullData[i]._viewInitial;
28357 aObj['node.groups'].push(viewInitial.node.groups.slice());
28358 aObj['node.x'].push(viewInitial.node.x.slice());
28359 aObj['node.y'].push(viewInitial.node.y.slice());
28360 }
28361 Registry.call('restyle', gd, aObj);
28362 }
28363};
28364
28365// buttons when more then one plot types are present
28366
28367modeBarButtons.toggleHover = {
28368 name: 'toggleHover',
28369 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
28370 attr: 'hovermode',
28371 val: null,
28372 toggle: true,
28373 icon: Icons.tooltip_basic,
28374 gravity: 'ne',
28375 click: function(gd, ev) {
28376 var layoutUpdate = getNextHover3d(gd, ev);
28377 layoutUpdate.hovermode = getNextHover(gd);
28378
28379 Registry.call('_guiRelayout', gd, layoutUpdate);
28380 }
28381};
28382
28383modeBarButtons.resetViews = {
28384 name: 'resetViews',
28385 title: function(gd) { return _(gd, 'Reset views'); },
28386 icon: Icons.home,
28387 click: function(gd, ev) {
28388 var button = ev.currentTarget;
28389
28390 button.setAttribute('data-attr', 'zoom');
28391 button.setAttribute('data-val', 'reset');
28392 handleCartesian(gd, ev);
28393
28394 button.setAttribute('data-attr', 'resetLastSave');
28395 handleCamera3d(gd, ev);
28396
28397 resetView(gd, 'geo');
28398 resetView(gd, 'mapbox');
28399 }
28400};
28401
28402modeBarButtons.toggleSpikelines = {
28403 name: 'toggleSpikelines',
28404 title: function(gd) { return _(gd, 'Toggle Spike Lines'); },
28405 icon: Icons.spikeline,
28406 attr: '_cartesianSpikesEnabled',
28407 val: 'on',
28408 click: function(gd) {
28409 var fullLayout = gd._fullLayout;
28410 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
28411
28412 fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on';
28413 Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd));
28414 }
28415};
28416
28417function setSpikelineVisibility(gd) {
28418 var fullLayout = gd._fullLayout;
28419 var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on';
28420 var axList = axisIds.list(gd, null, true);
28421 var aobj = {};
28422
28423 for(var i = 0; i < axList.length; i++) {
28424 var ax = axList[i];
28425 aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial;
28426 }
28427
28428 return aobj;
28429}
28430
28431modeBarButtons.resetViewMapbox = {
28432 name: 'resetViewMapbox',
28433 title: function(gd) { return _(gd, 'Reset view'); },
28434 attr: 'reset',
28435 icon: Icons.home,
28436 click: function(gd) {
28437 resetView(gd, 'mapbox');
28438 }
28439};
28440
28441modeBarButtons.zoomInMapbox = {
28442 name: 'zoomInMapbox',
28443 title: function(gd) { return _(gd, 'Zoom in'); },
28444 attr: 'zoom',
28445 val: 'in',
28446 icon: Icons.zoom_plus,
28447 click: handleMapboxZoom
28448};
28449
28450modeBarButtons.zoomOutMapbox = {
28451 name: 'zoomOutMapbox',
28452 title: function(gd) { return _(gd, 'Zoom out'); },
28453 attr: 'zoom',
28454 val: 'out',
28455 icon: Icons.zoom_minus,
28456 click: handleMapboxZoom
28457};
28458
28459function handleMapboxZoom(gd, ev) {
28460 var button = ev.currentTarget;
28461 var val = button.getAttribute('data-val');
28462 var fullLayout = gd._fullLayout;
28463 var subplotIds = fullLayout._subplots.mapbox || [];
28464 var scalar = 1.05;
28465 var aObj = {};
28466
28467 for(var i = 0; i < subplotIds.length; i++) {
28468 var id = subplotIds[i];
28469 var current = fullLayout[id].zoom;
28470 var next = (val === 'in') ? scalar * current : current / scalar;
28471 aObj[id + '.zoom'] = next;
28472 }
28473
28474 Registry.call('_guiRelayout', gd, aObj);
28475}
28476
28477function resetView(gd, subplotType) {
28478 var fullLayout = gd._fullLayout;
28479 var subplotIds = fullLayout._subplots[subplotType] || [];
28480 var aObj = {};
28481
28482 for(var i = 0; i < subplotIds.length; i++) {
28483 var id = subplotIds[i];
28484 var subplotObj = fullLayout[id]._subplot;
28485 var viewInitial = subplotObj.viewInitial;
28486 var viewKeys = Object.keys(viewInitial);
28487
28488 for(var j = 0; j < viewKeys.length; j++) {
28489 var key = viewKeys[j];
28490 aObj[id + '.' + key] = viewInitial[key];
28491 }
28492 }
28493
28494 Registry.call('_guiRelayout', gd, aObj);
28495}
28496
28497},{"../../fonts/ploticon":162,"../../lib":178,"../../plots/cartesian/axis_ids":225,"../../plots/plots":256,"../../registry":269,"../shapes/draw":133}],112:[function(_dereq_,module,exports){
28498/**
28499* Copyright 2012-2020, Plotly, Inc.
28500* All rights reserved.
28501*
28502* This source code is licensed under the MIT license found in the
28503* LICENSE file in the root directory of this source tree.
28504*/
28505
28506
28507'use strict';
28508
28509exports.manage = _dereq_('./manage');
28510
28511},{"./manage":113}],113:[function(_dereq_,module,exports){
28512/**
28513* Copyright 2012-2020, Plotly, Inc.
28514* All rights reserved.
28515*
28516* This source code is licensed under the MIT license found in the
28517* LICENSE file in the root directory of this source tree.
28518*/
28519
28520
28521'use strict';
28522
28523var axisIds = _dereq_('../../plots/cartesian/axis_ids');
28524var scatterSubTypes = _dereq_('../../traces/scatter/subtypes');
28525var Registry = _dereq_('../../registry');
28526var isUnifiedHover = _dereq_('../fx/helpers').isUnifiedHover;
28527
28528var createModeBar = _dereq_('./modebar');
28529var modeBarButtons = _dereq_('./buttons');
28530
28531/**
28532 * ModeBar wrapper around 'create' and 'update',
28533 * chooses buttons to pass to ModeBar constructor based on
28534 * plot type and plot config.
28535 *
28536 * @param {object} gd main plot object
28537 *
28538 */
28539module.exports = function manageModeBar(gd) {
28540 var fullLayout = gd._fullLayout;
28541 var context = gd._context;
28542 var modeBar = fullLayout._modeBar;
28543
28544 if(!context.displayModeBar && !context.watermark) {
28545 if(modeBar) {
28546 modeBar.destroy();
28547 delete fullLayout._modeBar;
28548 }
28549 return;
28550 }
28551
28552 if(!Array.isArray(context.modeBarButtonsToRemove)) {
28553 throw new Error([
28554 '*modeBarButtonsToRemove* configuration options',
28555 'must be an array.'
28556 ].join(' '));
28557 }
28558
28559 if(!Array.isArray(context.modeBarButtonsToAdd)) {
28560 throw new Error([
28561 '*modeBarButtonsToAdd* configuration options',
28562 'must be an array.'
28563 ].join(' '));
28564 }
28565
28566 var customButtons = context.modeBarButtons;
28567 var buttonGroups;
28568
28569 if(Array.isArray(customButtons) && customButtons.length) {
28570 buttonGroups = fillCustomButton(customButtons);
28571 } else if(!context.displayModeBar && context.watermark) {
28572 buttonGroups = [];
28573 } else {
28574 buttonGroups = getButtonGroups(gd);
28575 }
28576
28577 if(modeBar) modeBar.update(gd, buttonGroups);
28578 else fullLayout._modeBar = createModeBar(gd, buttonGroups);
28579};
28580
28581var DRAW_MODES = [
28582 'drawline',
28583 'drawopenpath',
28584 'drawclosedpath',
28585 'drawcircle',
28586 'drawrect',
28587 'eraseshape'
28588];
28589
28590// logic behind which buttons are displayed by default
28591function getButtonGroups(gd) {
28592 var fullLayout = gd._fullLayout;
28593 var fullData = gd._fullData;
28594 var context = gd._context;
28595 var buttonsToRemove = context.modeBarButtonsToRemove;
28596 var buttonsToAdd = context.modeBarButtonsToAdd;
28597
28598 var hasCartesian = fullLayout._has('cartesian');
28599 var hasGL3D = fullLayout._has('gl3d');
28600 var hasGeo = fullLayout._has('geo');
28601 var hasPie = fullLayout._has('pie');
28602 var hasFunnelarea = fullLayout._has('funnelarea');
28603 var hasGL2D = fullLayout._has('gl2d');
28604 var hasTernary = fullLayout._has('ternary');
28605 var hasMapbox = fullLayout._has('mapbox');
28606 var hasPolar = fullLayout._has('polar');
28607 var hasSankey = fullLayout._has('sankey');
28608 var allAxesFixed = areAllAxesFixed(fullLayout);
28609 var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode);
28610
28611 var groups = [];
28612
28613 function addGroup(newGroup) {
28614 if(!newGroup.length) return;
28615
28616 var out = [];
28617
28618 for(var i = 0; i < newGroup.length; i++) {
28619 var button = newGroup[i];
28620 if(buttonsToRemove.indexOf(button) !== -1) continue;
28621 out.push(modeBarButtons[button]);
28622 }
28623
28624 groups.push(out);
28625 }
28626
28627 // buttons common to all plot types
28628 var commonGroup = ['toImage'];
28629 if(context.showEditInChartStudio) commonGroup.push('editInChartStudio');
28630 else if(context.showSendToCloud) commonGroup.push('sendDataToCloud');
28631 addGroup(commonGroup);
28632
28633 var zoomGroup = [];
28634 var hoverGroup = [];
28635 var resetGroup = [];
28636 var dragModeGroup = [];
28637
28638 if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
28639 // graphs with more than one plot types get 'union buttons'
28640 // which reset the view or toggle hover labels across all subplots.
28641 hoverGroup = ['toggleHover'];
28642 resetGroup = ['resetViews'];
28643 } else if(hasGeo) {
28644 zoomGroup = ['zoomInGeo', 'zoomOutGeo'];
28645 hoverGroup = ['hoverClosestGeo'];
28646 resetGroup = ['resetGeo'];
28647 } else if(hasGL3D) {
28648 hoverGroup = ['hoverClosest3d'];
28649 resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d'];
28650 } else if(hasMapbox) {
28651 zoomGroup = ['zoomInMapbox', 'zoomOutMapbox'];
28652 hoverGroup = ['toggleHover'];
28653 resetGroup = ['resetViewMapbox'];
28654 } else if(hasGL2D) {
28655 hoverGroup = ['hoverClosestGl2d'];
28656 } else if(hasPie) {
28657 hoverGroup = ['hoverClosestPie'];
28658 } else if(hasSankey) {
28659 hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian'];
28660 resetGroup = ['resetViewSankey'];
28661 } else { // hasPolar, hasTernary
28662 // always show at least one hover icon.
28663 hoverGroup = ['toggleHover'];
28664 }
28665 // if we have cartesian, allow switching between closest and compare
28666 // regardless of what other types are on the plot, since they'll all
28667 // just treat any truthy hovermode as 'closest'
28668 if(hasCartesian) {
28669 hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'];
28670 }
28671 if(hasNoHover(fullData) || hasUnifiedHoverLabel) {
28672 hoverGroup = [];
28673 }
28674
28675 if((hasCartesian || hasGL2D) && !allAxesFixed) {
28676 zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d'];
28677 if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d'];
28678 }
28679
28680 if(hasGL3D) {
28681 dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'];
28682 } else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
28683 dragModeGroup = ['zoom2d', 'pan2d'];
28684 } else if(hasMapbox || hasGeo) {
28685 dragModeGroup = ['pan2d'];
28686 } else if(hasPolar) {
28687 dragModeGroup = ['zoom2d'];
28688 }
28689 if(isSelectable(fullData)) {
28690 dragModeGroup.push('select2d', 'lasso2d');
28691 }
28692
28693 // accept pre-defined buttons as string
28694 if(Array.isArray(buttonsToAdd)) {
28695 var newList = [];
28696 for(var i = 0; i < buttonsToAdd.length; i++) {
28697 var b = buttonsToAdd[i];
28698 if(typeof b === 'string') {
28699 if(DRAW_MODES.indexOf(b) !== -1) {
28700 if(
28701 fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch)
28702 fullLayout._has('cartesian') // draw shapes in data coordinate
28703 ) {
28704 dragModeGroup.push(b);
28705 }
28706 }
28707 } else newList.push(b);
28708 }
28709 buttonsToAdd = newList;
28710 }
28711
28712 addGroup(dragModeGroup);
28713 addGroup(zoomGroup.concat(resetGroup));
28714 addGroup(hoverGroup);
28715
28716 return appendButtonsToGroups(groups, buttonsToAdd);
28717}
28718
28719function areAllAxesFixed(fullLayout) {
28720 var axList = axisIds.list({_fullLayout: fullLayout}, null, true);
28721
28722 for(var i = 0; i < axList.length; i++) {
28723 if(!axList[i].fixedrange) {
28724 return false;
28725 }
28726 }
28727
28728 return true;
28729}
28730
28731// look for traces that support selection
28732// to be updated as we add more selectPoints handlers
28733function isSelectable(fullData) {
28734 var selectable = false;
28735
28736 for(var i = 0; i < fullData.length; i++) {
28737 if(selectable) break;
28738
28739 var trace = fullData[i];
28740
28741 if(!trace._module || !trace._module.selectPoints) continue;
28742
28743 if(Registry.traceIs(trace, 'scatter-like')) {
28744 if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
28745 selectable = true;
28746 }
28747 } else if(Registry.traceIs(trace, 'box-violin')) {
28748 if(trace.boxpoints === 'all' || trace.points === 'all') {
28749 selectable = true;
28750 }
28751 } else {
28752 // assume that in general if the trace module has selectPoints,
28753 // then it's selectable. Scatter is an exception to this because it must
28754 // have markers or text, not just be a scatter type.
28755
28756 selectable = true;
28757 }
28758 }
28759
28760 return selectable;
28761}
28762
28763// check whether all trace are 'noHover'
28764function hasNoHover(fullData) {
28765 for(var i = 0; i < fullData.length; i++) {
28766 if(!Registry.traceIs(fullData[i], 'noHover')) return false;
28767 }
28768 return true;
28769}
28770
28771function appendButtonsToGroups(groups, buttons) {
28772 if(buttons.length) {
28773 if(Array.isArray(buttons[0])) {
28774 for(var i = 0; i < buttons.length; i++) {
28775 groups.push(buttons[i]);
28776 }
28777 } else groups.push(buttons);
28778 }
28779
28780 return groups;
28781}
28782
28783// fill in custom buttons referring to default mode bar buttons
28784function fillCustomButton(customButtons) {
28785 for(var i = 0; i < customButtons.length; i++) {
28786 var buttonGroup = customButtons[i];
28787
28788 for(var j = 0; j < buttonGroup.length; j++) {
28789 var button = buttonGroup[j];
28790
28791 if(typeof button === 'string') {
28792 if(modeBarButtons[button] !== undefined) {
28793 customButtons[i][j] = modeBarButtons[button];
28794 } else {
28795 throw new Error([
28796 '*modeBarButtons* configuration options',
28797 'invalid button name'
28798 ].join(' '));
28799 }
28800 }
28801 }
28802 }
28803
28804 return customButtons;
28805}
28806
28807},{"../../plots/cartesian/axis_ids":225,"../../registry":269,"../../traces/scatter/subtypes":413,"../fx/helpers":88,"./buttons":111,"./modebar":114}],114:[function(_dereq_,module,exports){
28808/**
28809* Copyright 2012-2020, Plotly, Inc.
28810* All rights reserved.
28811*
28812* This source code is licensed under the MIT license found in the
28813* LICENSE file in the root directory of this source tree.
28814*/
28815
28816
28817'use strict';
28818
28819var d3 = _dereq_('d3');
28820var isNumeric = _dereq_('fast-isnumeric');
28821
28822var Lib = _dereq_('../../lib');
28823var Icons = _dereq_('../../fonts/ploticon');
28824var Parser = new DOMParser();
28825
28826/**
28827 * UI controller for interactive plots
28828 * @Class
28829 * @Param {object} opts
28830 * @Param {object} opts.buttons nested arrays of grouped buttons config objects
28831 * @Param {object} opts.container container div to append modeBar
28832 * @Param {object} opts.graphInfo primary plot object containing data and layout
28833 */
28834function ModeBar(opts) {
28835 this.container = opts.container;
28836 this.element = document.createElement('div');
28837
28838 this.update(opts.graphInfo, opts.buttons);
28839
28840 this.container.appendChild(this.element);
28841}
28842
28843var proto = ModeBar.prototype;
28844
28845/**
28846 * Update modeBar (buttons and logo)
28847 *
28848 * @param {object} graphInfo primary plot object containing data and layout
28849 * @param {array of arrays} buttons nested arrays of grouped buttons to initialize
28850 *
28851 */
28852proto.update = function(graphInfo, buttons) {
28853 this.graphInfo = graphInfo;
28854
28855 var context = this.graphInfo._context;
28856 var fullLayout = this.graphInfo._fullLayout;
28857 var modeBarId = 'modebar-' + fullLayout._uid;
28858
28859 this.element.setAttribute('id', modeBarId);
28860 this._uid = modeBarId;
28861
28862 this.element.className = 'modebar';
28863 if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg';
28864
28865 if(fullLayout.modebar.orientation === 'v') {
28866 this.element.className += ' vertical';
28867 buttons = buttons.reverse();
28868 }
28869
28870 var style = fullLayout.modebar;
28871 var bgSelector = context.displayModeBar === 'hover' ? '.js-plotly-plot .plotly:hover ' : '';
28872
28873 Lib.deleteRelatedStyleRule(modeBarId);
28874 Lib.addRelatedStyleRule(modeBarId, bgSelector + '#' + modeBarId + ' .modebar-group', 'background-color: ' + style.bgcolor);
28875 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + style.color);
28876 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + style.activecolor);
28877 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + style.activecolor);
28878
28879 // if buttons or logo have changed, redraw modebar interior
28880 var needsNewButtons = !this.hasButtons(buttons);
28881 var needsNewLogo = (this.hasLogo !== context.displaylogo);
28882 var needsNewLocale = (this.locale !== context.locale);
28883
28884 this.locale = context.locale;
28885
28886 if(needsNewButtons || needsNewLogo || needsNewLocale) {
28887 this.removeAllButtons();
28888
28889 this.updateButtons(buttons);
28890
28891 if(context.watermark || context.displaylogo) {
28892 var logoGroup = this.getLogo();
28893 if(context.watermark) {
28894 logoGroup.className = logoGroup.className + ' watermark';
28895 }
28896
28897 if(fullLayout.modebar.orientation === 'v') {
28898 this.element.insertBefore(logoGroup, this.element.childNodes[0]);
28899 } else {
28900 this.element.appendChild(logoGroup);
28901 }
28902
28903 this.hasLogo = true;
28904 }
28905 }
28906
28907 this.updateActiveButton();
28908};
28909
28910proto.updateButtons = function(buttons) {
28911 var _this = this;
28912
28913 this.buttons = buttons;
28914 this.buttonElements = [];
28915 this.buttonsNames = [];
28916
28917 this.buttons.forEach(function(buttonGroup) {
28918 var group = _this.createGroup();
28919
28920 buttonGroup.forEach(function(buttonConfig) {
28921 var buttonName = buttonConfig.name;
28922 if(!buttonName) {
28923 throw new Error('must provide button \'name\' in button config');
28924 }
28925 if(_this.buttonsNames.indexOf(buttonName) !== -1) {
28926 throw new Error('button name \'' + buttonName + '\' is taken');
28927 }
28928 _this.buttonsNames.push(buttonName);
28929
28930 var button = _this.createButton(buttonConfig);
28931 _this.buttonElements.push(button);
28932 group.appendChild(button);
28933 });
28934
28935 _this.element.appendChild(group);
28936 });
28937};
28938
28939/**
28940 * Empty div for containing a group of buttons
28941 * @Return {HTMLelement}
28942 */
28943proto.createGroup = function() {
28944 var group = document.createElement('div');
28945 group.className = 'modebar-group';
28946 return group;
28947};
28948
28949/**
28950 * Create a new button div and set constant and configurable attributes
28951 * @Param {object} config (see ./buttons.js for more info)
28952 * @Return {HTMLelement}
28953 */
28954proto.createButton = function(config) {
28955 var _this = this;
28956 var button = document.createElement('a');
28957
28958 button.setAttribute('rel', 'tooltip');
28959 button.className = 'modebar-btn';
28960
28961 var title = config.title;
28962 if(title === undefined) title = config.name;
28963 // for localization: allow title to be a callable that takes gd as arg
28964 else if(typeof title === 'function') title = title(this.graphInfo);
28965
28966 if(title || title === 0) button.setAttribute('data-title', title);
28967
28968 if(config.attr !== undefined) button.setAttribute('data-attr', config.attr);
28969
28970 var val = config.val;
28971 if(val !== undefined) {
28972 if(typeof val === 'function') val = val(this.graphInfo);
28973 button.setAttribute('data-val', val);
28974 }
28975
28976 var click = config.click;
28977 if(typeof click !== 'function') {
28978 throw new Error('must provide button \'click\' function in button config');
28979 } else {
28980 button.addEventListener('click', function(ev) {
28981 config.click(_this.graphInfo, ev);
28982
28983 // only needed for 'hoverClosestGeo' which does not call relayout
28984 _this.updateActiveButton(ev.currentTarget);
28985 });
28986 }
28987
28988 button.setAttribute('data-toggle', config.toggle || false);
28989 if(config.toggle) d3.select(button).classed('active', true);
28990
28991 var icon = config.icon;
28992 if(typeof icon === 'function') {
28993 button.appendChild(icon());
28994 } else {
28995 button.appendChild(this.createIcon(icon || Icons.question));
28996 }
28997 button.setAttribute('data-gravity', config.gravity || 'n');
28998
28999 return button;
29000};
29001
29002/**
29003 * Add an icon to a button
29004 * @Param {object} thisIcon
29005 * @Param {number} thisIcon.width
29006 * @Param {string} thisIcon.path
29007 * @Param {string} thisIcon.color
29008 * @Return {HTMLelement}
29009 */
29010proto.createIcon = function(thisIcon) {
29011 var iconHeight = isNumeric(thisIcon.height) ?
29012 Number(thisIcon.height) :
29013 thisIcon.ascent - thisIcon.descent;
29014 var svgNS = 'http://www.w3.org/2000/svg';
29015 var icon;
29016
29017 if(thisIcon.path) {
29018 icon = document.createElementNS(svgNS, 'svg');
29019 icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
29020 icon.setAttribute('class', 'icon');
29021
29022 var path = document.createElementNS(svgNS, 'path');
29023 path.setAttribute('d', thisIcon.path);
29024
29025 if(thisIcon.transform) {
29026 path.setAttribute('transform', thisIcon.transform);
29027 } else if(thisIcon.ascent !== undefined) {
29028 // Legacy icon transform calculation
29029 path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
29030 }
29031
29032 icon.appendChild(path);
29033 }
29034
29035 if(thisIcon.svg) {
29036 var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml');
29037 icon = svgDoc.childNodes[0];
29038 }
29039
29040 icon.setAttribute('height', '1em');
29041 icon.setAttribute('width', '1em');
29042
29043 return icon;
29044};
29045
29046/**
29047 * Updates active button with attribute specified in layout
29048 * @Param {object} graphInfo plot object containing data and layout
29049 * @Return {HTMLelement}
29050 */
29051proto.updateActiveButton = function(buttonClicked) {
29052 var fullLayout = this.graphInfo._fullLayout;
29053 var dataAttrClicked = (buttonClicked !== undefined) ?
29054 buttonClicked.getAttribute('data-attr') :
29055 null;
29056
29057 this.buttonElements.forEach(function(button) {
29058 var thisval = button.getAttribute('data-val') || true;
29059 var dataAttr = button.getAttribute('data-attr');
29060 var isToggleButton = (button.getAttribute('data-toggle') === 'true');
29061 var button3 = d3.select(button);
29062
29063 // Use 'data-toggle' and 'buttonClicked' to toggle buttons
29064 // that have no one-to-one equivalent in fullLayout
29065 if(isToggleButton) {
29066 if(dataAttr === dataAttrClicked) {
29067 button3.classed('active', !button3.classed('active'));
29068 }
29069 } else {
29070 var val = (dataAttr === null) ?
29071 dataAttr :
29072 Lib.nestedProperty(fullLayout, dataAttr).get();
29073
29074 button3.classed('active', val === thisval);
29075 }
29076 });
29077};
29078
29079/**
29080 * Check if modeBar is configured as button configuration argument
29081 *
29082 * @Param {object} buttons 2d array of grouped button config objects
29083 * @Return {boolean}
29084 */
29085proto.hasButtons = function(buttons) {
29086 var currentButtons = this.buttons;
29087
29088 if(!currentButtons) return false;
29089
29090 if(buttons.length !== currentButtons.length) return false;
29091
29092 for(var i = 0; i < buttons.length; ++i) {
29093 if(buttons[i].length !== currentButtons[i].length) return false;
29094 for(var j = 0; j < buttons[i].length; j++) {
29095 if(buttons[i][j].name !== currentButtons[i][j].name) return false;
29096 }
29097 }
29098
29099 return true;
29100};
29101
29102/**
29103 * @return {HTMLDivElement} The logo image wrapped in a group
29104 */
29105proto.getLogo = function() {
29106 var group = this.createGroup();
29107 var a = document.createElement('a');
29108
29109 a.href = 'https://plotly.com/';
29110 a.target = '_blank';
29111 a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly'));
29112 a.className = 'modebar-btn plotlyjsicon modebar-btn--logo';
29113
29114 a.appendChild(this.createIcon(Icons.newplotlylogo));
29115
29116 group.appendChild(a);
29117 return group;
29118};
29119
29120proto.removeAllButtons = function() {
29121 while(this.element.firstChild) {
29122 this.element.removeChild(this.element.firstChild);
29123 }
29124
29125 this.hasLogo = false;
29126};
29127
29128proto.destroy = function() {
29129 Lib.removeElement(this.container.querySelector('.modebar'));
29130 Lib.deleteRelatedStyleRule(this._uid);
29131};
29132
29133function createModeBar(gd, buttons) {
29134 var fullLayout = gd._fullLayout;
29135
29136 var modeBar = new ModeBar({
29137 graphInfo: gd,
29138 container: fullLayout._modebardiv.node(),
29139 buttons: buttons
29140 });
29141
29142 if(fullLayout._privateplot) {
29143 d3.select(modeBar.element).append('span')
29144 .classed('badge-private float--left', true)
29145 .text('PRIVATE');
29146 }
29147
29148 return modeBar;
29149}
29150
29151module.exports = createModeBar;
29152
29153},{"../../fonts/ploticon":162,"../../lib":178,"d3":16,"fast-isnumeric":18}],115:[function(_dereq_,module,exports){
29154/**
29155* Copyright 2012-2020, Plotly, Inc.
29156* All rights reserved.
29157*
29158* This source code is licensed under the MIT license found in the
29159* LICENSE file in the root directory of this source tree.
29160*/
29161
29162'use strict';
29163
29164var fontAttrs = _dereq_('../../plots/font_attributes');
29165var colorAttrs = _dereq_('../color/attributes');
29166var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
29167
29168var buttonAttrs = templatedArray('button', {
29169 visible: {
29170 valType: 'boolean',
29171
29172 dflt: true,
29173 editType: 'plot',
29174
29175 },
29176 step: {
29177 valType: 'enumerated',
29178
29179 values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'],
29180 dflt: 'month',
29181 editType: 'plot',
29182
29183 },
29184 stepmode: {
29185 valType: 'enumerated',
29186
29187 values: ['backward', 'todate'],
29188 dflt: 'backward',
29189 editType: 'plot',
29190
29191 },
29192 count: {
29193 valType: 'number',
29194
29195 min: 0,
29196 dflt: 1,
29197 editType: 'plot',
29198
29199 },
29200 label: {
29201 valType: 'string',
29202
29203 editType: 'plot',
29204
29205 },
29206 editType: 'plot',
29207
29208});
29209
29210module.exports = {
29211 visible: {
29212 valType: 'boolean',
29213
29214 editType: 'plot',
29215
29216 },
29217
29218 buttons: buttonAttrs,
29219
29220 x: {
29221 valType: 'number',
29222 min: -2,
29223 max: 3,
29224
29225 editType: 'plot',
29226
29227 },
29228 xanchor: {
29229 valType: 'enumerated',
29230 values: ['auto', 'left', 'center', 'right'],
29231 dflt: 'left',
29232
29233 editType: 'plot',
29234
29235 },
29236 y: {
29237 valType: 'number',
29238 min: -2,
29239 max: 3,
29240
29241 editType: 'plot',
29242
29243 },
29244 yanchor: {
29245 valType: 'enumerated',
29246 values: ['auto', 'top', 'middle', 'bottom'],
29247 dflt: 'bottom',
29248
29249 editType: 'plot',
29250
29251 },
29252
29253 font: fontAttrs({
29254 editType: 'plot',
29255
29256 }),
29257
29258 bgcolor: {
29259 valType: 'color',
29260 dflt: colorAttrs.lightLine,
29261
29262 editType: 'plot',
29263
29264 },
29265 activecolor: {
29266 valType: 'color',
29267
29268 editType: 'plot',
29269
29270 },
29271 bordercolor: {
29272 valType: 'color',
29273 dflt: colorAttrs.defaultLine,
29274
29275 editType: 'plot',
29276
29277 },
29278 borderwidth: {
29279 valType: 'number',
29280 min: 0,
29281 dflt: 0,
29282
29283 editType: 'plot',
29284
29285 },
29286 editType: 'plot'
29287};
29288
29289},{"../../plot_api/plot_template":212,"../../plots/font_attributes":250,"../color/attributes":51}],116:[function(_dereq_,module,exports){
29290/**
29291* Copyright 2012-2020, Plotly, Inc.
29292* All rights reserved.
29293*
29294* This source code is licensed under the MIT license found in the
29295* LICENSE file in the root directory of this source tree.
29296*/
29297
29298'use strict';
29299
29300
29301module.exports = {
29302
29303 // 'y' position pad above counter axis domain
29304 yPad: 0.02,
29305
29306 // minimum button width (regardless of text size)
29307 minButtonWidth: 30,
29308
29309 // buttons rect radii
29310 rx: 3,
29311 ry: 3,
29312
29313 // light fraction used to compute the 'activecolor' default
29314 lightAmount: 25,
29315 darkAmount: 10
29316};
29317
29318},{}],117:[function(_dereq_,module,exports){
29319/**
29320* Copyright 2012-2020, Plotly, Inc.
29321* All rights reserved.
29322*
29323* This source code is licensed under the MIT license found in the
29324* LICENSE file in the root directory of this source tree.
29325*/
29326
29327'use strict';
29328
29329var Lib = _dereq_('../../lib');
29330var Color = _dereq_('../color');
29331var Template = _dereq_('../../plot_api/plot_template');
29332var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
29333
29334var attributes = _dereq_('./attributes');
29335var constants = _dereq_('./constants');
29336
29337
29338module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) {
29339 var selectorIn = containerIn.rangeselector || {};
29340 var selectorOut = Template.newContainer(containerOut, 'rangeselector');
29341
29342 function coerce(attr, dflt) {
29343 return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt);
29344 }
29345
29346 var buttons = handleArrayContainerDefaults(selectorIn, selectorOut, {
29347 name: 'buttons',
29348 handleItemDefaults: buttonDefaults,
29349 calendar: calendar
29350 });
29351
29352 var visible = coerce('visible', buttons.length > 0);
29353 if(visible) {
29354 var posDflt = getPosDflt(containerOut, layout, counterAxes);
29355 coerce('x', posDflt[0]);
29356 coerce('y', posDflt[1]);
29357 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
29358
29359 coerce('xanchor');
29360 coerce('yanchor');
29361
29362 Lib.coerceFont(coerce, 'font', layout.font);
29363
29364 var bgColor = coerce('bgcolor');
29365 coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount));
29366 coerce('bordercolor');
29367 coerce('borderwidth');
29368 }
29369};
29370
29371function buttonDefaults(buttonIn, buttonOut, selectorOut, opts) {
29372 var calendar = opts.calendar;
29373
29374 function coerce(attr, dflt) {
29375 return Lib.coerce(buttonIn, buttonOut, attributes.buttons, attr, dflt);
29376 }
29377
29378 var visible = coerce('visible');
29379
29380 if(visible) {
29381 var step = coerce('step');
29382 if(step !== 'all') {
29383 if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) {
29384 buttonOut.stepmode = 'backward';
29385 } else {
29386 coerce('stepmode');
29387 }
29388
29389 coerce('count');
29390 }
29391
29392 coerce('label');
29393 }
29394}
29395
29396function getPosDflt(containerOut, layout, counterAxes) {
29397 var anchoredList = counterAxes.filter(function(ax) {
29398 return layout[ax].anchor === containerOut._id;
29399 });
29400
29401 var posY = 0;
29402 for(var i = 0; i < anchoredList.length; i++) {
29403 var domain = layout[anchoredList[i]].domain;
29404 if(domain) posY = Math.max(domain[1], posY);
29405 }
29406
29407 return [containerOut.domain[0], posY + constants.yPad];
29408}
29409
29410},{"../../lib":178,"../../plot_api/plot_template":212,"../../plots/array_container_defaults":218,"../color":52,"./attributes":115,"./constants":116}],118:[function(_dereq_,module,exports){
29411/**
29412* Copyright 2012-2020, Plotly, Inc.
29413* All rights reserved.
29414*
29415* This source code is licensed under the MIT license found in the
29416* LICENSE file in the root directory of this source tree.
29417*/
29418
29419'use strict';
29420
29421var d3 = _dereq_('d3');
29422
29423var Registry = _dereq_('../../registry');
29424var Plots = _dereq_('../../plots/plots');
29425var Color = _dereq_('../color');
29426var Drawing = _dereq_('../drawing');
29427var Lib = _dereq_('../../lib');
29428var svgTextUtils = _dereq_('../../lib/svg_text_utils');
29429var axisIds = _dereq_('../../plots/cartesian/axis_ids');
29430
29431var alignmentConstants = _dereq_('../../constants/alignment');
29432var LINE_SPACING = alignmentConstants.LINE_SPACING;
29433var FROM_TL = alignmentConstants.FROM_TL;
29434var FROM_BR = alignmentConstants.FROM_BR;
29435
29436var constants = _dereq_('./constants');
29437var getUpdateObject = _dereq_('./get_update_object');
29438
29439module.exports = function draw(gd) {
29440 var fullLayout = gd._fullLayout;
29441
29442 var selectors = fullLayout._infolayer.selectAll('.rangeselector')
29443 .data(makeSelectorData(gd), selectorKeyFunc);
29444
29445 selectors.enter().append('g')
29446 .classed('rangeselector', true);
29447
29448 selectors.exit().remove();
29449
29450 selectors.style({
29451 cursor: 'pointer',
29452 'pointer-events': 'all'
29453 });
29454
29455 selectors.each(function(d) {
29456 var selector = d3.select(this);
29457 var axisLayout = d;
29458 var selectorLayout = axisLayout.rangeselector;
29459
29460 var buttons = selector.selectAll('g.button')
29461 .data(Lib.filterVisible(selectorLayout.buttons));
29462
29463 buttons.enter().append('g')
29464 .classed('button', true);
29465
29466 buttons.exit().remove();
29467
29468 buttons.each(function(d) {
29469 var button = d3.select(this);
29470 var update = getUpdateObject(axisLayout, d);
29471
29472 d._isActive = isActive(axisLayout, d, update);
29473
29474 button.call(drawButtonRect, selectorLayout, d);
29475 button.call(drawButtonText, selectorLayout, d, gd);
29476
29477 button.on('click', function() {
29478 if(gd._dragged) return;
29479
29480 Registry.call('_guiRelayout', gd, update);
29481 });
29482
29483 button.on('mouseover', function() {
29484 d._isHovered = true;
29485 button.call(drawButtonRect, selectorLayout, d);
29486 });
29487
29488 button.on('mouseout', function() {
29489 d._isHovered = false;
29490 button.call(drawButtonRect, selectorLayout, d);
29491 });
29492 });
29493
29494 reposition(gd, buttons, selectorLayout, axisLayout._name, selector);
29495 });
29496};
29497
29498function makeSelectorData(gd) {
29499 var axes = axisIds.list(gd, 'x', true);
29500 var data = [];
29501
29502 for(var i = 0; i < axes.length; i++) {
29503 var axis = axes[i];
29504
29505 if(axis.rangeselector && axis.rangeselector.visible) {
29506 data.push(axis);
29507 }
29508 }
29509
29510 return data;
29511}
29512
29513function selectorKeyFunc(d) {
29514 return d._id;
29515}
29516
29517function isActive(axisLayout, opts, update) {
29518 if(opts.step === 'all') {
29519 return axisLayout.autorange === true;
29520 } else {
29521 var keys = Object.keys(update);
29522
29523 return (
29524 axisLayout.range[0] === update[keys[0]] &&
29525 axisLayout.range[1] === update[keys[1]]
29526 );
29527 }
29528}
29529
29530function drawButtonRect(button, selectorLayout, d) {
29531 var rect = Lib.ensureSingle(button, 'rect', 'selector-rect', function(s) {
29532 s.attr('shape-rendering', 'crispEdges');
29533 });
29534
29535 rect.attr({
29536 'rx': constants.rx,
29537 'ry': constants.ry
29538 });
29539
29540 rect.call(Color.stroke, selectorLayout.bordercolor)
29541 .call(Color.fill, getFillColor(selectorLayout, d))
29542 .style('stroke-width', selectorLayout.borderwidth + 'px');
29543}
29544
29545function getFillColor(selectorLayout, d) {
29546 return (d._isActive || d._isHovered) ?
29547 selectorLayout.activecolor :
29548 selectorLayout.bgcolor;
29549}
29550
29551function drawButtonText(button, selectorLayout, d, gd) {
29552 function textLayout(s) {
29553 svgTextUtils.convertToTspans(s, gd);
29554 }
29555
29556 var text = Lib.ensureSingle(button, 'text', 'selector-text', function(s) {
29557 s.classed('user-select-none', true)
29558 .attr('text-anchor', 'middle');
29559 });
29560
29561 text.call(Drawing.font, selectorLayout.font)
29562 .text(getLabel(d, gd._fullLayout._meta))
29563 .call(textLayout);
29564}
29565
29566function getLabel(opts, _meta) {
29567 if(opts.label) {
29568 return _meta ?
29569 Lib.templateString(opts.label, _meta) :
29570 opts.label;
29571 }
29572
29573 if(opts.step === 'all') return 'all';
29574
29575 return opts.count + opts.step.charAt(0);
29576}
29577
29578function reposition(gd, buttons, opts, axName, selector) {
29579 var width = 0;
29580 var height = 0;
29581
29582 var borderWidth = opts.borderwidth;
29583
29584 buttons.each(function() {
29585 var button = d3.select(this);
29586 var text = button.select('.selector-text');
29587
29588 var tHeight = opts.font.size * LINE_SPACING;
29589 var hEff = Math.max(tHeight * svgTextUtils.lineCount(text), 16) + 3;
29590
29591 height = Math.max(height, hEff);
29592 });
29593
29594 buttons.each(function() {
29595 var button = d3.select(this);
29596 var rect = button.select('.selector-rect');
29597 var text = button.select('.selector-text');
29598
29599 var tWidth = text.node() && Drawing.bBox(text.node()).width;
29600 var tHeight = opts.font.size * LINE_SPACING;
29601 var tLines = svgTextUtils.lineCount(text);
29602
29603 var wEff = Math.max(tWidth + 10, constants.minButtonWidth);
29604
29605 // TODO add MathJax support
29606
29607 // TODO add buttongap attribute
29608
29609 button.attr('transform', 'translate(' +
29610 (borderWidth + width) + ',' + borderWidth +
29611 ')');
29612
29613 rect.attr({
29614 x: 0,
29615 y: 0,
29616 width: wEff,
29617 height: height
29618 });
29619
29620 svgTextUtils.positionText(text, wEff / 2,
29621 height / 2 - ((tLines - 1) * tHeight / 2) + 3);
29622
29623 width += wEff + 5;
29624 });
29625
29626 var graphSize = gd._fullLayout._size;
29627 var lx = graphSize.l + graphSize.w * opts.x;
29628 var ly = graphSize.t + graphSize.h * (1 - opts.y);
29629
29630 var xanchor = 'left';
29631 if(Lib.isRightAnchor(opts)) {
29632 lx -= width;
29633 xanchor = 'right';
29634 }
29635 if(Lib.isCenterAnchor(opts)) {
29636 lx -= width / 2;
29637 xanchor = 'center';
29638 }
29639
29640 var yanchor = 'top';
29641 if(Lib.isBottomAnchor(opts)) {
29642 ly -= height;
29643 yanchor = 'bottom';
29644 }
29645 if(Lib.isMiddleAnchor(opts)) {
29646 ly -= height / 2;
29647 yanchor = 'middle';
29648 }
29649
29650 width = Math.ceil(width);
29651 height = Math.ceil(height);
29652 lx = Math.round(lx);
29653 ly = Math.round(ly);
29654
29655 Plots.autoMargin(gd, axName + '-range-selector', {
29656 x: opts.x,
29657 y: opts.y,
29658 l: width * FROM_TL[xanchor],
29659 r: width * FROM_BR[xanchor],
29660 b: height * FROM_BR[yanchor],
29661 t: height * FROM_TL[yanchor]
29662 });
29663
29664 selector.attr('transform', 'translate(' + lx + ',' + ly + ')');
29665}
29666
29667},{"../../constants/alignment":154,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/cartesian/axis_ids":225,"../../plots/plots":256,"../../registry":269,"../color":52,"../drawing":74,"./constants":116,"./get_update_object":119,"d3":16}],119:[function(_dereq_,module,exports){
29668/**
29669* Copyright 2012-2020, Plotly, Inc.
29670* All rights reserved.
29671*
29672* This source code is licensed under the MIT license found in the
29673* LICENSE file in the root directory of this source tree.
29674*/
29675
29676
29677'use strict';
29678
29679var d3 = _dereq_('d3');
29680
29681module.exports = function getUpdateObject(axisLayout, buttonLayout) {
29682 var axName = axisLayout._name;
29683 var update = {};
29684
29685 if(buttonLayout.step === 'all') {
29686 update[axName + '.autorange'] = true;
29687 } else {
29688 var xrange = getXRange(axisLayout, buttonLayout);
29689
29690 update[axName + '.range[0]'] = xrange[0];
29691 update[axName + '.range[1]'] = xrange[1];
29692 }
29693
29694 return update;
29695};
29696
29697function getXRange(axisLayout, buttonLayout) {
29698 var currentRange = axisLayout.range;
29699 var base = new Date(axisLayout.r2l(currentRange[1]));
29700 var step = buttonLayout.step;
29701 var count = buttonLayout.count;
29702 var range0;
29703
29704 switch(buttonLayout.stepmode) {
29705 case 'backward':
29706 range0 = axisLayout.l2r(+d3.time[step].utc.offset(base, -count));
29707 break;
29708
29709 case 'todate':
29710 var base2 = d3.time[step].utc.offset(base, -count);
29711
29712 range0 = axisLayout.l2r(+d3.time[step].utc.ceil(base2));
29713 break;
29714 }
29715
29716 var range1 = currentRange[1];
29717
29718 return [range0, range1];
29719}
29720
29721},{"d3":16}],120:[function(_dereq_,module,exports){
29722/**
29723* Copyright 2012-2020, Plotly, Inc.
29724* All rights reserved.
29725*
29726* This source code is licensed under the MIT license found in the
29727* LICENSE file in the root directory of this source tree.
29728*/
29729
29730'use strict';
29731
29732module.exports = {
29733 moduleType: 'component',
29734 name: 'rangeselector',
29735
29736 schema: {
29737 subplots: {
29738 xaxis: {rangeselector: _dereq_('./attributes')}
29739 }
29740 },
29741
29742 layoutAttributes: _dereq_('./attributes'),
29743 handleDefaults: _dereq_('./defaults'),
29744
29745 draw: _dereq_('./draw')
29746};
29747
29748},{"./attributes":115,"./defaults":117,"./draw":118}],121:[function(_dereq_,module,exports){
29749/**
29750* Copyright 2012-2020, Plotly, Inc.
29751* All rights reserved.
29752*
29753* This source code is licensed under the MIT license found in the
29754* LICENSE file in the root directory of this source tree.
29755*/
29756
29757'use strict';
29758
29759var colorAttributes = _dereq_('../color/attributes');
29760
29761module.exports = {
29762 bgcolor: {
29763 valType: 'color',
29764 dflt: colorAttributes.background,
29765
29766 editType: 'plot',
29767
29768 },
29769 bordercolor: {
29770 valType: 'color',
29771 dflt: colorAttributes.defaultLine,
29772
29773 editType: 'plot',
29774
29775 },
29776 borderwidth: {
29777 valType: 'integer',
29778 dflt: 0,
29779 min: 0,
29780
29781 editType: 'plot',
29782
29783 },
29784 autorange: {
29785 valType: 'boolean',
29786 dflt: true,
29787
29788 editType: 'calc',
29789 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
29790
29791 },
29792 range: {
29793 valType: 'info_array',
29794
29795 items: [
29796 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}},
29797 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}}
29798 ],
29799 editType: 'calc',
29800 impliedEdits: {'autorange': false},
29801
29802 },
29803 thickness: {
29804 valType: 'number',
29805 dflt: 0.15,
29806 min: 0,
29807 max: 1,
29808
29809 editType: 'plot',
29810
29811 },
29812 visible: {
29813 valType: 'boolean',
29814 dflt: true,
29815
29816 editType: 'calc',
29817
29818 },
29819 editType: 'calc'
29820};
29821
29822},{"../color/attributes":51}],122:[function(_dereq_,module,exports){
29823/**
29824* Copyright 2012-2020, Plotly, Inc.
29825* All rights reserved.
29826*
29827* This source code is licensed under the MIT license found in the
29828* LICENSE file in the root directory of this source tree.
29829*/
29830
29831'use strict';
29832
29833var listAxes = _dereq_('../../plots/cartesian/axis_ids').list;
29834var getAutoRange = _dereq_('../../plots/cartesian/autorange').getAutoRange;
29835var constants = _dereq_('./constants');
29836
29837module.exports = function calcAutorange(gd) {
29838 var axes = listAxes(gd, 'x', true);
29839
29840 // Compute new slider range using axis autorange if necessary.
29841 //
29842 // Copy back range to input range slider container to skip
29843 // this step in subsequent draw calls.
29844
29845 for(var i = 0; i < axes.length; i++) {
29846 var ax = axes[i];
29847 var opts = ax[constants.name];
29848
29849 if(opts && opts.visible && opts.autorange) {
29850 opts._input.autorange = true;
29851 opts._input.range = opts.range = getAutoRange(gd, ax);
29852 }
29853 }
29854};
29855
29856},{"../../plots/cartesian/autorange":221,"../../plots/cartesian/axis_ids":225,"./constants":123}],123:[function(_dereq_,module,exports){
29857/**
29858* Copyright 2012-2020, Plotly, Inc.
29859* All rights reserved.
29860*
29861* This source code is licensed under the MIT license found in the
29862* LICENSE file in the root directory of this source tree.
29863*/
29864
29865'use strict';
29866
29867module.exports = {
29868
29869 // attribute container name
29870 name: 'rangeslider',
29871
29872 // class names
29873
29874 containerClassName: 'rangeslider-container',
29875 bgClassName: 'rangeslider-bg',
29876 rangePlotClassName: 'rangeslider-rangeplot',
29877
29878 maskMinClassName: 'rangeslider-mask-min',
29879 maskMaxClassName: 'rangeslider-mask-max',
29880 slideBoxClassName: 'rangeslider-slidebox',
29881
29882 grabberMinClassName: 'rangeslider-grabber-min',
29883 grabAreaMinClassName: 'rangeslider-grabarea-min',
29884 handleMinClassName: 'rangeslider-handle-min',
29885
29886 grabberMaxClassName: 'rangeslider-grabber-max',
29887 grabAreaMaxClassName: 'rangeslider-grabarea-max',
29888 handleMaxClassName: 'rangeslider-handle-max',
29889
29890 maskMinOppAxisClassName: 'rangeslider-mask-min-opp-axis',
29891 maskMaxOppAxisClassName: 'rangeslider-mask-max-opp-axis',
29892
29893 // style constants
29894
29895 maskColor: 'rgba(0,0,0,0.4)',
29896 maskOppAxisColor: 'rgba(0,0,0,0.2)',
29897
29898 slideBoxFill: 'transparent',
29899 slideBoxCursor: 'ew-resize',
29900
29901 grabAreaFill: 'transparent',
29902 grabAreaCursor: 'col-resize',
29903 grabAreaWidth: 10,
29904
29905 handleWidth: 4,
29906 handleRadius: 1,
29907 handleStrokeWidth: 1,
29908
29909 extraPad: 15
29910};
29911
29912},{}],124:[function(_dereq_,module,exports){
29913/**
29914* Copyright 2012-2020, Plotly, Inc.
29915* All rights reserved.
29916*
29917* This source code is licensed under the MIT license found in the
29918* LICENSE file in the root directory of this source tree.
29919*/
29920
29921'use strict';
29922
29923var Lib = _dereq_('../../lib');
29924var Template = _dereq_('../../plot_api/plot_template');
29925var axisIds = _dereq_('../../plots/cartesian/axis_ids');
29926
29927var attributes = _dereq_('./attributes');
29928var oppAxisAttrs = _dereq_('./oppaxis_attributes');
29929
29930module.exports = function handleDefaults(layoutIn, layoutOut, axName) {
29931 var axIn = layoutIn[axName];
29932 var axOut = layoutOut[axName];
29933
29934 if(!(axIn.rangeslider || layoutOut._requestRangeslider[axOut._id])) return;
29935
29936 // not super proud of this (maybe store _ in axis object instead
29937 if(!Lib.isPlainObject(axIn.rangeslider)) {
29938 axIn.rangeslider = {};
29939 }
29940
29941 var containerIn = axIn.rangeslider;
29942 var containerOut = Template.newContainer(axOut, 'rangeslider');
29943
29944 function coerce(attr, dflt) {
29945 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
29946 }
29947
29948 var rangeContainerIn, rangeContainerOut;
29949 function coerceRange(attr, dflt) {
29950 return Lib.coerce(rangeContainerIn, rangeContainerOut, oppAxisAttrs, attr, dflt);
29951 }
29952
29953 var visible = coerce('visible');
29954 if(!visible) return;
29955
29956 coerce('bgcolor', layoutOut.plot_bgcolor);
29957 coerce('bordercolor');
29958 coerce('borderwidth');
29959 coerce('thickness');
29960
29961 coerce('autorange', !axOut.isValidRange(containerIn.range));
29962 coerce('range');
29963
29964 var subplots = layoutOut._subplots;
29965 if(subplots) {
29966 var yIds = subplots.cartesian
29967 .filter(function(subplotId) {
29968 return subplotId.substr(0, subplotId.indexOf('y')) === axisIds.name2id(axName);
29969 })
29970 .map(function(subplotId) {
29971 return subplotId.substr(subplotId.indexOf('y'), subplotId.length);
29972 });
29973 var yNames = Lib.simpleMap(yIds, axisIds.id2name);
29974 for(var i = 0; i < yNames.length; i++) {
29975 var yName = yNames[i];
29976
29977 rangeContainerIn = containerIn[yName] || {};
29978 rangeContainerOut = Template.newContainer(containerOut, yName, 'yaxis');
29979
29980 var yAxOut = layoutOut[yName];
29981
29982 var rangemodeDflt;
29983 if(rangeContainerIn.range && yAxOut.isValidRange(rangeContainerIn.range)) {
29984 rangemodeDflt = 'fixed';
29985 }
29986
29987 var rangeMode = coerceRange('rangemode', rangemodeDflt);
29988 if(rangeMode !== 'match') {
29989 coerceRange('range', yAxOut.range.slice());
29990 }
29991 }
29992 }
29993
29994 // to map back range slider (auto) range
29995 containerOut._input = containerIn;
29996};
29997
29998},{"../../lib":178,"../../plot_api/plot_template":212,"../../plots/cartesian/axis_ids":225,"./attributes":121,"./oppaxis_attributes":128}],125:[function(_dereq_,module,exports){
29999/**
30000* Copyright 2012-2020, Plotly, Inc.
30001* All rights reserved.
30002*
30003* This source code is licensed under the MIT license found in the
30004* LICENSE file in the root directory of this source tree.
30005*/
30006
30007'use strict';
30008
30009var d3 = _dereq_('d3');
30010
30011var Registry = _dereq_('../../registry');
30012var Plots = _dereq_('../../plots/plots');
30013
30014var Lib = _dereq_('../../lib');
30015var Drawing = _dereq_('../drawing');
30016var Color = _dereq_('../color');
30017var Titles = _dereq_('../titles');
30018
30019var Cartesian = _dereq_('../../plots/cartesian');
30020var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
30021
30022var dragElement = _dereq_('../dragelement');
30023var setCursor = _dereq_('../../lib/setcursor');
30024
30025var constants = _dereq_('./constants');
30026
30027module.exports = function(gd) {
30028 var fullLayout = gd._fullLayout;
30029 var rangeSliderData = fullLayout._rangeSliderData;
30030 for(var i = 0; i < rangeSliderData.length; i++) {
30031 var opts = rangeSliderData[i][constants.name];
30032 // fullLayout._uid may not exist when we call makeData
30033 opts._clipId = opts._id + '-' + fullLayout._uid;
30034 }
30035
30036 /*
30037 * <g container />
30038 * <rect bg />
30039 * < .... range plot />
30040 * <rect mask-min />
30041 * <rect mask-max />
30042 * <rect slidebox />
30043 * <g grabber-min />
30044 * <rect handle-min />
30045 * <rect grabare-min />
30046 * <g grabber-max />
30047 * <rect handle-max />
30048 * <rect grabare-max />
30049 *
30050 * ...
30051 */
30052
30053 function keyFunction(axisOpts) {
30054 return axisOpts._name;
30055 }
30056
30057 var rangeSliders = fullLayout._infolayer
30058 .selectAll('g.' + constants.containerClassName)
30059 .data(rangeSliderData, keyFunction);
30060
30061 // remove exiting sliders and their corresponding clip paths
30062 rangeSliders.exit().each(function(axisOpts) {
30063 var opts = axisOpts[constants.name];
30064 fullLayout._topdefs.select('#' + opts._clipId).remove();
30065 }).remove();
30066
30067 // return early if no range slider is visible
30068 if(rangeSliderData.length === 0) return;
30069
30070 rangeSliders.enter().append('g')
30071 .classed(constants.containerClassName, true)
30072 .attr('pointer-events', 'all');
30073
30074 // for all present range sliders
30075 rangeSliders.each(function(axisOpts) {
30076 var rangeSlider = d3.select(this);
30077 var opts = axisOpts[constants.name];
30078 var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)];
30079 var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)];
30080
30081 // update range
30082 // Expand slider range to the axis range
30083 if(opts.range) {
30084 var rng = Lib.simpleMap(opts.range, axisOpts.r2l);
30085 var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l);
30086 var newRng;
30087
30088 if(axRng[0] < axRng[1]) {
30089 newRng = [
30090 Math.min(rng[0], axRng[0]),
30091 Math.max(rng[1], axRng[1])
30092 ];
30093 } else {
30094 newRng = [
30095 Math.max(rng[0], axRng[0]),
30096 Math.min(rng[1], axRng[1])
30097 ];
30098 }
30099
30100 opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r);
30101 }
30102
30103 axisOpts.cleanRange('rangeslider.range');
30104
30105 // update range slider dimensions
30106
30107 var gs = fullLayout._size;
30108 var domain = axisOpts.domain;
30109
30110 opts._width = gs.w * (domain[1] - domain[0]);
30111
30112 var x = Math.round(gs.l + (gs.w * domain[0]));
30113
30114 var y = Math.round(
30115 gs.t + gs.h * (1 - axisOpts._counterDomainMin) +
30116 (axisOpts.side === 'bottom' ? axisOpts._depth : 0) +
30117 opts._offsetShift + constants.extraPad
30118 );
30119
30120 rangeSlider.attr('transform', 'translate(' + x + ',' + y + ')');
30121
30122 // update data <--> pixel coordinate conversion methods
30123
30124 opts._rl = Lib.simpleMap(opts.range, axisOpts.r2l);
30125 var rl0 = opts._rl[0];
30126 var rl1 = opts._rl[1];
30127 var drl = rl1 - rl0;
30128
30129 opts.p2d = function(v) {
30130 return (v / opts._width) * drl + rl0;
30131 };
30132
30133 opts.d2p = function(v) {
30134 return (v - rl0) / drl * opts._width;
30135 };
30136
30137 if(axisOpts.rangebreaks) {
30138 var rsBreaks = axisOpts.locateBreaks(rl0, rl1);
30139
30140 if(rsBreaks.length) {
30141 var j, brk;
30142
30143 var lBreaks = 0;
30144 for(j = 0; j < rsBreaks.length; j++) {
30145 brk = rsBreaks[j];
30146 lBreaks += (brk.max - brk.min);
30147 }
30148
30149 // TODO fix for reversed-range axes !!!
30150
30151 // compute slope and piecewise offsets
30152 var m2 = opts._width / (rl1 - rl0 - lBreaks);
30153 var _B = [-m2 * rl0];
30154 for(j = 0; j < rsBreaks.length; j++) {
30155 brk = rsBreaks[j];
30156 _B.push(_B[_B.length - 1] - m2 * (brk.max - brk.min));
30157 }
30158
30159 opts.d2p = function(v) {
30160 var b = _B[0];
30161 for(var j = 0; j < rsBreaks.length; j++) {
30162 var brk = rsBreaks[j];
30163 if(v >= brk.max) b = _B[j + 1];
30164 else if(v < brk.min) break;
30165 }
30166 return b + m2 * v;
30167 };
30168
30169 // fill pixel (i.e. 'p') min/max here,
30170 // to not have to loop through the _rangebreaks twice during `p2d`
30171 for(j = 0; j < rsBreaks.length; j++) {
30172 brk = rsBreaks[j];
30173 brk.pmin = opts.d2p(brk.min);
30174 brk.pmax = opts.d2p(brk.max);
30175 }
30176
30177 opts.p2d = function(v) {
30178 var b = _B[0];
30179 for(var j = 0; j < rsBreaks.length; j++) {
30180 var brk = rsBreaks[j];
30181 if(v >= brk.pmax) b = _B[j + 1];
30182 else if(v < brk.pmin) break;
30183 }
30184 return (v - b) / m2;
30185 };
30186 }
30187 }
30188
30189 if(oppAxisRangeOpts.rangemode !== 'match') {
30190 var range0OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[0]);
30191 var range1OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[1]);
30192 var distOppAxis = range1OppAxis - range0OppAxis;
30193
30194 opts.d2pOppAxis = function(v) {
30195 return (v - range0OppAxis) / distOppAxis * opts._height;
30196 };
30197 }
30198
30199 // update inner nodes
30200
30201 rangeSlider
30202 .call(drawBg, gd, axisOpts, opts)
30203 .call(addClipPath, gd, axisOpts, opts)
30204 .call(drawRangePlot, gd, axisOpts, opts)
30205 .call(drawMasks, gd, axisOpts, opts, oppAxisRangeOpts)
30206 .call(drawSlideBox, gd, axisOpts, opts)
30207 .call(drawGrabbers, gd, axisOpts, opts);
30208
30209 // setup drag element
30210 setupDragElement(rangeSlider, gd, axisOpts, opts);
30211
30212 // update current range
30213 setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts);
30214
30215 // title goes next to range slider instead of tick labels, so
30216 // just take it over and draw it from here
30217 if(axisOpts.side === 'bottom') {
30218 Titles.draw(gd, axisOpts._id + 'title', {
30219 propContainer: axisOpts,
30220 propName: axisOpts._name + '.title',
30221 placeholder: fullLayout._dfltTitle.x,
30222 attributes: {
30223 x: axisOpts._offset + axisOpts._length / 2,
30224 y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.title.font.size,
30225 'text-anchor': 'middle'
30226 }
30227 });
30228 }
30229 });
30230};
30231
30232function setupDragElement(rangeSlider, gd, axisOpts, opts) {
30233 var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node();
30234 var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node();
30235 var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node();
30236
30237 rangeSlider.on('mousedown', function() {
30238 var event = d3.event;
30239 var target = event.target;
30240 var startX = event.clientX;
30241 var offsetX = startX - rangeSlider.node().getBoundingClientRect().left;
30242 var minVal = opts.d2p(axisOpts._rl[0]);
30243 var maxVal = opts.d2p(axisOpts._rl[1]);
30244
30245 var dragCover = dragElement.coverSlip();
30246
30247 dragCover.addEventListener('mousemove', mouseMove);
30248 dragCover.addEventListener('mouseup', mouseUp);
30249
30250 function mouseMove(e) {
30251 var delta = +e.clientX - startX;
30252 var pixelMin, pixelMax, cursor;
30253
30254 switch(target) {
30255 case slideBox:
30256 cursor = 'ew-resize';
30257 pixelMin = minVal + delta;
30258 pixelMax = maxVal + delta;
30259 break;
30260
30261 case grabAreaMin:
30262 cursor = 'col-resize';
30263 pixelMin = minVal + delta;
30264 pixelMax = maxVal;
30265 break;
30266
30267 case grabAreaMax:
30268 cursor = 'col-resize';
30269 pixelMin = minVal;
30270 pixelMax = maxVal + delta;
30271 break;
30272
30273 default:
30274 cursor = 'ew-resize';
30275 pixelMin = offsetX;
30276 pixelMax = offsetX + delta;
30277 break;
30278 }
30279
30280 if(pixelMax < pixelMin) {
30281 var tmp = pixelMax;
30282 pixelMax = pixelMin;
30283 pixelMin = tmp;
30284 }
30285
30286 opts._pixelMin = pixelMin;
30287 opts._pixelMax = pixelMax;
30288
30289 setCursor(d3.select(dragCover), cursor);
30290 setDataRange(rangeSlider, gd, axisOpts, opts);
30291 }
30292
30293 function mouseUp() {
30294 dragCover.removeEventListener('mousemove', mouseMove);
30295 dragCover.removeEventListener('mouseup', mouseUp);
30296 Lib.removeElement(dragCover);
30297 }
30298 });
30299}
30300
30301function setDataRange(rangeSlider, gd, axisOpts, opts) {
30302 function clamp(v) {
30303 return axisOpts.l2r(Lib.constrain(v, opts._rl[0], opts._rl[1]));
30304 }
30305
30306 var dataMin = clamp(opts.p2d(opts._pixelMin));
30307 var dataMax = clamp(opts.p2d(opts._pixelMax));
30308
30309 window.requestAnimationFrame(function() {
30310 Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
30311 });
30312}
30313
30314function setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts) {
30315 var hw2 = constants.handleWidth / 2;
30316
30317 function clamp(v) {
30318 return Lib.constrain(v, 0, opts._width);
30319 }
30320
30321 function clampOppAxis(v) {
30322 return Lib.constrain(v, 0, opts._height);
30323 }
30324
30325 function clampHandle(v) {
30326 return Lib.constrain(v, -hw2, opts._width + hw2);
30327 }
30328
30329 var pixelMin = clamp(opts.d2p(axisOpts._rl[0]));
30330 var pixelMax = clamp(opts.d2p(axisOpts._rl[1]));
30331
30332 rangeSlider.select('rect.' + constants.slideBoxClassName)
30333 .attr('x', pixelMin)
30334 .attr('width', pixelMax - pixelMin);
30335
30336 rangeSlider.select('rect.' + constants.maskMinClassName)
30337 .attr('width', pixelMin);
30338
30339 rangeSlider.select('rect.' + constants.maskMaxClassName)
30340 .attr('x', pixelMax)
30341 .attr('width', opts._width - pixelMax);
30342
30343 if(oppAxisRangeOpts.rangemode !== 'match') {
30344 var pixelMinOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[1]));
30345 var pixelMaxOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[0]));
30346
30347 rangeSlider.select('rect.' + constants.maskMinOppAxisClassName)
30348 .attr('x', pixelMin)
30349 .attr('height', pixelMinOppAxis)
30350 .attr('width', pixelMax - pixelMin);
30351
30352 rangeSlider.select('rect.' + constants.maskMaxOppAxisClassName)
30353 .attr('x', pixelMin)
30354 .attr('y', pixelMaxOppAxis)
30355 .attr('height', opts._height - pixelMaxOppAxis)
30356 .attr('width', pixelMax - pixelMin);
30357
30358 rangeSlider.select('rect.' + constants.slideBoxClassName)
30359 .attr('y', pixelMinOppAxis)
30360 .attr('height', pixelMaxOppAxis - pixelMinOppAxis);
30361 }
30362
30363 // add offset for crispier corners
30364 // https://github.com/plotly/plotly.js/pull/1409
30365 var offset = 0.5;
30366
30367 var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset;
30368 var xMax = Math.round(clampHandle(pixelMax - hw2)) + offset;
30369
30370 rangeSlider.select('g.' + constants.grabberMinClassName)
30371 .attr('transform', 'translate(' + xMin + ',' + offset + ')');
30372
30373 rangeSlider.select('g.' + constants.grabberMaxClassName)
30374 .attr('transform', 'translate(' + xMax + ',' + offset + ')');
30375}
30376
30377function drawBg(rangeSlider, gd, axisOpts, opts) {
30378 var bg = Lib.ensureSingle(rangeSlider, 'rect', constants.bgClassName, function(s) {
30379 s.attr({
30380 x: 0,
30381 y: 0,
30382 'shape-rendering': 'crispEdges'
30383 });
30384 });
30385
30386 var borderCorrect = (opts.borderwidth % 2) === 0 ?
30387 opts.borderwidth :
30388 opts.borderwidth - 1;
30389
30390 var offsetShift = -opts._offsetShift;
30391 var lw = Drawing.crispRound(gd, opts.borderwidth);
30392
30393 bg.attr({
30394 width: opts._width + borderCorrect,
30395 height: opts._height + borderCorrect,
30396 transform: 'translate(' + offsetShift + ',' + offsetShift + ')',
30397 fill: opts.bgcolor,
30398 stroke: opts.bordercolor,
30399 'stroke-width': lw
30400 });
30401}
30402
30403function addClipPath(rangeSlider, gd, axisOpts, opts) {
30404 var fullLayout = gd._fullLayout;
30405
30406 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', opts._clipId, function(s) {
30407 s.append('rect').attr({ x: 0, y: 0 });
30408 });
30409
30410 clipPath.select('rect').attr({
30411 width: opts._width,
30412 height: opts._height
30413 });
30414}
30415
30416function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
30417 var calcData = gd.calcdata;
30418
30419 var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName)
30420 .data(axisOpts._subplotsWith, Lib.identity);
30421
30422 rangePlots.enter().append('g')
30423 .attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
30424 .call(Drawing.setClipUrl, opts._clipId, gd);
30425
30426 rangePlots.order();
30427
30428 rangePlots.exit().remove();
30429
30430 var mainplotinfo;
30431
30432 rangePlots.each(function(id, i) {
30433 var plotgroup = d3.select(this);
30434 var isMainPlot = (i === 0);
30435
30436 var oppAxisOpts = axisIDs.getFromId(gd, id, 'y');
30437 var oppAxisName = oppAxisOpts._name;
30438 var oppAxisRangeOpts = opts[oppAxisName];
30439
30440 var mockFigure = {
30441 data: [],
30442 layout: {
30443 xaxis: {
30444 type: axisOpts.type,
30445 domain: [0, 1],
30446 range: opts.range.slice(),
30447 calendar: axisOpts.calendar
30448 },
30449 width: opts._width,
30450 height: opts._height,
30451 margin: { t: 0, b: 0, l: 0, r: 0 }
30452 },
30453 _context: gd._context
30454 };
30455
30456 if(axisOpts.rangebreaks) {
30457 mockFigure.layout.xaxis.rangebreaks = axisOpts.rangebreaks;
30458 }
30459
30460 mockFigure.layout[oppAxisName] = {
30461 type: oppAxisOpts.type,
30462 domain: [0, 1],
30463 range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(),
30464 calendar: oppAxisOpts.calendar
30465 };
30466
30467 if(oppAxisOpts.rangebreaks) {
30468 mockFigure.layout[oppAxisName].rangebreaks = oppAxisOpts.rangebreaks;
30469 }
30470
30471 Plots.supplyDefaults(mockFigure);
30472
30473 var xa = mockFigure._fullLayout.xaxis;
30474 var ya = mockFigure._fullLayout[oppAxisName];
30475
30476 xa.clearCalc();
30477 xa.setScale();
30478 ya.clearCalc();
30479 ya.setScale();
30480
30481 var plotinfo = {
30482 id: id,
30483 plotgroup: plotgroup,
30484 xaxis: xa,
30485 yaxis: ya,
30486 isRangePlot: true
30487 };
30488
30489 if(isMainPlot) mainplotinfo = plotinfo;
30490 else {
30491 plotinfo.mainplot = 'xy';
30492 plotinfo.mainplotinfo = mainplotinfo;
30493 }
30494
30495 Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id));
30496 });
30497}
30498
30499function filterRangePlotCalcData(calcData, subplotId) {
30500 var out = [];
30501
30502 for(var i = 0; i < calcData.length; i++) {
30503 var calcTrace = calcData[i];
30504 var trace = calcTrace[0].trace;
30505
30506 if(trace.xaxis + trace.yaxis === subplotId) {
30507 out.push(calcTrace);
30508 }
30509 }
30510
30511 return out;
30512}
30513
30514function drawMasks(rangeSlider, gd, axisOpts, opts, oppAxisRangeOpts) {
30515 var maskMin = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinClassName, function(s) {
30516 s.attr({
30517 x: 0,
30518 y: 0,
30519 'shape-rendering': 'crispEdges'
30520 });
30521 });
30522
30523 maskMin
30524 .attr('height', opts._height)
30525 .call(Color.fill, constants.maskColor);
30526
30527 var maskMax = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxClassName, function(s) {
30528 s.attr({
30529 y: 0,
30530 'shape-rendering': 'crispEdges'
30531 });
30532 });
30533
30534 maskMax
30535 .attr('height', opts._height)
30536 .call(Color.fill, constants.maskColor);
30537
30538 // masks used for oppAxis zoom
30539 if(oppAxisRangeOpts.rangemode !== 'match') {
30540 var maskMinOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinOppAxisClassName, function(s) {
30541 s.attr({
30542 y: 0,
30543 'shape-rendering': 'crispEdges'
30544 });
30545 });
30546
30547 maskMinOppAxis
30548 .attr('width', opts._width)
30549 .call(Color.fill, constants.maskOppAxisColor);
30550
30551 var maskMaxOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxOppAxisClassName, function(s) {
30552 s.attr({
30553 y: 0,
30554 'shape-rendering': 'crispEdges'
30555 });
30556 });
30557
30558 maskMaxOppAxis
30559 .attr('width', opts._width)
30560 .style('border-top', constants.maskOppBorder)
30561 .call(Color.fill, constants.maskOppAxisColor);
30562 }
30563}
30564
30565function drawSlideBox(rangeSlider, gd, axisOpts, opts) {
30566 if(gd._context.staticPlot) return;
30567
30568 var slideBox = Lib.ensureSingle(rangeSlider, 'rect', constants.slideBoxClassName, function(s) {
30569 s.attr({
30570 y: 0,
30571 cursor: constants.slideBoxCursor,
30572 'shape-rendering': 'crispEdges'
30573 });
30574 });
30575
30576 slideBox.attr({
30577 height: opts._height,
30578 fill: constants.slideBoxFill
30579 });
30580}
30581
30582function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
30583 // <g grabber />
30584 var grabberMin = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMinClassName);
30585 var grabberMax = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMaxClassName);
30586
30587 // <g handle />
30588 var handleFixAttrs = {
30589 x: 0,
30590 width: constants.handleWidth,
30591 rx: constants.handleRadius,
30592 fill: Color.background,
30593 stroke: Color.defaultLine,
30594 'stroke-width': constants.handleStrokeWidth,
30595 'shape-rendering': 'crispEdges'
30596 };
30597 var handleDynamicAttrs = {
30598 y: Math.round(opts._height / 4),
30599 height: Math.round(opts._height / 2),
30600 };
30601 var handleMin = Lib.ensureSingle(grabberMin, 'rect', constants.handleMinClassName, function(s) {
30602 s.attr(handleFixAttrs);
30603 });
30604 handleMin.attr(handleDynamicAttrs);
30605
30606 var handleMax = Lib.ensureSingle(grabberMax, 'rect', constants.handleMaxClassName, function(s) {
30607 s.attr(handleFixAttrs);
30608 });
30609 handleMax.attr(handleDynamicAttrs);
30610
30611 // <g grabarea />
30612 if(gd._context.staticPlot) return;
30613
30614 var grabAreaFixAttrs = {
30615 width: constants.grabAreaWidth,
30616 x: 0,
30617 y: 0,
30618 fill: constants.grabAreaFill,
30619 cursor: constants.grabAreaCursor
30620 };
30621
30622 var grabAreaMin = Lib.ensureSingle(grabberMin, 'rect', constants.grabAreaMinClassName, function(s) {
30623 s.attr(grabAreaFixAttrs);
30624 });
30625 grabAreaMin.attr('height', opts._height);
30626
30627 var grabAreaMax = Lib.ensureSingle(grabberMax, 'rect', constants.grabAreaMaxClassName, function(s) {
30628 s.attr(grabAreaFixAttrs);
30629 });
30630 grabAreaMax.attr('height', opts._height);
30631}
30632
30633},{"../../lib":178,"../../lib/setcursor":197,"../../plots/cartesian":235,"../../plots/cartesian/axis_ids":225,"../../plots/plots":256,"../../registry":269,"../color":52,"../dragelement":71,"../drawing":74,"../titles":147,"./constants":123,"d3":16}],126:[function(_dereq_,module,exports){
30634/**
30635* Copyright 2012-2020, Plotly, Inc.
30636* All rights reserved.
30637*
30638* This source code is licensed under the MIT license found in the
30639* LICENSE file in the root directory of this source tree.
30640*/
30641
30642'use strict';
30643
30644var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
30645var svgTextUtils = _dereq_('../../lib/svg_text_utils');
30646var constants = _dereq_('./constants');
30647var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
30648var name = constants.name;
30649
30650function isVisible(ax) {
30651 var rangeSlider = ax && ax[name];
30652 return rangeSlider && rangeSlider.visible;
30653}
30654exports.isVisible = isVisible;
30655
30656exports.makeData = function(fullLayout) {
30657 var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true);
30658 var margin = fullLayout.margin;
30659 var rangeSliderData = [];
30660
30661 if(!fullLayout._has('gl2d')) {
30662 for(var i = 0; i < axes.length; i++) {
30663 var ax = axes[i];
30664
30665 if(isVisible(ax)) {
30666 rangeSliderData.push(ax);
30667
30668 var opts = ax[name];
30669 opts._id = name + ax._id;
30670 opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
30671 opts._offsetShift = Math.floor(opts.borderwidth / 2);
30672 }
30673 }
30674 }
30675
30676 fullLayout._rangeSliderData = rangeSliderData;
30677};
30678
30679exports.autoMarginOpts = function(gd, ax) {
30680 var fullLayout = gd._fullLayout;
30681 var opts = ax[name];
30682 var axLetter = ax._id.charAt(0);
30683
30684 var bottomDepth = 0;
30685 var titleHeight = 0;
30686 if(ax.side === 'bottom') {
30687 bottomDepth = ax._depth;
30688 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
30689 // as in rangeslider/draw.js
30690 titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift;
30691 // multi-line extra bump
30692 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
30693 titleHeight += extraLines * ax.title.font.size * LINE_SPACING;
30694 }
30695 }
30696
30697 return {
30698 x: 0,
30699 y: ax._counterDomainMin,
30700 l: 0,
30701 r: 0,
30702 t: 0,
30703 b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight),
30704 pad: constants.extraPad + opts._offsetShift * 2
30705 };
30706};
30707
30708},{"../../constants/alignment":154,"../../lib/svg_text_utils":199,"../../plots/cartesian/axis_ids":225,"./constants":123}],127:[function(_dereq_,module,exports){
30709/**
30710* Copyright 2012-2020, Plotly, Inc.
30711* All rights reserved.
30712*
30713* This source code is licensed under the MIT license found in the
30714* LICENSE file in the root directory of this source tree.
30715*/
30716
30717'use strict';
30718
30719var Lib = _dereq_('../../lib');
30720var attrs = _dereq_('./attributes');
30721var oppAxisAttrs = _dereq_('./oppaxis_attributes');
30722var helpers = _dereq_('./helpers');
30723
30724module.exports = {
30725 moduleType: 'component',
30726 name: 'rangeslider',
30727
30728 schema: {
30729 subplots: {
30730 xaxis: {
30731 rangeslider: Lib.extendFlat({}, attrs, {
30732 yaxis: oppAxisAttrs
30733 })
30734 }
30735 }
30736 },
30737
30738 layoutAttributes: _dereq_('./attributes'),
30739 handleDefaults: _dereq_('./defaults'),
30740 calcAutorange: _dereq_('./calc_autorange'),
30741 draw: _dereq_('./draw'),
30742 isVisible: helpers.isVisible,
30743 makeData: helpers.makeData,
30744 autoMarginOpts: helpers.autoMarginOpts
30745};
30746
30747},{"../../lib":178,"./attributes":121,"./calc_autorange":122,"./defaults":124,"./draw":125,"./helpers":126,"./oppaxis_attributes":128}],128:[function(_dereq_,module,exports){
30748/**
30749* Copyright 2012-2020, Plotly, Inc.
30750* All rights reserved.
30751*
30752* This source code is licensed under the MIT license found in the
30753* LICENSE file in the root directory of this source tree.
30754*/
30755
30756'use strict';
30757
30758module.exports = {
30759 // not really a 'subplot' attribute container,
30760 // but this is the flag we use to denote attributes that
30761 // support yaxis, yaxis2, yaxis3, ... counters
30762 _isSubplotObj: true,
30763
30764 rangemode: {
30765 valType: 'enumerated',
30766 values: ['auto', 'fixed', 'match'],
30767 dflt: 'match',
30768
30769 editType: 'calc',
30770
30771 },
30772 range: {
30773 valType: 'info_array',
30774
30775 items: [
30776 {valType: 'any', editType: 'plot'},
30777 {valType: 'any', editType: 'plot'}
30778 ],
30779 editType: 'plot',
30780
30781 },
30782 editType: 'calc'
30783};
30784
30785},{}],129:[function(_dereq_,module,exports){
30786/**
30787* Copyright 2012-2020, Plotly, Inc.
30788* All rights reserved.
30789*
30790* This source code is licensed under the MIT license found in the
30791* LICENSE file in the root directory of this source tree.
30792*/
30793
30794'use strict';
30795
30796var annAttrs = _dereq_('../annotations/attributes');
30797var scatterLineAttrs = _dereq_('../../traces/scatter/attributes').line;
30798var dash = _dereq_('../drawing/attributes').dash;
30799var extendFlat = _dereq_('../../lib/extend').extendFlat;
30800var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
30801
30802module.exports = templatedArray('shape', {
30803 visible: {
30804 valType: 'boolean',
30805
30806 dflt: true,
30807 editType: 'calc+arraydraw',
30808
30809 },
30810
30811 type: {
30812 valType: 'enumerated',
30813 values: ['circle', 'rect', 'path', 'line'],
30814
30815 editType: 'calc+arraydraw',
30816
30817 },
30818
30819 layer: {
30820 valType: 'enumerated',
30821 values: ['below', 'above'],
30822 dflt: 'above',
30823
30824 editType: 'arraydraw',
30825
30826 },
30827
30828 xref: extendFlat({}, annAttrs.xref, {
30829
30830 }),
30831 xsizemode: {
30832 valType: 'enumerated',
30833 values: ['scaled', 'pixel'],
30834 dflt: 'scaled',
30835
30836 editType: 'calc+arraydraw',
30837
30838 },
30839 xanchor: {
30840 valType: 'any',
30841
30842 editType: 'calc+arraydraw',
30843
30844 },
30845 x0: {
30846 valType: 'any',
30847
30848 editType: 'calc+arraydraw',
30849
30850 },
30851 x1: {
30852 valType: 'any',
30853
30854 editType: 'calc+arraydraw',
30855
30856 },
30857
30858 yref: extendFlat({}, annAttrs.yref, {
30859
30860 }),
30861 ysizemode: {
30862 valType: 'enumerated',
30863 values: ['scaled', 'pixel'],
30864 dflt: 'scaled',
30865
30866 editType: 'calc+arraydraw',
30867
30868 },
30869 yanchor: {
30870 valType: 'any',
30871
30872 editType: 'calc+arraydraw',
30873
30874 },
30875 y0: {
30876 valType: 'any',
30877
30878 editType: 'calc+arraydraw',
30879
30880 },
30881 y1: {
30882 valType: 'any',
30883
30884 editType: 'calc+arraydraw',
30885
30886 },
30887
30888 path: {
30889 valType: 'string',
30890
30891 editType: 'calc+arraydraw',
30892
30893 },
30894
30895 opacity: {
30896 valType: 'number',
30897 min: 0,
30898 max: 1,
30899 dflt: 1,
30900
30901 editType: 'arraydraw',
30902
30903 },
30904 line: {
30905 color: extendFlat({}, scatterLineAttrs.color, {editType: 'arraydraw'}),
30906 width: extendFlat({}, scatterLineAttrs.width, {editType: 'calc+arraydraw'}),
30907 dash: extendFlat({}, dash, {editType: 'arraydraw'}),
30908
30909 editType: 'calc+arraydraw'
30910 },
30911 fillcolor: {
30912 valType: 'color',
30913 dflt: 'rgba(0,0,0,0)',
30914
30915 editType: 'arraydraw',
30916
30917 },
30918 fillrule: {
30919 valType: 'enumerated',
30920 values: ['evenodd', 'nonzero'],
30921 dflt: 'evenodd',
30922
30923 editType: 'arraydraw',
30924
30925 },
30926 editable: {
30927 valType: 'boolean',
30928
30929 dflt: false,
30930 editType: 'calc+arraydraw',
30931
30932 },
30933
30934 editType: 'arraydraw'
30935});
30936
30937},{"../../lib/extend":173,"../../plot_api/plot_template":212,"../../traces/scatter/attributes":389,"../annotations/attributes":37,"../drawing/attributes":73}],130:[function(_dereq_,module,exports){
30938/**
30939* Copyright 2012-2020, Plotly, Inc.
30940* All rights reserved.
30941*
30942* This source code is licensed under the MIT license found in the
30943* LICENSE file in the root directory of this source tree.
30944*/
30945
30946'use strict';
30947
30948var Lib = _dereq_('../../lib');
30949var Axes = _dereq_('../../plots/cartesian/axes');
30950
30951var constants = _dereq_('./constants');
30952var helpers = _dereq_('./helpers');
30953
30954
30955module.exports = function calcAutorange(gd) {
30956 var fullLayout = gd._fullLayout;
30957 var shapeList = Lib.filterVisible(fullLayout.shapes);
30958
30959 if(!shapeList.length || !gd._fullData.length) return;
30960
30961 for(var i = 0; i < shapeList.length; i++) {
30962 var shape = shapeList[i];
30963 shape._extremes = {};
30964
30965 var ax, bounds;
30966
30967 if(shape.xref !== 'paper') {
30968 var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0;
30969 var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1;
30970 ax = Axes.getFromId(gd, shape.xref);
30971
30972 bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX);
30973 if(bounds) {
30974 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape));
30975 }
30976 }
30977
30978 if(shape.yref !== 'paper') {
30979 var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0;
30980 var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1;
30981 ax = Axes.getFromId(gd, shape.yref);
30982
30983 bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY);
30984 if(bounds) {
30985 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape));
30986 }
30987 }
30988 }
30989};
30990
30991function calcXPaddingOptions(shape) {
30992 return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false);
30993}
30994
30995function calcYPaddingOptions(shape) {
30996 return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true);
30997}
30998
30999function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
31000 var ppad = lineWidth / 2;
31001 var axisDirectionReverted = isYAxis;
31002
31003 if(sizeMode === 'pixel') {
31004 var coords = path ?
31005 helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) :
31006 [v0, v1];
31007 var maxValue = Lib.aggNums(Math.max, null, coords);
31008 var minValue = Lib.aggNums(Math.min, null, coords);
31009 var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad;
31010 var afterPad = maxValue > 0 ? maxValue + ppad : ppad;
31011
31012 return {
31013 ppad: ppad,
31014 ppadplus: axisDirectionReverted ? beforePad : afterPad,
31015 ppadminus: axisDirectionReverted ? afterPad : beforePad
31016 };
31017 } else {
31018 return {ppad: ppad};
31019 }
31020}
31021
31022function shapeBounds(ax, v0, v1, path, paramsToUse) {
31023 var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c;
31024
31025 if(v0 !== undefined) return [convertVal(v0), convertVal(v1)];
31026 if(!path) return;
31027
31028 var min = Infinity;
31029 var max = -Infinity;
31030 var segments = path.match(constants.segmentRE);
31031 var i;
31032 var segment;
31033 var drawnParam;
31034 var params;
31035 var val;
31036
31037 if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal);
31038
31039 for(i = 0; i < segments.length; i++) {
31040 segment = segments[i];
31041 drawnParam = paramsToUse[segment.charAt(0)].drawn;
31042 if(drawnParam === undefined) continue;
31043
31044 params = segments[i].substr(1).match(constants.paramRE);
31045 if(!params || params.length < drawnParam) continue;
31046
31047 val = convertVal(params[drawnParam]);
31048 if(val < min) min = val;
31049 if(val > max) max = val;
31050 }
31051 if(max >= min) return [min, max];
31052}
31053
31054},{"../../lib":178,"../../plots/cartesian/axes":222,"./constants":131,"./helpers":140}],131:[function(_dereq_,module,exports){
31055/**
31056* Copyright 2012-2020, Plotly, Inc.
31057* All rights reserved.
31058*
31059* This source code is licensed under the MIT license found in the
31060* LICENSE file in the root directory of this source tree.
31061*/
31062
31063
31064'use strict';
31065
31066
31067module.exports = {
31068 segmentRE: /[MLHVQCTSZ][^MLHVQCTSZ]*/g,
31069 paramRE: /[^\s,]+/g,
31070
31071 // which numbers in each path segment are x (or y) values
31072 // drawn is which param is a drawn point, as opposed to a
31073 // control point (which doesn't count toward autorange.
31074 // TODO: this means curved paths could extend beyond the
31075 // autorange bounds. This is a bit tricky to get right
31076 // unless we revert to bounding boxes, but perhaps there's
31077 // a calculation we could do...)
31078 paramIsX: {
31079 M: {0: true, drawn: 0},
31080 L: {0: true, drawn: 0},
31081 H: {0: true, drawn: 0},
31082 V: {},
31083 Q: {0: true, 2: true, drawn: 2},
31084 C: {0: true, 2: true, 4: true, drawn: 4},
31085 T: {0: true, drawn: 0},
31086 S: {0: true, 2: true, drawn: 2},
31087 // A: {0: true, 5: true},
31088 Z: {}
31089 },
31090
31091 paramIsY: {
31092 M: {1: true, drawn: 1},
31093 L: {1: true, drawn: 1},
31094 H: {},
31095 V: {0: true, drawn: 0},
31096 Q: {1: true, 3: true, drawn: 3},
31097 C: {1: true, 3: true, 5: true, drawn: 5},
31098 T: {1: true, drawn: 1},
31099 S: {1: true, 3: true, drawn: 5},
31100 // A: {1: true, 6: true},
31101 Z: {}
31102 },
31103
31104 numParams: {
31105 M: 2,
31106 L: 2,
31107 H: 1,
31108 V: 1,
31109 Q: 4,
31110 C: 6,
31111 T: 2,
31112 S: 4,
31113 // A: 7,
31114 Z: 0
31115 }
31116};
31117
31118},{}],132:[function(_dereq_,module,exports){
31119/**
31120* Copyright 2012-2020, Plotly, Inc.
31121* All rights reserved.
31122*
31123* This source code is licensed under the MIT license found in the
31124* LICENSE file in the root directory of this source tree.
31125*/
31126
31127
31128'use strict';
31129
31130var Lib = _dereq_('../../lib');
31131var Axes = _dereq_('../../plots/cartesian/axes');
31132var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
31133
31134var attributes = _dereq_('./attributes');
31135var helpers = _dereq_('./helpers');
31136
31137
31138module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
31139 handleArrayContainerDefaults(layoutIn, layoutOut, {
31140 name: 'shapes',
31141 handleItemDefaults: handleShapeDefaults
31142 });
31143};
31144
31145function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
31146 function coerce(attr, dflt) {
31147 return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
31148 }
31149
31150 var visible = coerce('visible');
31151 if(!visible) return;
31152
31153 var path = coerce('path');
31154 var dfltType = path ? 'path' : 'rect';
31155 var shapeType = coerce('type', dfltType);
31156 if(shapeOut.type !== 'path') delete shapeOut.path;
31157
31158 coerce('editable');
31159 coerce('layer');
31160 coerce('opacity');
31161 coerce('fillcolor');
31162 coerce('fillrule');
31163 var lineWidth = coerce('line.width');
31164 if(lineWidth) {
31165 coerce('line.color');
31166 coerce('line.dash');
31167 }
31168
31169 var xSizeMode = coerce('xsizemode');
31170 var ySizeMode = coerce('ysizemode');
31171
31172 // positioning
31173 var axLetters = ['x', 'y'];
31174 for(var i = 0; i < 2; i++) {
31175 var axLetter = axLetters[i];
31176 var attrAnchor = axLetter + 'anchor';
31177 var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
31178 var gdMock = {_fullLayout: fullLayout};
31179 var ax;
31180 var pos2r;
31181 var r2pos;
31182
31183 // xref, yref
31184 var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, '', 'paper');
31185
31186 if(axRef !== 'paper') {
31187 ax = Axes.getFromId(gdMock, axRef);
31188 ax._shapeIndices.push(shapeOut._index);
31189 r2pos = helpers.rangeToShapePosition(ax);
31190 pos2r = helpers.shapePositionToRange(ax);
31191 } else {
31192 pos2r = r2pos = Lib.identity;
31193 }
31194
31195 // Coerce x0, x1, y0, y1
31196 if(shapeType !== 'path') {
31197 var dflt0 = 0.25;
31198 var dflt1 = 0.75;
31199
31200 // hack until V2.0 when log has regular range behavior - make it look like other
31201 // ranges to send to coerce, then put it back after
31202 // this is all to give reasonable default position behavior on log axes, which is
31203 // a pretty unimportant edge case so we could just ignore this.
31204 var attr0 = axLetter + '0';
31205 var attr1 = axLetter + '1';
31206 var in0 = shapeIn[attr0];
31207 var in1 = shapeIn[attr1];
31208 shapeIn[attr0] = pos2r(shapeIn[attr0], true);
31209 shapeIn[attr1] = pos2r(shapeIn[attr1], true);
31210
31211 if(sizeMode === 'pixel') {
31212 coerce(attr0, 0);
31213 coerce(attr1, 10);
31214 } else {
31215 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
31216 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
31217 }
31218
31219 // hack part 2
31220 shapeOut[attr0] = r2pos(shapeOut[attr0]);
31221 shapeOut[attr1] = r2pos(shapeOut[attr1]);
31222 shapeIn[attr0] = in0;
31223 shapeIn[attr1] = in1;
31224 }
31225
31226 // Coerce xanchor and yanchor
31227 if(sizeMode === 'pixel') {
31228 // Hack for log axis described above
31229 var inAnchor = shapeIn[attrAnchor];
31230 shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
31231
31232 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
31233
31234 // Hack part 2
31235 shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
31236 shapeIn[attrAnchor] = inAnchor;
31237 }
31238 }
31239
31240 if(shapeType === 'path') {
31241 coerce('path');
31242 } else {
31243 Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
31244 }
31245}
31246
31247},{"../../lib":178,"../../plots/array_container_defaults":218,"../../plots/cartesian/axes":222,"./attributes":129,"./helpers":140}],133:[function(_dereq_,module,exports){
31248/**
31249* Copyright 2012-2020, Plotly, Inc.
31250* All rights reserved.
31251*
31252* This source code is licensed under the MIT license found in the
31253* LICENSE file in the root directory of this source tree.
31254*/
31255
31256
31257'use strict';
31258
31259var Registry = _dereq_('../../registry');
31260var Lib = _dereq_('../../lib');
31261var Axes = _dereq_('../../plots/cartesian/axes');
31262
31263var readPaths = _dereq_('./draw_newshape/helpers').readPaths;
31264var displayOutlines = _dereq_('./draw_newshape/display_outlines');
31265
31266var clearOutlineControllers = _dereq_('../../plots/cartesian/handle_outline').clearOutlineControllers;
31267
31268var Color = _dereq_('../color');
31269var Drawing = _dereq_('../drawing');
31270var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
31271
31272var dragElement = _dereq_('../dragelement');
31273var setCursor = _dereq_('../../lib/setcursor');
31274
31275var constants = _dereq_('./constants');
31276var helpers = _dereq_('./helpers');
31277
31278
31279// Shapes are stored in gd.layout.shapes, an array of objects
31280// index can point to one item in this array,
31281// or non-numeric to simply add a new one
31282// or -1 to modify all existing
31283// opt can be the full options object, or one key (to be set to value)
31284// or undefined to simply redraw
31285// if opt is blank, val can be 'add' or a full options object to add a new
31286// annotation at that point in the array, or 'remove' to delete this one
31287
31288module.exports = {
31289 draw: draw,
31290 drawOne: drawOne,
31291 eraseActiveShape: eraseActiveShape
31292};
31293
31294function draw(gd) {
31295 var fullLayout = gd._fullLayout;
31296
31297 // Remove previous shapes before drawing new in shapes in fullLayout.shapes
31298 fullLayout._shapeUpperLayer.selectAll('path').remove();
31299 fullLayout._shapeLowerLayer.selectAll('path').remove();
31300
31301 for(var k in fullLayout._plots) {
31302 var shapelayer = fullLayout._plots[k].shapelayer;
31303 if(shapelayer) shapelayer.selectAll('path').remove();
31304 }
31305
31306 for(var i = 0; i < fullLayout.shapes.length; i++) {
31307 if(fullLayout.shapes[i].visible) {
31308 drawOne(gd, i);
31309 }
31310 }
31311
31312 // may need to resurrect this if we put text (LaTeX) in shapes
31313 // return Plots.previousPromises(gd);
31314}
31315
31316function shouldSkipEdits(gd) {
31317 return !!gd._fullLayout._drawing;
31318}
31319
31320function couldHaveActiveShape(gd) {
31321 // for now keep config.editable: true as it was before shape-drawing PR
31322 return !gd._context.edits.shapePosition;
31323}
31324
31325function drawOne(gd, index) {
31326 // remove the existing shape if there is one.
31327 // because indices can change, we need to look in all shape layers
31328 gd._fullLayout._paperdiv
31329 .selectAll('.shapelayer [data-index="' + index + '"]')
31330 .remove();
31331
31332 var o = helpers.makeOptionsAndPlotinfo(gd, index);
31333 var options = o.options;
31334 var plotinfo = o.plotinfo;
31335
31336 // this shape is gone - quit now after deleting it
31337 // TODO: use d3 idioms instead of deleting and redrawing every time
31338 if(!options._input || options.visible === false) return;
31339
31340 if(options.layer !== 'below') {
31341 drawShape(gd._fullLayout._shapeUpperLayer);
31342 } else if(options.xref === 'paper' || options.yref === 'paper') {
31343 drawShape(gd._fullLayout._shapeLowerLayer);
31344 } else {
31345 if(plotinfo._hadPlotinfo) {
31346 var mainPlot = plotinfo.mainplotinfo || plotinfo;
31347 drawShape(mainPlot.shapelayer);
31348 } else {
31349 // Fall back to _shapeLowerLayer in case the requested subplot doesn't exist.
31350 // This can happen if you reference the shape to an x / y axis combination
31351 // that doesn't have any data on it (and layer is below)
31352 drawShape(gd._fullLayout._shapeLowerLayer);
31353 }
31354 }
31355
31356 function drawShape(shapeLayer) {
31357 var d = getPathString(gd, options);
31358 var attrs = {
31359 'data-index': index,
31360 'fill-rule': options.fillrule,
31361 d: d
31362 };
31363
31364 var opacity = options.opacity;
31365 var fillColor = options.fillcolor;
31366 var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)';
31367 var lineWidth = options.line.width;
31368 var lineDash = options.line.dash;
31369
31370 var isOpen = d[d.length - 1] !== 'Z';
31371
31372 var isActiveShape = couldHaveActiveShape(gd) &&
31373 options.editable && gd._fullLayout._activeShapeIndex === index;
31374
31375 if(isActiveShape) {
31376 fillColor = isOpen ? 'rgba(0,0,0,0)' :
31377 gd._fullLayout.activeshape.fillcolor;
31378
31379 opacity = gd._fullLayout.activeshape.opacity;
31380 }
31381
31382 var path = shapeLayer.append('path')
31383 .attr(attrs)
31384 .style('opacity', opacity)
31385 .call(Color.stroke, lineColor)
31386 .call(Color.fill, fillColor)
31387 .call(Drawing.dashLine, lineDash, lineWidth);
31388
31389 setClipPath(path, gd, options);
31390
31391 var editHelpers;
31392 if(isActiveShape || gd._context.edits.shapePosition) editHelpers = arrayEditor(gd.layout, 'shapes', options);
31393
31394 if(isActiveShape) {
31395 path.style({
31396 'cursor': 'move',
31397 });
31398
31399 var dragOptions = {
31400 element: path.node(),
31401 plotinfo: plotinfo,
31402 gd: gd,
31403 editHelpers: editHelpers,
31404 isActiveShape: true // i.e. to enable controllers
31405 };
31406
31407 var polygons = readPaths(d, gd);
31408 // display polygons on the screen
31409 displayOutlines(polygons, path, dragOptions);
31410 } else {
31411 if(gd._context.edits.shapePosition) {
31412 setupDragElement(gd, path, options, index, shapeLayer, editHelpers);
31413 }
31414
31415 path.style('pointer-events',
31416 !couldHaveActiveShape(gd) || (
31417 lineWidth < 2 || ( // not has a remarkable border
31418 !isOpen && Color.opacity(fillColor) * opacity > 0.5 // not too transparent
31419 )
31420 ) ? 'all' : 'stroke'
31421 );
31422 }
31423
31424 path.node().addEventListener('click', function() { return activateShape(gd, path); });
31425 }
31426}
31427
31428function setClipPath(shapePath, gd, shapeOptions) {
31429 // note that for layer="below" the clipAxes can be different from the
31430 // subplot we're drawing this in. This could cause problems if the shape
31431 // spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
31432 var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '');
31433
31434 Drawing.setClipUrl(
31435 shapePath,
31436 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
31437 gd
31438 );
31439}
31440
31441function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHelpers) {
31442 var MINWIDTH = 10;
31443 var MINHEIGHT = 10;
31444
31445 var xPixelSized = shapeOptions.xsizemode === 'pixel';
31446 var yPixelSized = shapeOptions.ysizemode === 'pixel';
31447 var isLine = shapeOptions.type === 'line';
31448 var isPath = shapeOptions.type === 'path';
31449
31450 var modifyItem = editHelpers.modifyItem;
31451
31452 var x0, y0, x1, y1, xAnchor, yAnchor;
31453 var n0, s0, w0, e0, optN, optS, optW, optE;
31454 var pathIn;
31455
31456 // setup conversion functions
31457 var xa = Axes.getFromId(gd, shapeOptions.xref);
31458 var ya = Axes.getFromId(gd, shapeOptions.yref);
31459 var x2p = helpers.getDataToPixel(gd, xa);
31460 var y2p = helpers.getDataToPixel(gd, ya, true);
31461 var p2x = helpers.getPixelToData(gd, xa);
31462 var p2y = helpers.getPixelToData(gd, ya, true);
31463
31464 var sensoryElement = obtainSensoryElement();
31465 var dragOptions = {
31466 element: sensoryElement.node(),
31467 gd: gd,
31468 prepFn: startDrag,
31469 doneFn: endDrag,
31470 clickFn: abortDrag
31471 };
31472 var dragMode;
31473
31474 dragElement.init(dragOptions);
31475
31476 sensoryElement.node().onmousemove = updateDragMode;
31477
31478 function obtainSensoryElement() {
31479 return isLine ? createLineDragHandles() : shapePath;
31480 }
31481
31482 function createLineDragHandles() {
31483 var minSensoryWidth = 10;
31484 var sensoryWidth = Math.max(shapeOptions.line.width, minSensoryWidth);
31485
31486 // Helper shapes group
31487 // Note that by setting the `data-index` attr, it is ensured that
31488 // the helper group is purged in this modules `draw` function
31489 var g = shapeLayer.append('g')
31490 .attr('data-index', index);
31491
31492 // Helper path for moving
31493 g.append('path')
31494 .attr('d', shapePath.attr('d'))
31495 .style({
31496 'cursor': 'move',
31497 'stroke-width': sensoryWidth,
31498 'stroke-opacity': '0' // ensure not visible
31499 });
31500
31501 // Helper circles for resizing
31502 var circleStyle = {
31503 'fill-opacity': '0' // ensure not visible
31504 };
31505 var circleRadius = Math.max(sensoryWidth / 2, minSensoryWidth);
31506
31507 g.append('circle')
31508 .attr({
31509 'data-line-point': 'start-point',
31510 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0),
31511 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0),
31512 'r': circleRadius
31513 })
31514 .style(circleStyle)
31515 .classed('cursor-grab', true);
31516
31517 g.append('circle')
31518 .attr({
31519 'data-line-point': 'end-point',
31520 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1),
31521 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1),
31522 'r': circleRadius
31523 })
31524 .style(circleStyle)
31525 .classed('cursor-grab', true);
31526
31527 return g;
31528 }
31529
31530 function updateDragMode(evt) {
31531 if(shouldSkipEdits(gd)) {
31532 dragMode = null;
31533 return;
31534 }
31535
31536 if(isLine) {
31537 if(evt.target.tagName === 'path') {
31538 dragMode = 'move';
31539 } else {
31540 dragMode = evt.target.attributes['data-line-point'].value === 'start-point' ?
31541 'resize-over-start-point' : 'resize-over-end-point';
31542 }
31543 } else {
31544 // element might not be on screen at time of setup,
31545 // so obtain bounding box here
31546 var dragBBox = dragOptions.element.getBoundingClientRect();
31547
31548 // choose 'move' or 'resize'
31549 // based on initial position of cursor within the drag element
31550 var w = dragBBox.right - dragBBox.left;
31551 var h = dragBBox.bottom - dragBBox.top;
31552 var x = evt.clientX - dragBBox.left;
31553 var y = evt.clientY - dragBBox.top;
31554 var cursor = (!isPath && w > MINWIDTH && h > MINHEIGHT && !evt.shiftKey) ?
31555 dragElement.getCursor(x / w, 1 - y / h) :
31556 'move';
31557
31558 setCursor(shapePath, cursor);
31559
31560 // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
31561 dragMode = cursor.split('-')[0];
31562 }
31563 }
31564
31565 function startDrag(evt) {
31566 if(shouldSkipEdits(gd)) return;
31567
31568 // setup update strings and initial values
31569 if(xPixelSized) {
31570 xAnchor = x2p(shapeOptions.xanchor);
31571 }
31572 if(yPixelSized) {
31573 yAnchor = y2p(shapeOptions.yanchor);
31574 }
31575
31576 if(shapeOptions.type === 'path') {
31577 pathIn = shapeOptions.path;
31578 } else {
31579 x0 = xPixelSized ? shapeOptions.x0 : x2p(shapeOptions.x0);
31580 y0 = yPixelSized ? shapeOptions.y0 : y2p(shapeOptions.y0);
31581 x1 = xPixelSized ? shapeOptions.x1 : x2p(shapeOptions.x1);
31582 y1 = yPixelSized ? shapeOptions.y1 : y2p(shapeOptions.y1);
31583 }
31584
31585 if(x0 < x1) {
31586 w0 = x0;
31587 optW = 'x0';
31588 e0 = x1;
31589 optE = 'x1';
31590 } else {
31591 w0 = x1;
31592 optW = 'x1';
31593 e0 = x0;
31594 optE = 'x0';
31595 }
31596
31597 // For fixed size shapes take opposing direction of y-axis into account.
31598 // Hint: For data sized shapes this is done by the y2p function.
31599 if((!yPixelSized && y0 < y1) || (yPixelSized && y0 > y1)) {
31600 n0 = y0;
31601 optN = 'y0';
31602 s0 = y1;
31603 optS = 'y1';
31604 } else {
31605 n0 = y1;
31606 optN = 'y1';
31607 s0 = y0;
31608 optS = 'y0';
31609 }
31610
31611 // setup dragMode and the corresponding handler
31612 updateDragMode(evt);
31613 renderVisualCues(shapeLayer, shapeOptions);
31614 deactivateClipPathTemporarily(shapePath, shapeOptions, gd);
31615 dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
31616 dragOptions.altKey = evt.altKey;
31617 }
31618
31619 function endDrag() {
31620 if(shouldSkipEdits(gd)) return;
31621
31622 setCursor(shapePath);
31623 removeVisualCues(shapeLayer);
31624
31625 // Don't rely on clipPath being activated during re-layout
31626 setClipPath(shapePath, gd, shapeOptions);
31627 Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj());
31628 }
31629
31630 function abortDrag() {
31631 if(shouldSkipEdits(gd)) return;
31632
31633 removeVisualCues(shapeLayer);
31634 }
31635
31636 function moveShape(dx, dy) {
31637 if(shapeOptions.type === 'path') {
31638 var noOp = function(coord) { return coord; };
31639 var moveX = noOp;
31640 var moveY = noOp;
31641
31642 if(xPixelSized) {
31643 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
31644 } else {
31645 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
31646 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
31647 }
31648
31649 if(yPixelSized) {
31650 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
31651 } else {
31652 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
31653 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
31654 }
31655
31656 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
31657 } else {
31658 if(xPixelSized) {
31659 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
31660 } else {
31661 modifyItem('x0', shapeOptions.x0 = p2x(x0 + dx));
31662 modifyItem('x1', shapeOptions.x1 = p2x(x1 + dx));
31663 }
31664
31665 if(yPixelSized) {
31666 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
31667 } else {
31668 modifyItem('y0', shapeOptions.y0 = p2y(y0 + dy));
31669 modifyItem('y1', shapeOptions.y1 = p2y(y1 + dy));
31670 }
31671 }
31672
31673 shapePath.attr('d', getPathString(gd, shapeOptions));
31674 renderVisualCues(shapeLayer, shapeOptions);
31675 }
31676
31677 function resizeShape(dx, dy) {
31678 if(isPath) {
31679 // TODO: implement path resize, don't forget to update dragMode code
31680 var noOp = function(coord) { return coord; };
31681 var moveX = noOp;
31682 var moveY = noOp;
31683
31684 if(xPixelSized) {
31685 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
31686 } else {
31687 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
31688 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
31689 }
31690
31691 if(yPixelSized) {
31692 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
31693 } else {
31694 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
31695 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
31696 }
31697
31698 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
31699 } else if(isLine) {
31700 if(dragMode === 'resize-over-start-point') {
31701 var newX0 = x0 + dx;
31702 var newY0 = yPixelSized ? y0 - dy : y0 + dy;
31703 modifyItem('x0', shapeOptions.x0 = xPixelSized ? newX0 : p2x(newX0));
31704 modifyItem('y0', shapeOptions.y0 = yPixelSized ? newY0 : p2y(newY0));
31705 } else if(dragMode === 'resize-over-end-point') {
31706 var newX1 = x1 + dx;
31707 var newY1 = yPixelSized ? y1 - dy : y1 + dy;
31708 modifyItem('x1', shapeOptions.x1 = xPixelSized ? newX1 : p2x(newX1));
31709 modifyItem('y1', shapeOptions.y1 = yPixelSized ? newY1 : p2y(newY1));
31710 }
31711 } else {
31712 var has = function(str) { return dragMode.indexOf(str) !== -1; };
31713 var hasN = has('n');
31714 var hasS = has('s');
31715 var hasW = has('w');
31716 var hasE = has('e');
31717
31718 var newN = hasN ? n0 + dy : n0;
31719 var newS = hasS ? s0 + dy : s0;
31720 var newW = hasW ? w0 + dx : w0;
31721 var newE = hasE ? e0 + dx : e0;
31722
31723 if(yPixelSized) {
31724 // Do things in opposing direction for y-axis.
31725 // Hint: for data-sized shapes the reversal of axis direction is done in p2y.
31726 if(hasN) newN = n0 - dy;
31727 if(hasS) newS = s0 - dy;
31728 }
31729
31730 // Update shape eventually. Again, be aware of the
31731 // opposing direction of the y-axis of fixed size shapes.
31732 if(
31733 (!yPixelSized && newS - newN > MINHEIGHT) ||
31734 (yPixelSized && newN - newS > MINHEIGHT)
31735 ) {
31736 modifyItem(optN, shapeOptions[optN] = yPixelSized ? newN : p2y(newN));
31737 modifyItem(optS, shapeOptions[optS] = yPixelSized ? newS : p2y(newS));
31738 }
31739 if(newE - newW > MINWIDTH) {
31740 modifyItem(optW, shapeOptions[optW] = xPixelSized ? newW : p2x(newW));
31741 modifyItem(optE, shapeOptions[optE] = xPixelSized ? newE : p2x(newE));
31742 }
31743 }
31744
31745 shapePath.attr('d', getPathString(gd, shapeOptions));
31746 renderVisualCues(shapeLayer, shapeOptions);
31747 }
31748
31749 function renderVisualCues(shapeLayer, shapeOptions) {
31750 if(xPixelSized || yPixelSized) {
31751 renderAnchor();
31752 }
31753
31754 function renderAnchor() {
31755 var isNotPath = shapeOptions.type !== 'path';
31756
31757 // d3 join with dummy data to satisfy d3 data-binding
31758 var visualCues = shapeLayer.selectAll('.visual-cue').data([0]);
31759
31760 // Enter
31761 var strokeWidth = 1;
31762 visualCues.enter()
31763 .append('path')
31764 .attr({
31765 'fill': '#fff',
31766 'fill-rule': 'evenodd',
31767 'stroke': '#000',
31768 'stroke-width': strokeWidth
31769 })
31770 .classed('visual-cue', true);
31771
31772 // Update
31773 var posX = x2p(
31774 xPixelSized ?
31775 shapeOptions.xanchor :
31776 Lib.midRange(
31777 isNotPath ?
31778 [shapeOptions.x0, shapeOptions.x1] :
31779 helpers.extractPathCoords(shapeOptions.path, constants.paramIsX))
31780 );
31781 var posY = y2p(
31782 yPixelSized ?
31783 shapeOptions.yanchor :
31784 Lib.midRange(
31785 isNotPath ?
31786 [shapeOptions.y0, shapeOptions.y1] :
31787 helpers.extractPathCoords(shapeOptions.path, constants.paramIsY))
31788 );
31789
31790 posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth);
31791 posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth);
31792
31793 if(xPixelSized && yPixelSized) {
31794 var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
31795 'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
31796 visualCues.attr('d', crossPath);
31797 } else if(xPixelSized) {
31798 var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) +
31799 'v18 h2 v-18 Z';
31800 visualCues.attr('d', vBarPath);
31801 } else {
31802 var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
31803 'h18 v2 h-18 Z';
31804 visualCues.attr('d', hBarPath);
31805 }
31806 }
31807 }
31808
31809 function removeVisualCues(shapeLayer) {
31810 shapeLayer.selectAll('.visual-cue').remove();
31811 }
31812
31813 function deactivateClipPathTemporarily(shapePath, shapeOptions, gd) {
31814 var xref = shapeOptions.xref;
31815 var yref = shapeOptions.yref;
31816 var xa = Axes.getFromId(gd, xref);
31817 var ya = Axes.getFromId(gd, yref);
31818
31819 var clipAxes = '';
31820 if(xref !== 'paper' && !xa.autorange) clipAxes += xref;
31821 if(yref !== 'paper' && !ya.autorange) clipAxes += yref;
31822
31823 Drawing.setClipUrl(
31824 shapePath,
31825 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
31826 gd
31827 );
31828 }
31829}
31830
31831function getPathString(gd, options) {
31832 var type = options.type;
31833 var xa = Axes.getFromId(gd, options.xref);
31834 var ya = Axes.getFromId(gd, options.yref);
31835 var gs = gd._fullLayout._size;
31836 var x2r, x2p, y2r, y2p;
31837 var x0, x1, y0, y1;
31838
31839 if(xa) {
31840 x2r = helpers.shapePositionToRange(xa);
31841 x2p = function(v) { return xa._offset + xa.r2p(x2r(v, true)); };
31842 } else {
31843 x2p = function(v) { return gs.l + gs.w * v; };
31844 }
31845
31846 if(ya) {
31847 y2r = helpers.shapePositionToRange(ya);
31848 y2p = function(v) { return ya._offset + ya.r2p(y2r(v, true)); };
31849 } else {
31850 y2p = function(v) { return gs.t + gs.h * (1 - v); };
31851 }
31852
31853 if(type === 'path') {
31854 if(xa && xa.type === 'date') x2p = helpers.decodeDate(x2p);
31855 if(ya && ya.type === 'date') y2p = helpers.decodeDate(y2p);
31856 return convertPath(options, x2p, y2p);
31857 }
31858
31859 if(options.xsizemode === 'pixel') {
31860 var xAnchorPos = x2p(options.xanchor);
31861 x0 = xAnchorPos + options.x0;
31862 x1 = xAnchorPos + options.x1;
31863 } else {
31864 x0 = x2p(options.x0);
31865 x1 = x2p(options.x1);
31866 }
31867
31868 if(options.ysizemode === 'pixel') {
31869 var yAnchorPos = y2p(options.yanchor);
31870 y0 = yAnchorPos - options.y0;
31871 y1 = yAnchorPos - options.y1;
31872 } else {
31873 y0 = y2p(options.y0);
31874 y1 = y2p(options.y1);
31875 }
31876
31877 if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1;
31878 if(type === 'rect') return 'M' + x0 + ',' + y0 + 'H' + x1 + 'V' + y1 + 'H' + x0 + 'Z';
31879
31880 // circle
31881 var cx = (x0 + x1) / 2;
31882 var cy = (y0 + y1) / 2;
31883 var rx = Math.abs(cx - x0);
31884 var ry = Math.abs(cy - y0);
31885 var rArc = 'A' + rx + ',' + ry;
31886 var rightPt = (cx + rx) + ',' + cy;
31887 var topPt = cx + ',' + (cy - ry);
31888 return 'M' + rightPt + rArc + ' 0 1,1 ' + topPt +
31889 rArc + ' 0 0,1 ' + rightPt + 'Z';
31890}
31891
31892
31893function convertPath(options, x2p, y2p) {
31894 var pathIn = options.path;
31895 var xSizemode = options.xsizemode;
31896 var ySizemode = options.ysizemode;
31897 var xAnchor = options.xanchor;
31898 var yAnchor = options.yanchor;
31899
31900 return pathIn.replace(constants.segmentRE, function(segment) {
31901 var paramNumber = 0;
31902 var segmentType = segment.charAt(0);
31903 var xParams = constants.paramIsX[segmentType];
31904 var yParams = constants.paramIsY[segmentType];
31905 var nParams = constants.numParams[segmentType];
31906
31907 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
31908 if(xParams[paramNumber]) {
31909 if(xSizemode === 'pixel') param = x2p(xAnchor) + Number(param);
31910 else param = x2p(param);
31911 } else if(yParams[paramNumber]) {
31912 if(ySizemode === 'pixel') param = y2p(yAnchor) - Number(param);
31913 else param = y2p(param);
31914 }
31915 paramNumber++;
31916
31917 if(paramNumber > nParams) param = 'X';
31918 return param;
31919 });
31920
31921 if(paramNumber > nParams) {
31922 paramString = paramString.replace(/[\s,]*X.*/, '');
31923 Lib.log('Ignoring extra params in segment ' + segment);
31924 }
31925
31926 return segmentType + paramString;
31927 });
31928}
31929
31930function movePath(pathIn, moveX, moveY) {
31931 return pathIn.replace(constants.segmentRE, function(segment) {
31932 var paramNumber = 0;
31933 var segmentType = segment.charAt(0);
31934 var xParams = constants.paramIsX[segmentType];
31935 var yParams = constants.paramIsY[segmentType];
31936 var nParams = constants.numParams[segmentType];
31937
31938 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
31939 if(paramNumber >= nParams) return param;
31940
31941 if(xParams[paramNumber]) param = moveX(param);
31942 else if(yParams[paramNumber]) param = moveY(param);
31943
31944 paramNumber++;
31945
31946 return param;
31947 });
31948
31949 return segmentType + paramString;
31950 });
31951}
31952
31953function activateShape(gd, path) {
31954 if(!couldHaveActiveShape(gd)) return;
31955
31956 var element = path.node();
31957 var id = +element.getAttribute('data-index');
31958 if(id >= 0) {
31959 // deactivate if already active
31960 if(id === gd._fullLayout._activeShapeIndex) {
31961 deactivateShape(gd);
31962 return;
31963 }
31964
31965 gd._fullLayout._activeShapeIndex = id;
31966 gd._fullLayout._deactivateShape = deactivateShape;
31967 draw(gd);
31968 }
31969}
31970
31971function deactivateShape(gd) {
31972 if(!couldHaveActiveShape(gd)) return;
31973
31974 var id = gd._fullLayout._activeShapeIndex;
31975 if(id >= 0) {
31976 clearOutlineControllers(gd);
31977 delete gd._fullLayout._activeShapeIndex;
31978 draw(gd);
31979 }
31980}
31981
31982function eraseActiveShape(gd) {
31983 if(!couldHaveActiveShape(gd)) return;
31984
31985 clearOutlineControllers(gd);
31986
31987 var id = gd._fullLayout._activeShapeIndex;
31988 var shapes = (gd.layout || {}).shapes || [];
31989 if(id < shapes.length) {
31990 var newShapes = [];
31991 for(var q = 0; q < shapes.length; q++) {
31992 if(q !== id) {
31993 newShapes.push(shapes[q]);
31994 }
31995 }
31996
31997 delete gd._fullLayout._activeShapeIndex;
31998
31999 Registry.call('_guiRelayout', gd, {
32000 shapes: newShapes
32001 });
32002 }
32003}
32004
32005},{"../../lib":178,"../../lib/setcursor":197,"../../plot_api/plot_template":212,"../../plots/cartesian/axes":222,"../../plots/cartesian/handle_outline":232,"../../registry":269,"../color":52,"../dragelement":71,"../drawing":74,"./constants":131,"./draw_newshape/display_outlines":137,"./draw_newshape/helpers":138,"./helpers":140}],134:[function(_dereq_,module,exports){
32006/**
32007* Copyright 2012-2020, Plotly, Inc.
32008* All rights reserved.
32009*
32010* This source code is licensed under the MIT license found in the
32011* LICENSE file in the root directory of this source tree.
32012*/
32013
32014'use strict';
32015
32016var dash = _dereq_('../../drawing/attributes').dash;
32017var extendFlat = _dereq_('../../../lib/extend').extendFlat;
32018
32019module.exports = {
32020 newshape: {
32021 line: {
32022 color: {
32023 valType: 'color',
32024 editType: 'none',
32025
32026
32027 },
32028 width: {
32029 valType: 'number',
32030 min: 0,
32031 dflt: 4,
32032
32033 editType: 'none',
32034
32035 },
32036 dash: extendFlat({}, dash, {
32037 dflt: 'solid',
32038 editType: 'none'
32039 }),
32040
32041 editType: 'none'
32042 },
32043 fillcolor: {
32044 valType: 'color',
32045 dflt: 'rgba(0,0,0,0)',
32046
32047 editType: 'none',
32048
32049 },
32050 fillrule: {
32051 valType: 'enumerated',
32052 values: ['evenodd', 'nonzero'],
32053 dflt: 'evenodd',
32054
32055 editType: 'none',
32056
32057 },
32058 opacity: {
32059 valType: 'number',
32060 min: 0,
32061 max: 1,
32062 dflt: 1,
32063
32064 editType: 'none',
32065
32066 },
32067 layer: {
32068 valType: 'enumerated',
32069 values: ['below', 'above'],
32070 dflt: 'above',
32071
32072 editType: 'none',
32073
32074 },
32075 drawdirection: {
32076 valType: 'enumerated',
32077
32078 values: ['ortho', 'horizontal', 'vertical', 'diagonal'],
32079 dflt: 'diagonal',
32080 editType: 'none',
32081
32082 },
32083
32084 editType: 'none'
32085 },
32086
32087 activeshape: {
32088 fillcolor: {
32089 valType: 'color',
32090 dflt: 'rgb(255,0,255)',
32091
32092 editType: 'none',
32093
32094 },
32095 opacity: {
32096 valType: 'number',
32097 min: 0,
32098 max: 1,
32099 dflt: 0.5,
32100
32101 editType: 'none',
32102
32103 },
32104 editType: 'none'
32105 }
32106};
32107
32108},{"../../../lib/extend":173,"../../drawing/attributes":73}],135:[function(_dereq_,module,exports){
32109/**
32110* Copyright 2012-2020, Plotly, Inc.
32111* All rights reserved.
32112*
32113* This source code is licensed under the MIT license found in the
32114* LICENSE file in the root directory of this source tree.
32115*/
32116
32117'use strict';
32118
32119var CIRCLE_SIDES = 32; // should be divisible by 4
32120
32121module.exports = {
32122 CIRCLE_SIDES: CIRCLE_SIDES,
32123 i000: 0,
32124 i090: CIRCLE_SIDES / 4,
32125 i180: CIRCLE_SIDES / 2,
32126 i270: CIRCLE_SIDES / 4 * 3,
32127 cos45: Math.cos(Math.PI / 4),
32128 sin45: Math.sin(Math.PI / 4),
32129 SQRT2: Math.sqrt(2)
32130};
32131
32132},{}],136:[function(_dereq_,module,exports){
32133/**
32134* Copyright 2012-2020, Plotly, Inc.
32135* All rights reserved.
32136*
32137* This source code is licensed under the MIT license found in the
32138* LICENSE file in the root directory of this source tree.
32139*/
32140
32141
32142'use strict';
32143
32144var Color = _dereq_('../../color');
32145
32146
32147module.exports = function supplyDrawNewShapeDefaults(layoutIn, layoutOut, coerce) {
32148 coerce('newshape.drawdirection');
32149 coerce('newshape.layer');
32150 coerce('newshape.fillcolor');
32151 coerce('newshape.fillrule');
32152 coerce('newshape.opacity');
32153 var newshapeLineWidth = coerce('newshape.line.width');
32154 if(newshapeLineWidth) {
32155 var bgcolor = (layoutIn || {}).plot_bgcolor || '#FFF';
32156 coerce('newshape.line.color', Color.contrast(bgcolor));
32157 coerce('newshape.line.dash');
32158 }
32159
32160 coerce('activeshape.fillcolor');
32161 coerce('activeshape.opacity');
32162};
32163
32164},{"../../color":52}],137:[function(_dereq_,module,exports){
32165/**
32166* Copyright 2012-2020, Plotly, Inc.
32167* All rights reserved.
32168*
32169* This source code is licensed under the MIT license found in the
32170* LICENSE file in the root directory of this source tree.
32171*/
32172
32173
32174'use strict';
32175
32176var dragElement = _dereq_('../../dragelement');
32177var dragHelpers = _dereq_('../../dragelement/helpers');
32178var drawMode = dragHelpers.drawMode;
32179
32180var Registry = _dereq_('../../../registry');
32181
32182var constants = _dereq_('./constants');
32183var i000 = constants.i000;
32184var i090 = constants.i090;
32185var i180 = constants.i180;
32186var i270 = constants.i270;
32187
32188var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
32189var clearOutlineControllers = handleOutline.clearOutlineControllers;
32190
32191var helpers = _dereq_('./helpers');
32192var pointsShapeRectangle = helpers.pointsShapeRectangle;
32193var pointsShapeEllipse = helpers.pointsShapeEllipse;
32194var writePaths = helpers.writePaths;
32195var newShapes = _dereq_('./newshapes');
32196
32197module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) {
32198 if(!nCalls) nCalls = 0;
32199
32200 var gd = dragOptions.gd;
32201
32202 function redraw() {
32203 // recursive call
32204 displayOutlines(polygons, outlines, dragOptions, nCalls++);
32205
32206 if(pointsShapeEllipse(polygons[0])) {
32207 update({redrawing: true});
32208 }
32209 }
32210
32211 function update(opts) {
32212 dragOptions.isActiveShape = false; // i.e. to disable controllers
32213
32214 var updateObject = newShapes(outlines, dragOptions);
32215 if(Object.keys(updateObject).length) {
32216 Registry.call((opts || {}).redrawing ? 'relayout' : '_guiRelayout', gd, updateObject);
32217 }
32218 }
32219
32220
32221 var isActiveShape = dragOptions.isActiveShape;
32222 var fullLayout = gd._fullLayout;
32223 var zoomLayer = fullLayout._zoomlayer;
32224
32225 var dragmode = dragOptions.dragmode;
32226 var isDrawMode = drawMode(dragmode);
32227
32228 if(isDrawMode) gd._fullLayout._drawing = true;
32229 else if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd);
32230
32231 // make outline
32232 outlines.attr('d', writePaths(polygons));
32233
32234 // add controllers
32235 var vertexDragOptions;
32236 var shapeDragOptions;
32237 var indexI; // cell index
32238 var indexJ; // vertex or cell-controller index
32239 var copyPolygons;
32240
32241 if(isActiveShape && !nCalls) {
32242 copyPolygons = recordPositions([], polygons);
32243
32244 var g = zoomLayer.append('g').attr('class', 'outline-controllers');
32245 addVertexControllers(g);
32246 addShapeControllers();
32247 }
32248
32249 function startDragVertex(evt) {
32250 indexI = +evt.srcElement.getAttribute('data-i');
32251 indexJ = +evt.srcElement.getAttribute('data-j');
32252
32253 vertexDragOptions[indexI][indexJ].moveFn = moveVertexController;
32254 }
32255
32256 function moveVertexController(dx, dy) {
32257 if(!polygons.length) return;
32258
32259 var x0 = copyPolygons[indexI][indexJ][1];
32260 var y0 = copyPolygons[indexI][indexJ][2];
32261
32262 var cell = polygons[indexI];
32263 var len = cell.length;
32264 if(pointsShapeRectangle(cell)) {
32265 for(var q = 0; q < len; q++) {
32266 if(q === indexJ) continue;
32267
32268 // move other corners of rectangle
32269 var pos = cell[q];
32270
32271 if(pos[1] === cell[indexJ][1]) {
32272 pos[1] = x0 + dx;
32273 }
32274
32275 if(pos[2] === cell[indexJ][2]) {
32276 pos[2] = y0 + dy;
32277 }
32278 }
32279 // move the corner
32280 cell[indexJ][1] = x0 + dx;
32281 cell[indexJ][2] = y0 + dy;
32282
32283 if(!pointsShapeRectangle(cell)) {
32284 // reject result to rectangles with ensure areas
32285 for(var j = 0; j < len; j++) {
32286 for(var k = 0; k < cell[j].length; k++) {
32287 cell[j][k] = copyPolygons[indexI][j][k];
32288 }
32289 }
32290 }
32291 } else { // other polylines
32292 cell[indexJ][1] = x0 + dx;
32293 cell[indexJ][2] = y0 + dy;
32294 }
32295
32296 redraw();
32297 }
32298
32299 function endDragVertexController() {
32300 update();
32301 }
32302
32303 function removeVertex() {
32304 if(!polygons.length) return;
32305 if(!polygons[indexI]) return;
32306 if(!polygons[indexI].length) return;
32307
32308 var newPolygon = [];
32309 for(var j = 0; j < polygons[indexI].length; j++) {
32310 if(j !== indexJ) {
32311 newPolygon.push(
32312 polygons[indexI][j]
32313 );
32314 }
32315 }
32316
32317 if(newPolygon.length > 1 && !(
32318 newPolygon.length === 2 && newPolygon[1][0] === 'Z')
32319 ) {
32320 if(indexJ === 0) {
32321 newPolygon[0][0] = 'M';
32322 }
32323
32324 polygons[indexI] = newPolygon;
32325
32326 redraw();
32327 update();
32328 }
32329 }
32330
32331 function clickVertexController(numClicks, evt) {
32332 if(numClicks === 2) {
32333 indexI = +evt.srcElement.getAttribute('data-i');
32334 indexJ = +evt.srcElement.getAttribute('data-j');
32335
32336 var cell = polygons[indexI];
32337 if(
32338 !pointsShapeRectangle(cell) &&
32339 !pointsShapeEllipse(cell)
32340 ) {
32341 removeVertex();
32342 }
32343 }
32344 }
32345
32346 function addVertexControllers(g) {
32347 vertexDragOptions = [];
32348
32349 for(var i = 0; i < polygons.length; i++) {
32350 var cell = polygons[i];
32351
32352 var onRect = pointsShapeRectangle(cell);
32353 var onEllipse = !onRect && pointsShapeEllipse(cell);
32354
32355 vertexDragOptions[i] = [];
32356 for(var j = 0; j < cell.length; j++) {
32357 if(cell[j][0] === 'Z') continue;
32358
32359 if(onEllipse &&
32360 j !== i000 &&
32361 j !== i090 &&
32362 j !== i180 &&
32363 j !== i270
32364 ) {
32365 continue;
32366 }
32367
32368 var x = cell[j][1];
32369 var y = cell[j][2];
32370
32371 var vertex = g.append('circle')
32372 .classed('cursor-grab', true)
32373 .attr('data-i', i)
32374 .attr('data-j', j)
32375 .attr('cx', x)
32376 .attr('cy', y)
32377 .attr('r', 4)
32378 .style({
32379 'mix-blend-mode': 'luminosity',
32380 fill: 'black',
32381 stroke: 'white',
32382 'stroke-width': 1
32383 });
32384
32385 vertexDragOptions[i][j] = {
32386 element: vertex.node(),
32387 gd: gd,
32388 prepFn: startDragVertex,
32389 doneFn: endDragVertexController,
32390 clickFn: clickVertexController
32391 };
32392
32393 dragElement.init(vertexDragOptions[i][j]);
32394 }
32395 }
32396 }
32397
32398 function moveShape(dx, dy) {
32399 if(!polygons.length) return;
32400
32401 for(var i = 0; i < polygons.length; i++) {
32402 for(var j = 0; j < polygons[i].length; j++) {
32403 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
32404 polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx;
32405 polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy;
32406 }
32407 }
32408 }
32409 }
32410
32411 function moveShapeController(dx, dy) {
32412 moveShape(dx, dy);
32413
32414 redraw();
32415 }
32416
32417 function startDragShapeController(evt) {
32418 indexI = +evt.srcElement.getAttribute('data-i');
32419 if(!indexI) indexI = 0; // ensure non-existing move button get zero index
32420
32421 shapeDragOptions[indexI].moveFn = moveShapeController;
32422 }
32423
32424 function endDragShapeController() {
32425 update();
32426 }
32427
32428 function addShapeControllers() {
32429 shapeDragOptions = [];
32430
32431 if(!polygons.length) return;
32432
32433 var i = 0;
32434 shapeDragOptions[i] = {
32435 element: outlines[0][0],
32436 gd: gd,
32437 prepFn: startDragShapeController,
32438 doneFn: endDragShapeController
32439 };
32440
32441 dragElement.init(shapeDragOptions[i]);
32442 }
32443};
32444
32445function recordPositions(polygonsOut, polygonsIn) {
32446 for(var i = 0; i < polygonsIn.length; i++) {
32447 var cell = polygonsIn[i];
32448 polygonsOut[i] = [];
32449 for(var j = 0; j < cell.length; j++) {
32450 polygonsOut[i][j] = [];
32451 for(var k = 0; k < cell[j].length; k++) {
32452 polygonsOut[i][j][k] = cell[j][k];
32453 }
32454 }
32455 }
32456 return polygonsOut;
32457}
32458
32459},{"../../../plots/cartesian/handle_outline":232,"../../../registry":269,"../../dragelement":71,"../../dragelement/helpers":70,"./constants":135,"./helpers":138,"./newshapes":139}],138:[function(_dereq_,module,exports){
32460/**
32461* Copyright 2012-2020, Plotly, Inc.
32462* All rights reserved.
32463*
32464* This source code is licensed under the MIT license found in the
32465* LICENSE file in the root directory of this source tree.
32466*/
32467
32468
32469'use strict';
32470
32471var parseSvgPath = _dereq_('parse-svg-path');
32472
32473var constants = _dereq_('./constants');
32474var CIRCLE_SIDES = constants.CIRCLE_SIDES;
32475var SQRT2 = constants.SQRT2;
32476
32477var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
32478var p2r = cartesianHelpers.p2r;
32479var r2p = cartesianHelpers.r2p;
32480
32481var iC = [0, 3, 4, 5, 6, 1, 2];
32482var iQS = [0, 3, 4, 1, 2];
32483
32484exports.writePaths = function(polygons) {
32485 var nI = polygons.length;
32486 if(!nI) return 'M0,0Z';
32487
32488 var str = '';
32489 for(var i = 0; i < nI; i++) {
32490 var nJ = polygons[i].length;
32491 for(var j = 0; j < nJ; j++) {
32492 var w = polygons[i][j][0];
32493 if(w === 'Z') {
32494 str += 'Z';
32495 } else {
32496 var nK = polygons[i][j].length;
32497 for(var k = 0; k < nK; k++) {
32498 var realK = k;
32499 if(w === 'Q' || w === 'S') {
32500 realK = iQS[k];
32501 } else if(w === 'C') {
32502 realK = iC[k];
32503 }
32504
32505 str += polygons[i][j][realK];
32506 if(k > 0 && k < nK - 1) {
32507 str += ',';
32508 }
32509 }
32510 }
32511 }
32512 }
32513
32514 return str;
32515};
32516
32517exports.readPaths = function(str, gd, plotinfo, isActiveShape) {
32518 var cmd = parseSvgPath(str);
32519
32520 var polys = [];
32521 var n = -1;
32522 var newPoly = function() {
32523 n++;
32524 polys[n] = [];
32525 };
32526
32527 var k;
32528 var x = 0;
32529 var y = 0;
32530 var initX;
32531 var initY;
32532 var recStart = function() {
32533 initX = x;
32534 initY = y;
32535 };
32536
32537 recStart();
32538 for(var i = 0; i < cmd.length; i++) {
32539 var newPos = [];
32540
32541 var x1, x2, y1, y2; // i.e. extra params for curves
32542
32543 var c = cmd[i][0];
32544 var w = c;
32545 switch(c) {
32546 case 'M':
32547 newPoly();
32548 x = +cmd[i][1];
32549 y = +cmd[i][2];
32550 newPos.push([w, x, y]);
32551
32552 recStart();
32553 break;
32554
32555 case 'Q':
32556 case 'S':
32557 x1 = +cmd[i][1];
32558 y1 = +cmd[i][2];
32559 x = +cmd[i][3];
32560 y = +cmd[i][4];
32561 newPos.push([w, x, y, x1, y1]); // -> iQS order
32562 break;
32563
32564 case 'C':
32565 x1 = +cmd[i][1];
32566 y1 = +cmd[i][2];
32567 x2 = +cmd[i][3];
32568 y2 = +cmd[i][4];
32569 x = +cmd[i][5];
32570 y = +cmd[i][6];
32571 newPos.push([w, x, y, x1, y1, x2, y2]); // -> iC order
32572 break;
32573
32574 case 'T':
32575 case 'L':
32576 x = +cmd[i][1];
32577 y = +cmd[i][2];
32578 newPos.push([w, x, y]);
32579 break;
32580
32581 case 'H':
32582 w = 'L'; // convert to line (for now)
32583 x = +cmd[i][1];
32584 newPos.push([w, x, y]);
32585 break;
32586
32587 case 'V':
32588 w = 'L'; // convert to line (for now)
32589 y = +cmd[i][1];
32590 newPos.push([w, x, y]);
32591 break;
32592
32593 case 'A':
32594 w = 'L'; // convert to line to handle circle
32595 var rx = +cmd[i][1];
32596 var ry = +cmd[i][2];
32597 if(!+cmd[i][4]) {
32598 rx = -rx;
32599 ry = -ry;
32600 }
32601
32602 var cenX = x - rx;
32603 var cenY = y;
32604 for(k = 1; k <= CIRCLE_SIDES / 2; k++) {
32605 var t = 2 * Math.PI * k / CIRCLE_SIDES;
32606 newPos.push([
32607 w,
32608 cenX + rx * Math.cos(t),
32609 cenY + ry * Math.sin(t)
32610 ]);
32611 }
32612 break;
32613
32614 case 'Z':
32615 if(x !== initX || y !== initY) {
32616 x = initX;
32617 y = initY;
32618 newPos.push([w, x, y]);
32619 }
32620 break;
32621 }
32622
32623 var domain = (plotinfo || {}).domain;
32624 var size = gd._fullLayout._size;
32625 var xPixelSized = plotinfo && plotinfo.xsizemode === 'pixel';
32626 var yPixelSized = plotinfo && plotinfo.ysizemode === 'pixel';
32627 var noOffset = isActiveShape === false;
32628
32629 for(var j = 0; j < newPos.length; j++) {
32630 for(k = 0; k + 2 < 7; k += 2) {
32631 var _x = newPos[j][k + 1];
32632 var _y = newPos[j][k + 2];
32633
32634 if(_x === undefined || _y === undefined) continue;
32635 // keep track of end point for Z
32636 x = _x;
32637 y = _y;
32638
32639 if(plotinfo) {
32640 if(plotinfo.xaxis && plotinfo.xaxis.p2r) {
32641 if(noOffset) _x -= plotinfo.xaxis._offset;
32642 if(xPixelSized) {
32643 _x = r2p(plotinfo.xaxis, plotinfo.xanchor) + _x;
32644 } else {
32645 _x = p2r(plotinfo.xaxis, _x);
32646 }
32647 } else {
32648 if(noOffset) _x -= size.l;
32649 if(domain) _x = domain.x[0] + _x / size.w;
32650 else _x = _x / size.w;
32651 }
32652
32653 if(plotinfo.yaxis && plotinfo.yaxis.p2r) {
32654 if(noOffset) _y -= plotinfo.yaxis._offset;
32655 if(yPixelSized) {
32656 _y = r2p(plotinfo.yaxis, plotinfo.yanchor) - _y;
32657 } else {
32658 _y = p2r(plotinfo.yaxis, _y);
32659 }
32660 } else {
32661 if(noOffset) _y -= size.t;
32662 if(domain) _y = domain.y[1] - _y / size.h;
32663 else _y = 1 - _y / size.h;
32664 }
32665 }
32666
32667 newPos[j][k + 1] = _x;
32668 newPos[j][k + 2] = _y;
32669 }
32670 polys[n].push(
32671 newPos[j].slice()
32672 );
32673 }
32674 }
32675
32676 return polys;
32677};
32678
32679function almostEq(a, b) {
32680 return Math.abs(a - b) <= 1e-6;
32681}
32682
32683function dist(a, b) {
32684 var dx = b[1] - a[1];
32685 var dy = b[2] - a[2];
32686 return Math.sqrt(
32687 dx * dx +
32688 dy * dy
32689 );
32690}
32691
32692exports.pointsShapeRectangle = function(cell) {
32693 var len = cell.length;
32694 if(len !== 5) return false;
32695
32696 for(var j = 1; j < 3; j++) {
32697 var e01 = cell[0][j] - cell[1][j];
32698 var e32 = cell[3][j] - cell[2][j];
32699
32700 if(!almostEq(e01, e32)) return false;
32701
32702 var e03 = cell[0][j] - cell[3][j];
32703 var e12 = cell[1][j] - cell[2][j];
32704 if(!almostEq(e03, e12)) return false;
32705 }
32706
32707 // N.B. rotated rectangles are not valid rects since rotation is not supported in shapes for now.
32708 if(
32709 !almostEq(cell[0][1], cell[1][1]) &&
32710 !almostEq(cell[0][1], cell[3][1])
32711 ) return false;
32712
32713 // reject cases with zero area
32714 return !!(
32715 dist(cell[0], cell[1]) *
32716 dist(cell[0], cell[3])
32717 );
32718};
32719
32720exports.pointsShapeEllipse = function(cell) {
32721 var len = cell.length;
32722 if(len !== CIRCLE_SIDES + 1) return false;
32723
32724 // opposite diagonals should be the same
32725 len = CIRCLE_SIDES;
32726 for(var i = 0; i < len; i++) {
32727 var k = (len * 2 - i) % len;
32728
32729 var k2 = (len / 2 + k) % len;
32730 var i2 = (len / 2 + i) % len;
32731
32732 if(!almostEq(
32733 dist(cell[i], cell[i2]),
32734 dist(cell[k], cell[k2])
32735 )) return false;
32736 }
32737 return true;
32738};
32739
32740exports.handleEllipse = function(isEllipse, start, end) {
32741 if(!isEllipse) return [start, end]; // i.e. case of line
32742
32743 var pos = exports.ellipseOver({
32744 x0: start[0],
32745 y0: start[1],
32746 x1: end[0],
32747 y1: end[1]
32748 });
32749
32750 var cx = (pos.x1 + pos.x0) / 2;
32751 var cy = (pos.y1 + pos.y0) / 2;
32752 var rx = (pos.x1 - pos.x0) / 2;
32753 var ry = (pos.y1 - pos.y0) / 2;
32754
32755 // make a circle when one dimension is zero
32756 if(!rx) rx = ry = ry / SQRT2;
32757 if(!ry) ry = rx = rx / SQRT2;
32758
32759 var cell = [];
32760 for(var i = 0; i < CIRCLE_SIDES; i++) {
32761 var t = i * 2 * Math.PI / CIRCLE_SIDES;
32762 cell.push([
32763 cx + rx * Math.cos(t),
32764 cy + ry * Math.sin(t),
32765 ]);
32766 }
32767 return cell;
32768};
32769
32770exports.ellipseOver = function(pos) {
32771 var x0 = pos.x0;
32772 var y0 = pos.y0;
32773 var x1 = pos.x1;
32774 var y1 = pos.y1;
32775
32776 var dx = x1 - x0;
32777 var dy = y1 - y0;
32778
32779 x0 -= dx;
32780 y0 -= dy;
32781
32782 var cx = (x0 + x1) / 2;
32783 var cy = (y0 + y1) / 2;
32784
32785 var scale = SQRT2;
32786 dx *= scale;
32787 dy *= scale;
32788
32789 return {
32790 x0: cx - dx,
32791 y0: cy - dy,
32792 x1: cx + dx,
32793 y1: cy + dy
32794 };
32795};
32796
32797},{"../../../plots/cartesian/helpers":233,"./constants":135,"parse-svg-path":25}],139:[function(_dereq_,module,exports){
32798/**
32799* Copyright 2012-2020, Plotly, Inc.
32800* All rights reserved.
32801*
32802* This source code is licensed under the MIT license found in the
32803* LICENSE file in the root directory of this source tree.
32804*/
32805
32806
32807'use strict';
32808
32809var dragHelpers = _dereq_('../../dragelement/helpers');
32810var drawMode = dragHelpers.drawMode;
32811var openMode = dragHelpers.openMode;
32812
32813var constants = _dereq_('./constants');
32814var i000 = constants.i000;
32815var i090 = constants.i090;
32816var i180 = constants.i180;
32817var i270 = constants.i270;
32818var cos45 = constants.cos45;
32819var sin45 = constants.sin45;
32820
32821var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
32822var p2r = cartesianHelpers.p2r;
32823var r2p = cartesianHelpers.r2p;
32824
32825var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
32826var clearSelect = handleOutline.clearSelect;
32827
32828var helpers = _dereq_('./helpers');
32829var readPaths = helpers.readPaths;
32830var writePaths = helpers.writePaths;
32831var ellipseOver = helpers.ellipseOver;
32832
32833
32834module.exports = function newShapes(outlines, dragOptions) {
32835 if(!outlines.length) return;
32836 var e = outlines[0][0]; // pick first
32837 if(!e) return;
32838 var d = e.getAttribute('d');
32839
32840 var gd = dragOptions.gd;
32841 var drwStyle = gd._fullLayout.newshape;
32842
32843 var plotinfo = dragOptions.plotinfo;
32844 var xaxis = plotinfo.xaxis;
32845 var yaxis = plotinfo.yaxis;
32846 var xPaper = !!plotinfo.domain || !plotinfo.xaxis;
32847 var yPaper = !!plotinfo.domain || !plotinfo.yaxis;
32848
32849 var isActiveShape = dragOptions.isActiveShape;
32850 var dragmode = dragOptions.dragmode;
32851
32852 var shapes = (gd.layout || {}).shapes || [];
32853
32854 if(!drawMode(dragmode) && isActiveShape !== undefined) {
32855 var id = gd._fullLayout._activeShapeIndex;
32856 if(id < shapes.length) {
32857 switch(gd._fullLayout.shapes[id].type) {
32858 case 'rect':
32859 dragmode = 'drawrect';
32860 break;
32861 case 'circle':
32862 dragmode = 'drawcircle';
32863 break;
32864 case 'line':
32865 dragmode = 'drawline';
32866 break;
32867 case 'path':
32868 var path = shapes[id].path || '';
32869 if(path[path.length - 1] === 'Z') {
32870 dragmode = 'drawclosedpath';
32871 } else {
32872 dragmode = 'drawopenpath';
32873 }
32874 break;
32875 }
32876 }
32877 }
32878
32879 var isOpenMode = openMode(dragmode);
32880
32881 var polygons = readPaths(d, gd, plotinfo, isActiveShape);
32882
32883 var newShape = {
32884 editable: true,
32885
32886 xref: xPaper ? 'paper' : xaxis._id,
32887 yref: yPaper ? 'paper' : yaxis._id,
32888
32889 layer: drwStyle.layer,
32890 opacity: drwStyle.opacity,
32891 line: {
32892 color: drwStyle.line.color,
32893 width: drwStyle.line.width,
32894 dash: drwStyle.line.dash
32895 }
32896 };
32897
32898 if(!isOpenMode) {
32899 newShape.fillcolor = drwStyle.fillcolor;
32900 newShape.fillrule = drwStyle.fillrule;
32901 }
32902
32903 var cell;
32904 // line, rect and circle can be in one cell
32905 // only define cell if there is single cell
32906 if(polygons.length === 1) cell = polygons[0];
32907
32908 if(
32909 cell &&
32910 dragmode === 'drawrect'
32911 ) {
32912 newShape.type = 'rect';
32913 newShape.x0 = cell[0][1];
32914 newShape.y0 = cell[0][2];
32915 newShape.x1 = cell[2][1];
32916 newShape.y1 = cell[2][2];
32917 } else if(
32918 cell &&
32919 dragmode === 'drawline'
32920 ) {
32921 newShape.type = 'line';
32922 newShape.x0 = cell[0][1];
32923 newShape.y0 = cell[0][2];
32924 newShape.x1 = cell[1][1];
32925 newShape.y1 = cell[1][2];
32926 } else if(
32927 cell &&
32928 dragmode === 'drawcircle'
32929 ) {
32930 newShape.type = 'circle'; // an ellipse!
32931
32932 var xA = cell[i000][1];
32933 var xB = cell[i090][1];
32934 var xC = cell[i180][1];
32935 var xD = cell[i270][1];
32936
32937 var yA = cell[i000][2];
32938 var yB = cell[i090][2];
32939 var yC = cell[i180][2];
32940 var yD = cell[i270][2];
32941
32942 var xDateOrLog = plotinfo.xaxis && (
32943 plotinfo.xaxis.type === 'date' ||
32944 plotinfo.xaxis.type === 'log'
32945 );
32946
32947 var yDateOrLog = plotinfo.yaxis && (
32948 plotinfo.yaxis.type === 'date' ||
32949 plotinfo.yaxis.type === 'log'
32950 );
32951
32952 if(xDateOrLog) {
32953 xA = r2p(plotinfo.xaxis, xA);
32954 xB = r2p(plotinfo.xaxis, xB);
32955 xC = r2p(plotinfo.xaxis, xC);
32956 xD = r2p(plotinfo.xaxis, xD);
32957 }
32958
32959 if(yDateOrLog) {
32960 yA = r2p(plotinfo.yaxis, yA);
32961 yB = r2p(plotinfo.yaxis, yB);
32962 yC = r2p(plotinfo.yaxis, yC);
32963 yD = r2p(plotinfo.yaxis, yD);
32964 }
32965
32966 var x0 = (xB + xD) / 2;
32967 var y0 = (yA + yC) / 2;
32968 var rx = (xD - xB + xC - xA) / 2;
32969 var ry = (yD - yB + yC - yA) / 2;
32970 var pos = ellipseOver({
32971 x0: x0,
32972 y0: y0,
32973 x1: x0 + rx * cos45,
32974 y1: y0 + ry * sin45
32975 });
32976
32977 if(xDateOrLog) {
32978 pos.x0 = p2r(plotinfo.xaxis, pos.x0);
32979 pos.x1 = p2r(plotinfo.xaxis, pos.x1);
32980 }
32981
32982 if(yDateOrLog) {
32983 pos.y0 = p2r(plotinfo.yaxis, pos.y0);
32984 pos.y1 = p2r(plotinfo.yaxis, pos.y1);
32985 }
32986
32987 newShape.x0 = pos.x0;
32988 newShape.y0 = pos.y0;
32989 newShape.x1 = pos.x1;
32990 newShape.y1 = pos.y1;
32991 } else {
32992 newShape.type = 'path';
32993 if(xaxis && yaxis) fixDatesForPaths(polygons, xaxis, yaxis);
32994 newShape.path = writePaths(polygons);
32995 cell = null;
32996 }
32997
32998 clearSelect(gd);
32999
33000 var editHelpers = dragOptions.editHelpers;
33001 var modifyItem = (editHelpers || {}).modifyItem;
33002
33003 var allShapes = [];
33004 for(var q = 0; q < shapes.length; q++) {
33005 var beforeEdit = gd._fullLayout.shapes[q];
33006 allShapes[q] = beforeEdit._input;
33007
33008 if(
33009 isActiveShape !== undefined &&
33010 q === gd._fullLayout._activeShapeIndex
33011 ) {
33012 var afterEdit = newShape;
33013
33014 switch(beforeEdit.type) {
33015 case 'line':
33016 case 'rect':
33017 case 'circle':
33018 modifyItem('x0', afterEdit.x0);
33019 modifyItem('x1', afterEdit.x1);
33020 modifyItem('y0', afterEdit.y0);
33021 modifyItem('y1', afterEdit.y1);
33022 break;
33023
33024 case 'path':
33025 modifyItem('path', afterEdit.path);
33026 break;
33027 }
33028 }
33029 }
33030
33031 if(isActiveShape === undefined) {
33032 allShapes.push(newShape); // add new shape
33033 return allShapes;
33034 }
33035
33036 return editHelpers ? editHelpers.getUpdateObj() : {};
33037};
33038
33039function fixDatesForPaths(polygons, xaxis, yaxis) {
33040 var xIsDate = xaxis.type === 'date';
33041 var yIsDate = yaxis.type === 'date';
33042 if(!xIsDate && !yIsDate) return polygons;
33043
33044 for(var i = 0; i < polygons.length; i++) {
33045 for(var j = 0; j < polygons[i].length; j++) {
33046 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
33047 if(xIsDate) polygons[i][j][k + 1] = polygons[i][j][k + 1].replace(' ', '_');
33048 if(yIsDate) polygons[i][j][k + 2] = polygons[i][j][k + 2].replace(' ', '_');
33049 }
33050 }
33051 }
33052
33053 return polygons;
33054}
33055
33056},{"../../../plots/cartesian/handle_outline":232,"../../../plots/cartesian/helpers":233,"../../dragelement/helpers":70,"./constants":135,"./helpers":138}],140:[function(_dereq_,module,exports){
33057/**
33058* Copyright 2012-2020, Plotly, Inc.
33059* All rights reserved.
33060*
33061* This source code is licensed under the MIT license found in the
33062* LICENSE file in the root directory of this source tree.
33063*/
33064
33065
33066'use strict';
33067
33068var constants = _dereq_('./constants');
33069
33070var Lib = _dereq_('../../lib');
33071
33072// special position conversion functions... category axis positions can't be
33073// specified by their data values, because they don't make a continuous mapping.
33074// so these have to be specified in terms of the category serial numbers,
33075// but can take fractional values. Other axis types we specify position based on
33076// the actual data values.
33077// TODO: in V2.0 (when log axis ranges are in data units) range and shape position
33078// will be identical, so rangeToShapePosition and shapePositionToRange can be
33079// removed entirely.
33080
33081exports.rangeToShapePosition = function(ax) {
33082 return (ax.type === 'log') ? ax.r2d : function(v) { return v; };
33083};
33084
33085exports.shapePositionToRange = function(ax) {
33086 return (ax.type === 'log') ? ax.d2r : function(v) { return v; };
33087};
33088
33089exports.decodeDate = function(convertToPx) {
33090 return function(v) {
33091 if(v.replace) v = v.replace('_', ' ');
33092 return convertToPx(v);
33093 };
33094};
33095
33096exports.encodeDate = function(convertToDate) {
33097 return function(v) { return convertToDate(v).replace(' ', '_'); };
33098};
33099
33100exports.extractPathCoords = function(path, paramsToUse) {
33101 var extractedCoordinates = [];
33102
33103 var segments = path.match(constants.segmentRE);
33104 segments.forEach(function(segment) {
33105 var relevantParamIdx = paramsToUse[segment.charAt(0)].drawn;
33106 if(relevantParamIdx === undefined) return;
33107
33108 var params = segment.substr(1).match(constants.paramRE);
33109 if(!params || params.length < relevantParamIdx) return;
33110
33111 extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx]));
33112 });
33113
33114 return extractedCoordinates;
33115};
33116
33117exports.getDataToPixel = function(gd, axis, isVertical) {
33118 var gs = gd._fullLayout._size;
33119 var dataToPixel;
33120
33121 if(axis) {
33122 var d2r = exports.shapePositionToRange(axis);
33123
33124 dataToPixel = function(v) {
33125 return axis._offset + axis.r2p(d2r(v, true));
33126 };
33127
33128 if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel);
33129 } else if(isVertical) {
33130 dataToPixel = function(v) { return gs.t + gs.h * (1 - v); };
33131 } else {
33132 dataToPixel = function(v) { return gs.l + gs.w * v; };
33133 }
33134
33135 return dataToPixel;
33136};
33137
33138exports.getPixelToData = function(gd, axis, isVertical) {
33139 var gs = gd._fullLayout._size;
33140 var pixelToData;
33141
33142 if(axis) {
33143 var r2d = exports.rangeToShapePosition(axis);
33144 pixelToData = function(p) { return r2d(axis.p2r(p - axis._offset)); };
33145 } else if(isVertical) {
33146 pixelToData = function(p) { return 1 - (p - gs.t) / gs.h; };
33147 } else {
33148 pixelToData = function(p) { return (p - gs.l) / gs.w; };
33149 }
33150
33151 return pixelToData;
33152};
33153
33154/**
33155 * Based on the given stroke width, rounds the passed
33156 * position value to represent either a full or half pixel.
33157 *
33158 * In case of an odd stroke width (e.g. 1), this measure ensures
33159 * that a stroke positioned at the returned position isn't rendered
33160 * blurry due to anti-aliasing.
33161 *
33162 * In case of an even stroke width (e.g. 2), this measure ensures
33163 * that the position value is transformed to a full pixel value
33164 * so that anti-aliasing doesn't take effect either.
33165 *
33166 * @param {number} pos The raw position value to be transformed
33167 * @param {number} strokeWidth The stroke width
33168 * @returns {number} either an integer or a .5 decimal number
33169 */
33170exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) {
33171 var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1;
33172 var posValAsInt = Math.round(pos);
33173
33174 return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt;
33175};
33176
33177exports.makeOptionsAndPlotinfo = function(gd, index) {
33178 var options = gd._fullLayout.shapes[index] || {};
33179
33180 var plotinfo = gd._fullLayout._plots[options.xref + options.yref];
33181 var hasPlotinfo = !!plotinfo;
33182 if(hasPlotinfo) {
33183 plotinfo._hadPlotinfo = true;
33184 } else {
33185 plotinfo = {};
33186 if(options.xref && options.xref !== 'paper') plotinfo.xaxis = gd._fullLayout[options.xref + 'axis'];
33187 if(options.yref && options.yref !== 'paper') plotinfo.yaxis = gd._fullLayout[options.yref + 'axis'];
33188 }
33189
33190 plotinfo.xsizemode = options.xsizemode;
33191 plotinfo.ysizemode = options.ysizemode;
33192 plotinfo.xanchor = options.xanchor;
33193 plotinfo.yanchor = options.yanchor;
33194
33195 return {
33196 options: options,
33197 plotinfo: plotinfo
33198 };
33199};
33200
33201},{"../../lib":178,"./constants":131}],141:[function(_dereq_,module,exports){
33202/**
33203* Copyright 2012-2020, Plotly, Inc.
33204* All rights reserved.
33205*
33206* This source code is licensed under the MIT license found in the
33207* LICENSE file in the root directory of this source tree.
33208*/
33209
33210
33211'use strict';
33212
33213var drawModule = _dereq_('./draw');
33214
33215module.exports = {
33216 moduleType: 'component',
33217 name: 'shapes',
33218
33219 layoutAttributes: _dereq_('./attributes'),
33220 supplyLayoutDefaults: _dereq_('./defaults'),
33221 supplyDrawNewShapeDefaults: _dereq_('./draw_newshape/defaults'),
33222 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'),
33223
33224 calcAutorange: _dereq_('./calc_autorange'),
33225 draw: drawModule.draw,
33226 drawOne: drawModule.drawOne
33227};
33228
33229},{"../../plots/cartesian/include_components":234,"./attributes":129,"./calc_autorange":130,"./defaults":132,"./draw":133,"./draw_newshape/defaults":136}],142:[function(_dereq_,module,exports){
33230/**
33231* Copyright 2012-2020, Plotly, Inc.
33232* All rights reserved.
33233*
33234* This source code is licensed under the MIT license found in the
33235* LICENSE file in the root directory of this source tree.
33236*/
33237
33238'use strict';
33239
33240var fontAttrs = _dereq_('../../plots/font_attributes');
33241var padAttrs = _dereq_('../../plots/pad_attributes');
33242var extendDeepAll = _dereq_('../../lib/extend').extendDeepAll;
33243var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
33244var animationAttrs = _dereq_('../../plots/animation_attributes');
33245var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
33246var constants = _dereq_('./constants');
33247
33248var stepsAttrs = templatedArray('step', {
33249 visible: {
33250 valType: 'boolean',
33251
33252 dflt: true,
33253
33254 },
33255 method: {
33256 valType: 'enumerated',
33257 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
33258 dflt: 'restyle',
33259
33260
33261 },
33262 args: {
33263 valType: 'info_array',
33264
33265 freeLength: true,
33266 items: [
33267 { valType: 'any' },
33268 { valType: 'any' },
33269 { valType: 'any' }
33270 ],
33271
33272 },
33273 label: {
33274 valType: 'string',
33275
33276
33277 },
33278 value: {
33279 valType: 'string',
33280
33281
33282 },
33283 execute: {
33284 valType: 'boolean',
33285
33286 dflt: true,
33287
33288 }
33289});
33290
33291module.exports = overrideAll(templatedArray('slider', {
33292 visible: {
33293 valType: 'boolean',
33294
33295 dflt: true,
33296
33297 },
33298
33299 active: {
33300 valType: 'number',
33301
33302 min: 0,
33303 dflt: 0,
33304
33305 },
33306
33307 steps: stepsAttrs,
33308
33309 lenmode: {
33310 valType: 'enumerated',
33311 values: ['fraction', 'pixels'],
33312
33313 dflt: 'fraction',
33314
33315 },
33316 len: {
33317 valType: 'number',
33318 min: 0,
33319 dflt: 1,
33320
33321
33322 },
33323 x: {
33324 valType: 'number',
33325 min: -2,
33326 max: 3,
33327 dflt: 0,
33328
33329
33330 },
33331 pad: extendDeepAll(padAttrs({editType: 'arraydraw'}), {
33332
33333 }, {t: {dflt: 20}}),
33334 xanchor: {
33335 valType: 'enumerated',
33336 values: ['auto', 'left', 'center', 'right'],
33337 dflt: 'left',
33338
33339
33340 },
33341 y: {
33342 valType: 'number',
33343 min: -2,
33344 max: 3,
33345 dflt: 0,
33346
33347
33348 },
33349 yanchor: {
33350 valType: 'enumerated',
33351 values: ['auto', 'top', 'middle', 'bottom'],
33352 dflt: 'top',
33353
33354
33355 },
33356
33357 transition: {
33358 duration: {
33359 valType: 'number',
33360
33361 min: 0,
33362 dflt: 150,
33363
33364 },
33365 easing: {
33366 valType: 'enumerated',
33367 values: animationAttrs.transition.easing.values,
33368
33369 dflt: 'cubic-in-out',
33370
33371 }
33372 },
33373
33374 currentvalue: {
33375 visible: {
33376 valType: 'boolean',
33377
33378 dflt: true,
33379
33380 },
33381
33382 xanchor: {
33383 valType: 'enumerated',
33384 values: ['left', 'center', 'right'],
33385 dflt: 'left',
33386
33387
33388 },
33389
33390 offset: {
33391 valType: 'number',
33392 dflt: 10,
33393
33394
33395 },
33396
33397 prefix: {
33398 valType: 'string',
33399
33400
33401 },
33402
33403 suffix: {
33404 valType: 'string',
33405
33406
33407 },
33408
33409 font: fontAttrs({
33410
33411 })
33412 },
33413
33414 font: fontAttrs({
33415
33416 }),
33417
33418 activebgcolor: {
33419 valType: 'color',
33420
33421 dflt: constants.gripBgActiveColor,
33422
33423 },
33424 bgcolor: {
33425 valType: 'color',
33426
33427 dflt: constants.railBgColor,
33428
33429 },
33430 bordercolor: {
33431 valType: 'color',
33432 dflt: constants.railBorderColor,
33433
33434
33435 },
33436 borderwidth: {
33437 valType: 'number',
33438 min: 0,
33439 dflt: constants.railBorderWidth,
33440
33441
33442 },
33443 ticklen: {
33444 valType: 'number',
33445 min: 0,
33446 dflt: constants.tickLength,
33447
33448
33449 },
33450 tickcolor: {
33451 valType: 'color',
33452 dflt: constants.tickColor,
33453
33454
33455 },
33456 tickwidth: {
33457 valType: 'number',
33458 min: 0,
33459 dflt: 1,
33460
33461
33462 },
33463 minorticklen: {
33464 valType: 'number',
33465 min: 0,
33466 dflt: constants.minorTickLength,
33467
33468
33469 }
33470}), 'arraydraw', 'from-root');
33471
33472},{"../../lib/extend":173,"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../../plots/animation_attributes":217,"../../plots/font_attributes":250,"../../plots/pad_attributes":255,"./constants":143}],143:[function(_dereq_,module,exports){
33473/**
33474* Copyright 2012-2020, Plotly, Inc.
33475* All rights reserved.
33476*
33477* This source code is licensed under the MIT license found in the
33478* LICENSE file in the root directory of this source tree.
33479*/
33480
33481
33482'use strict';
33483
33484
33485module.exports = {
33486
33487 // layout attribute name
33488 name: 'sliders',
33489
33490 // class names
33491 containerClassName: 'slider-container',
33492 groupClassName: 'slider-group',
33493 inputAreaClass: 'slider-input-area',
33494 railRectClass: 'slider-rail-rect',
33495 railTouchRectClass: 'slider-rail-touch-rect',
33496 gripRectClass: 'slider-grip-rect',
33497 tickRectClass: 'slider-tick-rect',
33498 inputProxyClass: 'slider-input-proxy',
33499 labelsClass: 'slider-labels',
33500 labelGroupClass: 'slider-label-group',
33501 labelClass: 'slider-label',
33502 currentValueClass: 'slider-current-value',
33503
33504 railHeight: 5,
33505
33506 // DOM attribute name in button group keeping track
33507 // of active update menu
33508 menuIndexAttrName: 'slider-active-index',
33509
33510 // id root pass to Plots.autoMargin
33511 autoMarginIdRoot: 'slider-',
33512
33513 // min item width / height
33514 minWidth: 30,
33515 minHeight: 30,
33516
33517 // padding around item text
33518 textPadX: 40,
33519
33520 // arrow offset off right edge
33521 arrowOffsetX: 4,
33522
33523 railRadius: 2,
33524 railWidth: 5,
33525 railBorder: 4,
33526 railBorderWidth: 1,
33527 railBorderColor: '#bec8d9',
33528 railBgColor: '#f8fafc',
33529
33530 // The distance of the rail from the edge of the touchable area
33531 // Slightly less than the step inset because of the curved edges
33532 // of the rail
33533 railInset: 8,
33534
33535 // The distance from the extremal tick marks to the edge of the
33536 // touchable area. This is basically the same as the grip radius,
33537 // but for other styles it wouldn't really need to be.
33538 stepInset: 10,
33539
33540 gripRadius: 10,
33541 gripWidth: 20,
33542 gripHeight: 20,
33543 gripBorder: 20,
33544 gripBorderWidth: 1,
33545 gripBorderColor: '#bec8d9',
33546 gripBgColor: '#f6f8fa',
33547 gripBgActiveColor: '#dbdde0',
33548
33549 labelPadding: 8,
33550 labelOffset: 0,
33551
33552 tickWidth: 1,
33553 tickColor: '#333',
33554 tickOffset: 25,
33555 tickLength: 7,
33556
33557 minorTickOffset: 25,
33558 minorTickColor: '#333',
33559 minorTickLength: 4,
33560
33561 // Extra space below the current value label:
33562 currentValuePadding: 8,
33563 currentValueInset: 0,
33564};
33565
33566},{}],144:[function(_dereq_,module,exports){
33567/**
33568* Copyright 2012-2020, Plotly, Inc.
33569* All rights reserved.
33570*
33571* This source code is licensed under the MIT license found in the
33572* LICENSE file in the root directory of this source tree.
33573*/
33574
33575'use strict';
33576
33577var Lib = _dereq_('../../lib');
33578var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
33579
33580var attributes = _dereq_('./attributes');
33581var constants = _dereq_('./constants');
33582
33583var name = constants.name;
33584var stepAttrs = attributes.steps;
33585
33586
33587module.exports = function slidersDefaults(layoutIn, layoutOut) {
33588 handleArrayContainerDefaults(layoutIn, layoutOut, {
33589 name: name,
33590 handleItemDefaults: sliderDefaults
33591 });
33592};
33593
33594function sliderDefaults(sliderIn, sliderOut, layoutOut) {
33595 function coerce(attr, dflt) {
33596 return Lib.coerce(sliderIn, sliderOut, attributes, attr, dflt);
33597 }
33598
33599 var steps = handleArrayContainerDefaults(sliderIn, sliderOut, {
33600 name: 'steps',
33601 handleItemDefaults: stepDefaults
33602 });
33603
33604 var stepCount = 0;
33605 for(var i = 0; i < steps.length; i++) {
33606 if(steps[i].visible) stepCount++;
33607 }
33608
33609 var visible;
33610 // If it has fewer than two options, it's not really a slider
33611 if(stepCount < 2) visible = sliderOut.visible = false;
33612 else visible = coerce('visible');
33613 if(!visible) return;
33614
33615 sliderOut._stepCount = stepCount;
33616 var visSteps = sliderOut._visibleSteps = Lib.filterVisible(steps);
33617
33618 var active = coerce('active');
33619 if(!(steps[active] || {}).visible) sliderOut.active = visSteps[0]._index;
33620
33621 coerce('x');
33622 coerce('y');
33623 Lib.noneOrAll(sliderIn, sliderOut, ['x', 'y']);
33624
33625 coerce('xanchor');
33626 coerce('yanchor');
33627
33628 coerce('len');
33629 coerce('lenmode');
33630
33631 coerce('pad.t');
33632 coerce('pad.r');
33633 coerce('pad.b');
33634 coerce('pad.l');
33635
33636 Lib.coerceFont(coerce, 'font', layoutOut.font);
33637
33638 var currentValueIsVisible = coerce('currentvalue.visible');
33639
33640 if(currentValueIsVisible) {
33641 coerce('currentvalue.xanchor');
33642 coerce('currentvalue.prefix');
33643 coerce('currentvalue.suffix');
33644 coerce('currentvalue.offset');
33645
33646 Lib.coerceFont(coerce, 'currentvalue.font', sliderOut.font);
33647 }
33648
33649 coerce('transition.duration');
33650 coerce('transition.easing');
33651
33652 coerce('bgcolor');
33653 coerce('activebgcolor');
33654 coerce('bordercolor');
33655 coerce('borderwidth');
33656 coerce('ticklen');
33657 coerce('tickwidth');
33658 coerce('tickcolor');
33659 coerce('minorticklen');
33660}
33661
33662function stepDefaults(valueIn, valueOut) {
33663 function coerce(attr, dflt) {
33664 return Lib.coerce(valueIn, valueOut, stepAttrs, attr, dflt);
33665 }
33666
33667 var visible;
33668 if(valueIn.method !== 'skip' && !Array.isArray(valueIn.args)) {
33669 visible = valueOut.visible = false;
33670 } else visible = coerce('visible');
33671
33672 if(visible) {
33673 coerce('method');
33674 coerce('args');
33675 var label = coerce('label', 'step-' + valueOut._index);
33676 coerce('value', label);
33677 coerce('execute');
33678 }
33679}
33680
33681},{"../../lib":178,"../../plots/array_container_defaults":218,"./attributes":142,"./constants":143}],145:[function(_dereq_,module,exports){
33682/**
33683* Copyright 2012-2020, Plotly, Inc.
33684* All rights reserved.
33685*
33686* This source code is licensed under the MIT license found in the
33687* LICENSE file in the root directory of this source tree.
33688*/
33689
33690'use strict';
33691
33692var d3 = _dereq_('d3');
33693
33694var Plots = _dereq_('../../plots/plots');
33695var Color = _dereq_('../color');
33696var Drawing = _dereq_('../drawing');
33697var Lib = _dereq_('../../lib');
33698var svgTextUtils = _dereq_('../../lib/svg_text_utils');
33699var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
33700
33701var constants = _dereq_('./constants');
33702var alignmentConstants = _dereq_('../../constants/alignment');
33703var LINE_SPACING = alignmentConstants.LINE_SPACING;
33704var FROM_TL = alignmentConstants.FROM_TL;
33705var FROM_BR = alignmentConstants.FROM_BR;
33706
33707module.exports = function draw(gd) {
33708 var fullLayout = gd._fullLayout;
33709 var sliderData = makeSliderData(fullLayout, gd);
33710
33711 // draw a container for *all* sliders:
33712 var sliders = fullLayout._infolayer
33713 .selectAll('g.' + constants.containerClassName)
33714 .data(sliderData.length > 0 ? [0] : []);
33715
33716 sliders.enter().append('g')
33717 .classed(constants.containerClassName, true)
33718 .style('cursor', 'ew-resize');
33719
33720 function clearSlider(sliderOpts) {
33721 if(sliderOpts._commandObserver) {
33722 sliderOpts._commandObserver.remove();
33723 delete sliderOpts._commandObserver;
33724 }
33725
33726 // Most components don't need to explicitly remove autoMargin, because
33727 // marginPushers does this - but slider updates don't go through
33728 // a full replot so we need to explicitly remove it.
33729 Plots.autoMargin(gd, autoMarginId(sliderOpts));
33730 }
33731
33732 sliders.exit().each(function() {
33733 d3.select(this).selectAll('g.' + constants.groupClassName)
33734 .each(clearSlider);
33735 })
33736 .remove();
33737
33738 // Return early if no menus visible:
33739 if(sliderData.length === 0) return;
33740
33741 var sliderGroups = sliders.selectAll('g.' + constants.groupClassName)
33742 .data(sliderData, keyFunction);
33743
33744 sliderGroups.enter().append('g')
33745 .classed(constants.groupClassName, true);
33746
33747 sliderGroups.exit()
33748 .each(clearSlider)
33749 .remove();
33750
33751 // Find the dimensions of the sliders:
33752 for(var i = 0; i < sliderData.length; i++) {
33753 var sliderOpts = sliderData[i];
33754 findDimensions(gd, sliderOpts);
33755 }
33756
33757 sliderGroups.each(function(sliderOpts) {
33758 var gSlider = d3.select(this);
33759
33760 computeLabelSteps(sliderOpts);
33761
33762 Plots.manageCommandObserver(gd, sliderOpts, sliderOpts._visibleSteps, function(data) {
33763 // NB: Same as below. This is *not* always the same as sliderOpts since
33764 // if a new set of steps comes in, the reference in this callback would
33765 // be invalid. We need to refetch it from the slider group, which is
33766 // the join data that creates this slider. So if this slider still exists,
33767 // the group should be valid, *to the best of my knowledge.* If not,
33768 // we'd have to look it up by d3 data join index/key.
33769 var opts = gSlider.data()[0];
33770
33771 if(opts.active === data.index) return;
33772 if(opts._dragging) return;
33773
33774 setActive(gd, gSlider, opts, data.index, false, true);
33775 });
33776
33777 drawSlider(gd, d3.select(this), sliderOpts);
33778 });
33779};
33780
33781function autoMarginId(sliderOpts) {
33782 return constants.autoMarginIdRoot + sliderOpts._index;
33783}
33784
33785// This really only just filters by visibility:
33786function makeSliderData(fullLayout, gd) {
33787 var contOpts = fullLayout[constants.name];
33788 var sliderData = [];
33789
33790 for(var i = 0; i < contOpts.length; i++) {
33791 var item = contOpts[i];
33792 if(!item.visible) continue;
33793 item._gd = gd;
33794 sliderData.push(item);
33795 }
33796
33797 return sliderData;
33798}
33799
33800// This is set in the defaults step:
33801function keyFunction(opts) {
33802 return opts._index;
33803}
33804
33805// Compute the dimensions (mutates sliderOpts):
33806function findDimensions(gd, sliderOpts) {
33807 var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass)
33808 .data(sliderOpts._visibleSteps);
33809
33810 sliderLabels.enter().append('g')
33811 .classed(constants.labelGroupClass, true);
33812
33813 // loop over fake buttons to find width / height
33814 var maxLabelWidth = 0;
33815 var labelHeight = 0;
33816 sliderLabels.each(function(stepOpts) {
33817 var labelGroup = d3.select(this);
33818
33819 var text = drawLabel(labelGroup, {step: stepOpts}, sliderOpts);
33820
33821 var textNode = text.node();
33822 if(textNode) {
33823 var bBox = Drawing.bBox(textNode);
33824 labelHeight = Math.max(labelHeight, bBox.height);
33825 maxLabelWidth = Math.max(maxLabelWidth, bBox.width);
33826 }
33827 });
33828
33829 sliderLabels.remove();
33830
33831 var dims = sliderOpts._dims = {};
33832
33833 dims.inputAreaWidth = Math.max(
33834 constants.railWidth,
33835 constants.gripHeight
33836 );
33837
33838 // calculate some overall dimensions - some of these are needed for
33839 // calculating the currentValue dimensions
33840 var graphSize = gd._fullLayout._size;
33841 dims.lx = graphSize.l + graphSize.w * sliderOpts.x;
33842 dims.ly = graphSize.t + graphSize.h * (1 - sliderOpts.y);
33843
33844 if(sliderOpts.lenmode === 'fraction') {
33845 // fraction:
33846 dims.outerLength = Math.round(graphSize.w * sliderOpts.len);
33847 } else {
33848 // pixels:
33849 dims.outerLength = sliderOpts.len;
33850 }
33851
33852 // The length of the rail, *excluding* padding on either end:
33853 dims.inputAreaStart = 0;
33854 dims.inputAreaLength = Math.round(dims.outerLength - sliderOpts.pad.l - sliderOpts.pad.r);
33855
33856 var textableInputLength = dims.inputAreaLength - 2 * constants.stepInset;
33857 var availableSpacePerLabel = textableInputLength / (sliderOpts._stepCount - 1);
33858 var computedSpacePerLabel = maxLabelWidth + constants.labelPadding;
33859 dims.labelStride = Math.max(1, Math.ceil(computedSpacePerLabel / availableSpacePerLabel));
33860 dims.labelHeight = labelHeight;
33861
33862 // loop over all possible values for currentValue to find the
33863 // area we need for it
33864 dims.currentValueMaxWidth = 0;
33865 dims.currentValueHeight = 0;
33866 dims.currentValueTotalHeight = 0;
33867 dims.currentValueMaxLines = 1;
33868
33869 if(sliderOpts.currentvalue.visible) {
33870 // Get the dimensions of the current value label:
33871 var dummyGroup = Drawing.tester.append('g');
33872
33873 sliderLabels.each(function(stepOpts) {
33874 var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label);
33875 var curValSize = (curValPrefix.node() && Drawing.bBox(curValPrefix.node())) || {width: 0, height: 0};
33876 var lines = svgTextUtils.lineCount(curValPrefix);
33877 dims.currentValueMaxWidth = Math.max(dims.currentValueMaxWidth, Math.ceil(curValSize.width));
33878 dims.currentValueHeight = Math.max(dims.currentValueHeight, Math.ceil(curValSize.height));
33879 dims.currentValueMaxLines = Math.max(dims.currentValueMaxLines, lines);
33880 });
33881
33882 dims.currentValueTotalHeight = dims.currentValueHeight + sliderOpts.currentvalue.offset;
33883
33884 dummyGroup.remove();
33885 }
33886
33887 dims.height = dims.currentValueTotalHeight + constants.tickOffset + sliderOpts.ticklen + constants.labelOffset + dims.labelHeight + sliderOpts.pad.t + sliderOpts.pad.b;
33888
33889 var xanchor = 'left';
33890 if(Lib.isRightAnchor(sliderOpts)) {
33891 dims.lx -= dims.outerLength;
33892 xanchor = 'right';
33893 }
33894 if(Lib.isCenterAnchor(sliderOpts)) {
33895 dims.lx -= dims.outerLength / 2;
33896 xanchor = 'center';
33897 }
33898
33899 var yanchor = 'top';
33900 if(Lib.isBottomAnchor(sliderOpts)) {
33901 dims.ly -= dims.height;
33902 yanchor = 'bottom';
33903 }
33904 if(Lib.isMiddleAnchor(sliderOpts)) {
33905 dims.ly -= dims.height / 2;
33906 yanchor = 'middle';
33907 }
33908
33909 dims.outerLength = Math.ceil(dims.outerLength);
33910 dims.height = Math.ceil(dims.height);
33911 dims.lx = Math.round(dims.lx);
33912 dims.ly = Math.round(dims.ly);
33913
33914 var marginOpts = {
33915 y: sliderOpts.y,
33916 b: dims.height * FROM_BR[yanchor],
33917 t: dims.height * FROM_TL[yanchor]
33918 };
33919
33920 if(sliderOpts.lenmode === 'fraction') {
33921 marginOpts.l = 0;
33922 marginOpts.xl = sliderOpts.x - sliderOpts.len * FROM_TL[xanchor];
33923 marginOpts.r = 0;
33924 marginOpts.xr = sliderOpts.x + sliderOpts.len * FROM_BR[xanchor];
33925 } else {
33926 marginOpts.x = sliderOpts.x;
33927 marginOpts.l = dims.outerLength * FROM_TL[xanchor];
33928 marginOpts.r = dims.outerLength * FROM_BR[xanchor];
33929 }
33930
33931 Plots.autoMargin(gd, autoMarginId(sliderOpts), marginOpts);
33932}
33933
33934function drawSlider(gd, sliderGroup, sliderOpts) {
33935 // This is related to the other long notes in this file regarding what happens
33936 // when slider steps disappear. This particular fix handles what happens when
33937 // the *current* slider step is removed. The drawing functions will error out
33938 // when they fail to find it, so the fix for now is that it will just draw the
33939 // slider in the first position but will not execute the command.
33940 if(!((sliderOpts.steps[sliderOpts.active] || {}).visible)) {
33941 sliderOpts.active = sliderOpts._visibleSteps[0]._index;
33942 }
33943
33944 // These are carefully ordered for proper z-ordering:
33945 sliderGroup
33946 .call(drawCurrentValue, sliderOpts)
33947 .call(drawRail, sliderOpts)
33948 .call(drawLabelGroup, sliderOpts)
33949 .call(drawTicks, sliderOpts)
33950 .call(drawTouchRect, gd, sliderOpts)
33951 .call(drawGrip, gd, sliderOpts);
33952
33953 var dims = sliderOpts._dims;
33954
33955 // Position the rectangle:
33956 Drawing.setTranslate(sliderGroup, dims.lx + sliderOpts.pad.l, dims.ly + sliderOpts.pad.t);
33957
33958 sliderGroup.call(setGripPosition, sliderOpts, false);
33959 sliderGroup.call(drawCurrentValue, sliderOpts);
33960}
33961
33962function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
33963 if(!sliderOpts.currentvalue.visible) return;
33964
33965 var dims = sliderOpts._dims;
33966 var x0, textAnchor;
33967
33968 switch(sliderOpts.currentvalue.xanchor) {
33969 case 'right':
33970 // This is anchored left and adjusted by the width of the longest label
33971 // so that the prefix doesn't move. The goal of this is to emphasize
33972 // what's actually changing and make the update less distracting.
33973 x0 = dims.inputAreaLength - constants.currentValueInset - dims.currentValueMaxWidth;
33974 textAnchor = 'left';
33975 break;
33976 case 'center':
33977 x0 = dims.inputAreaLength * 0.5;
33978 textAnchor = 'middle';
33979 break;
33980 default:
33981 x0 = constants.currentValueInset;
33982 textAnchor = 'left';
33983 }
33984
33985 var text = Lib.ensureSingle(sliderGroup, 'text', constants.labelClass, function(s) {
33986 s.classed('user-select-none', true)
33987 .attr({
33988 'text-anchor': textAnchor,
33989 'data-notex': 1
33990 });
33991 });
33992
33993 var str = sliderOpts.currentvalue.prefix ? sliderOpts.currentvalue.prefix : '';
33994
33995 if(typeof valueOverride === 'string') {
33996 str += valueOverride;
33997 } else {
33998 var curVal = sliderOpts.steps[sliderOpts.active].label;
33999 var _meta = sliderOpts._gd._fullLayout._meta;
34000 if(_meta) curVal = Lib.templateString(curVal, _meta);
34001 str += curVal;
34002 }
34003
34004 if(sliderOpts.currentvalue.suffix) {
34005 str += sliderOpts.currentvalue.suffix;
34006 }
34007
34008 text.call(Drawing.font, sliderOpts.currentvalue.font)
34009 .text(str)
34010 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
34011
34012 var lines = svgTextUtils.lineCount(text);
34013
34014 var y0 = (dims.currentValueMaxLines + 1 - lines) *
34015 sliderOpts.currentvalue.font.size * LINE_SPACING;
34016
34017 svgTextUtils.positionText(text, x0, y0);
34018
34019 return text;
34020}
34021
34022function drawGrip(sliderGroup, gd, sliderOpts) {
34023 var grip = Lib.ensureSingle(sliderGroup, 'rect', constants.gripRectClass, function(s) {
34024 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
34025 .style('pointer-events', 'all');
34026 });
34027
34028 grip.attr({
34029 width: constants.gripWidth,
34030 height: constants.gripHeight,
34031 rx: constants.gripRadius,
34032 ry: constants.gripRadius,
34033 })
34034 .call(Color.stroke, sliderOpts.bordercolor)
34035 .call(Color.fill, sliderOpts.bgcolor)
34036 .style('stroke-width', sliderOpts.borderwidth + 'px');
34037}
34038
34039function drawLabel(item, data, sliderOpts) {
34040 var text = Lib.ensureSingle(item, 'text', constants.labelClass, function(s) {
34041 s.classed('user-select-none', true)
34042 .attr({
34043 'text-anchor': 'middle',
34044 'data-notex': 1
34045 });
34046 });
34047
34048 var tx = data.step.label;
34049 var _meta = sliderOpts._gd._fullLayout._meta;
34050 if(_meta) tx = Lib.templateString(tx, _meta);
34051
34052 text.call(Drawing.font, sliderOpts.font)
34053 .text(tx)
34054 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
34055
34056 return text;
34057}
34058
34059function drawLabelGroup(sliderGroup, sliderOpts) {
34060 var labels = Lib.ensureSingle(sliderGroup, 'g', constants.labelsClass);
34061 var dims = sliderOpts._dims;
34062
34063 var labelItems = labels.selectAll('g.' + constants.labelGroupClass)
34064 .data(dims.labelSteps);
34065
34066 labelItems.enter().append('g')
34067 .classed(constants.labelGroupClass, true);
34068
34069 labelItems.exit().remove();
34070
34071 labelItems.each(function(d) {
34072 var item = d3.select(this);
34073
34074 item.call(drawLabel, d, sliderOpts);
34075
34076 Drawing.setTranslate(item,
34077 normalizedValueToPosition(sliderOpts, d.fraction),
34078 constants.tickOffset +
34079 sliderOpts.ticklen +
34080 // position is the baseline of the top line of text only, even
34081 // if the label spans multiple lines
34082 sliderOpts.font.size * LINE_SPACING +
34083 constants.labelOffset +
34084 dims.currentValueTotalHeight
34085 );
34086 });
34087}
34088
34089function handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, doTransition) {
34090 var quantizedPosition = Math.round(normalizedPosition * (sliderOpts._stepCount - 1));
34091 var quantizedIndex = sliderOpts._visibleSteps[quantizedPosition]._index;
34092
34093 if(quantizedIndex !== sliderOpts.active) {
34094 setActive(gd, sliderGroup, sliderOpts, quantizedIndex, true, doTransition);
34095 }
34096}
34097
34098function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition) {
34099 var previousActive = sliderOpts.active;
34100 sliderOpts.active = index;
34101
34102 // due to templating, it's possible this slider doesn't even exist yet
34103 arrayEditor(gd.layout, constants.name, sliderOpts)
34104 .applyUpdate('active', index);
34105
34106 var step = sliderOpts.steps[sliderOpts.active];
34107
34108 sliderGroup.call(setGripPosition, sliderOpts, doTransition);
34109 sliderGroup.call(drawCurrentValue, sliderOpts);
34110
34111 gd.emit('plotly_sliderchange', {
34112 slider: sliderOpts,
34113 step: sliderOpts.steps[sliderOpts.active],
34114 interaction: doCallback,
34115 previousActive: previousActive
34116 });
34117
34118 if(step && step.method && doCallback) {
34119 if(sliderGroup._nextMethod) {
34120 // If we've already queued up an update, just overwrite it with the most recent:
34121 sliderGroup._nextMethod.step = step;
34122 sliderGroup._nextMethod.doCallback = doCallback;
34123 sliderGroup._nextMethod.doTransition = doTransition;
34124 } else {
34125 sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
34126 sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
34127 var _step = sliderGroup._nextMethod.step;
34128 if(!_step.method) return;
34129
34130 if(_step.execute) {
34131 Plots.executeAPICommand(gd, _step.method, _step.args);
34132 }
34133
34134 sliderGroup._nextMethod = null;
34135 sliderGroup._nextMethodRaf = null;
34136 });
34137 }
34138 }
34139}
34140
34141function attachGripEvents(item, gd, sliderGroup) {
34142 var node = sliderGroup.node();
34143 var $gd = d3.select(gd);
34144
34145 // NB: This is *not* the same as sliderOpts itself! These callbacks
34146 // are in a closure so this array won't actually be correct if the
34147 // steps have changed since this was initialized. The sliderGroup,
34148 // however, has not changed since that *is* the slider, so it must
34149 // be present to receive mouse events.
34150 function getSliderOpts() {
34151 return sliderGroup.data()[0];
34152 }
34153
34154 item.on('mousedown', function() {
34155 var sliderOpts = getSliderOpts();
34156 gd.emit('plotly_sliderstart', {slider: sliderOpts});
34157
34158 var grip = sliderGroup.select('.' + constants.gripRectClass);
34159
34160 d3.event.stopPropagation();
34161 d3.event.preventDefault();
34162 grip.call(Color.fill, sliderOpts.activebgcolor);
34163
34164 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
34165 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
34166 sliderOpts._dragging = true;
34167
34168 $gd.on('mousemove', function() {
34169 var sliderOpts = getSliderOpts();
34170 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
34171 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
34172 });
34173
34174 $gd.on('mouseup', function() {
34175 var sliderOpts = getSliderOpts();
34176 sliderOpts._dragging = false;
34177 grip.call(Color.fill, sliderOpts.bgcolor);
34178 $gd.on('mouseup', null);
34179 $gd.on('mousemove', null);
34180
34181 gd.emit('plotly_sliderend', {
34182 slider: sliderOpts,
34183 step: sliderOpts.steps[sliderOpts.active]
34184 });
34185 });
34186 });
34187}
34188
34189function drawTicks(sliderGroup, sliderOpts) {
34190 var tick = sliderGroup.selectAll('rect.' + constants.tickRectClass)
34191 .data(sliderOpts._visibleSteps);
34192 var dims = sliderOpts._dims;
34193
34194 tick.enter().append('rect')
34195 .classed(constants.tickRectClass, true);
34196
34197 tick.exit().remove();
34198
34199 tick.attr({
34200 width: sliderOpts.tickwidth + 'px',
34201 'shape-rendering': 'crispEdges'
34202 });
34203
34204 tick.each(function(d, i) {
34205 var isMajor = i % dims.labelStride === 0;
34206 var item = d3.select(this);
34207
34208 item
34209 .attr({height: isMajor ? sliderOpts.ticklen : sliderOpts.minorticklen})
34210 .call(Color.fill, isMajor ? sliderOpts.tickcolor : sliderOpts.tickcolor);
34211
34212 Drawing.setTranslate(item,
34213 normalizedValueToPosition(sliderOpts, i / (sliderOpts._stepCount - 1)) - 0.5 * sliderOpts.tickwidth,
34214 (isMajor ? constants.tickOffset : constants.minorTickOffset) + dims.currentValueTotalHeight
34215 );
34216 });
34217}
34218
34219function computeLabelSteps(sliderOpts) {
34220 var dims = sliderOpts._dims;
34221 dims.labelSteps = [];
34222 var nsteps = sliderOpts._stepCount;
34223
34224 for(var i = 0; i < nsteps; i += dims.labelStride) {
34225 dims.labelSteps.push({
34226 fraction: i / (nsteps - 1),
34227 step: sliderOpts._visibleSteps[i]
34228 });
34229 }
34230}
34231
34232function setGripPosition(sliderGroup, sliderOpts, doTransition) {
34233 var grip = sliderGroup.select('rect.' + constants.gripRectClass);
34234
34235 var quantizedIndex = 0;
34236 for(var i = 0; i < sliderOpts._stepCount; i++) {
34237 if(sliderOpts._visibleSteps[i]._index === sliderOpts.active) {
34238 quantizedIndex = i;
34239 break;
34240 }
34241 }
34242
34243 var x = normalizedValueToPosition(sliderOpts, quantizedIndex / (sliderOpts._stepCount - 1));
34244
34245 // If this is true, then *this component* is already invoking its own command
34246 // and has triggered its own animation.
34247 if(sliderOpts._invokingCommand) return;
34248
34249 var el = grip;
34250 if(doTransition && sliderOpts.transition.duration > 0) {
34251 el = el.transition()
34252 .duration(sliderOpts.transition.duration)
34253 .ease(sliderOpts.transition.easing);
34254 }
34255
34256 // Drawing.setTranslate doesn't work here becasue of the transition duck-typing.
34257 // It's also not necessary because there are no other transitions to preserve.
34258 el.attr('transform', 'translate(' + (x - constants.gripWidth * 0.5) + ',' + (sliderOpts._dims.currentValueTotalHeight) + ')');
34259}
34260
34261// Convert a number from [0-1] to a pixel position relative to the slider group container:
34262function normalizedValueToPosition(sliderOpts, normalizedPosition) {
34263 var dims = sliderOpts._dims;
34264 return dims.inputAreaStart + constants.stepInset +
34265 (dims.inputAreaLength - 2 * constants.stepInset) * Math.min(1, Math.max(0, normalizedPosition));
34266}
34267
34268// Convert a position relative to the slider group to a nubmer in [0, 1]
34269function positionToNormalizedValue(sliderOpts, position) {
34270 var dims = sliderOpts._dims;
34271 return Math.min(1, Math.max(0, (position - constants.stepInset - dims.inputAreaStart) / (dims.inputAreaLength - 2 * constants.stepInset - 2 * dims.inputAreaStart)));
34272}
34273
34274function drawTouchRect(sliderGroup, gd, sliderOpts) {
34275 var dims = sliderOpts._dims;
34276 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railTouchRectClass, function(s) {
34277 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
34278 .style('pointer-events', 'all');
34279 });
34280
34281 rect.attr({
34282 width: dims.inputAreaLength,
34283 height: Math.max(dims.inputAreaWidth, constants.tickOffset + sliderOpts.ticklen + dims.labelHeight)
34284 })
34285 .call(Color.fill, sliderOpts.bgcolor)
34286 .attr('opacity', 0);
34287
34288 Drawing.setTranslate(rect, 0, dims.currentValueTotalHeight);
34289}
34290
34291function drawRail(sliderGroup, sliderOpts) {
34292 var dims = sliderOpts._dims;
34293 var computedLength = dims.inputAreaLength - constants.railInset * 2;
34294 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railRectClass);
34295
34296 rect.attr({
34297 width: computedLength,
34298 height: constants.railWidth,
34299 rx: constants.railRadius,
34300 ry: constants.railRadius,
34301 'shape-rendering': 'crispEdges'
34302 })
34303 .call(Color.stroke, sliderOpts.bordercolor)
34304 .call(Color.fill, sliderOpts.bgcolor)
34305 .style('stroke-width', sliderOpts.borderwidth + 'px');
34306
34307 Drawing.setTranslate(rect,
34308 constants.railInset,
34309 (dims.inputAreaWidth - constants.railWidth) * 0.5 + dims.currentValueTotalHeight
34310 );
34311}
34312
34313},{"../../constants/alignment":154,"../../lib":178,"../../lib/svg_text_utils":199,"../../plot_api/plot_template":212,"../../plots/plots":256,"../color":52,"../drawing":74,"./constants":143,"d3":16}],146:[function(_dereq_,module,exports){
34314/**
34315* Copyright 2012-2020, Plotly, Inc.
34316* All rights reserved.
34317*
34318* This source code is licensed under the MIT license found in the
34319* LICENSE file in the root directory of this source tree.
34320*/
34321
34322'use strict';
34323
34324var constants = _dereq_('./constants');
34325
34326module.exports = {
34327 moduleType: 'component',
34328 name: constants.name,
34329
34330 layoutAttributes: _dereq_('./attributes'),
34331 supplyLayoutDefaults: _dereq_('./defaults'),
34332
34333 draw: _dereq_('./draw')
34334};
34335
34336},{"./attributes":142,"./constants":143,"./defaults":144,"./draw":145}],147:[function(_dereq_,module,exports){
34337/**
34338* Copyright 2012-2020, Plotly, Inc.
34339* All rights reserved.
34340*
34341* This source code is licensed under the MIT license found in the
34342* LICENSE file in the root directory of this source tree.
34343*/
34344
34345
34346'use strict';
34347
34348var d3 = _dereq_('d3');
34349var isNumeric = _dereq_('fast-isnumeric');
34350
34351var Plots = _dereq_('../../plots/plots');
34352var Registry = _dereq_('../../registry');
34353var Lib = _dereq_('../../lib');
34354var Drawing = _dereq_('../drawing');
34355var Color = _dereq_('../color');
34356var svgTextUtils = _dereq_('../../lib/svg_text_utils');
34357var interactConstants = _dereq_('../../constants/interactions');
34358
34359var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE;
34360var numStripRE = / [XY][0-9]* /;
34361
34362/**
34363 * Titles - (re)draw titles on the axes and plot:
34364 * @param {DOM element} gd - the graphDiv
34365 * @param {string} titleClass - the css class of this title
34366 * @param {object} options - how and what to draw
34367 * propContainer - the layout object containing `title` and `titlefont`
34368 * attributes that apply to this title
34369 * propName - the full name of the title property (for Plotly.relayout)
34370 * [traceIndex] - include only if this property applies to one trace
34371 * (such as a colorbar title) - then editing pipes to Plotly.restyle
34372 * instead of Plotly.relayout
34373 * placeholder - placeholder text for an empty editable title
34374 * [avoid] {object} - include if this title should move to avoid other elements
34375 * selection - d3 selection of elements to avoid
34376 * side - which direction to move if there is a conflict
34377 * [offsetLeft] - if these elements are subject to a translation
34378 * wrt the title element
34379 * [offsetTop]
34380 * attributes {object} - position and alignment attributes
34381 * x - pixels
34382 * y - pixels
34383 * text-anchor - start|middle|end
34384 * transform {object} - how to transform the title after positioning
34385 * rotate - degrees
34386 * offset - shift up/down in the rotated frame (unused?)
34387 * containerGroup - if an svg <g> element already exists to hold this
34388 * title, include here. Otherwise it will go in fullLayout._infolayer
34389 * _meta {object (optional} - meta key-value to for title with
34390 * Lib.templateString, default to fullLayout._meta, if not provided
34391 *
34392 * @return {selection} d3 selection of title container group
34393 */
34394function draw(gd, titleClass, options) {
34395 var cont = options.propContainer;
34396 var prop = options.propName;
34397 var placeholder = options.placeholder;
34398 var traceIndex = options.traceIndex;
34399 var avoid = options.avoid || {};
34400 var attributes = options.attributes;
34401 var transform = options.transform;
34402 var group = options.containerGroup;
34403
34404 var fullLayout = gd._fullLayout;
34405
34406 var opacity = 1;
34407 var isplaceholder = false;
34408 var title = cont.title;
34409 var txt = (title && title.text ? title.text : '').trim();
34410
34411 var font = title && title.font ? title.font : {};
34412 var fontFamily = font.family;
34413 var fontSize = font.size;
34414 var fontColor = font.color;
34415
34416 // only make this title editable if we positively identify its property
34417 // as one that has editing enabled.
34418 var editAttr;
34419 if(prop === 'title.text') editAttr = 'titleText';
34420 else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText';
34421 else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText';
34422 var editable = gd._context.edits[editAttr];
34423
34424 if(txt === '') opacity = 0;
34425 // look for placeholder text while stripping out numbers from eg X2, Y3
34426 // this is just for backward compatibility with the old version that had
34427 // "Click to enter X2 title" and may have gotten saved in some old plots,
34428 // we don't want this to show up when these are displayed.
34429 else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) {
34430 opacity = 0.2;
34431 isplaceholder = true;
34432 if(!editable) txt = '';
34433 }
34434
34435 if(options._meta) {
34436 txt = Lib.templateString(txt, options._meta);
34437 } else if(fullLayout._meta) {
34438 txt = Lib.templateString(txt, fullLayout._meta);
34439 }
34440
34441 var elShouldExist = txt || editable;
34442
34443 if(!group) {
34444 group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass);
34445 }
34446
34447 var el = group.selectAll('text')
34448 .data(elShouldExist ? [0] : []);
34449 el.enter().append('text');
34450 el.text(txt)
34451 // this is hacky, but convertToTspans uses the class
34452 // to determine whether to rotate mathJax...
34453 // so we need to clear out any old class and put the
34454 // correct one (only relevant for colorbars, at least
34455 // for now) - ie don't use .classed
34456 .attr('class', titleClass);
34457 el.exit().remove();
34458
34459 if(!elShouldExist) return group;
34460
34461 function titleLayout(titleEl) {
34462 Lib.syncOrAsync([drawTitle, scootTitle], titleEl);
34463 }
34464
34465 function drawTitle(titleEl) {
34466 var transformVal;
34467
34468 if(transform) {
34469 transformVal = '';
34470 if(transform.rotate) {
34471 transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')';
34472 }
34473 if(transform.offset) {
34474 transformVal += 'translate(0, ' + transform.offset + ')';
34475 }
34476 } else {
34477 transformVal = null;
34478 }
34479
34480 titleEl.attr('transform', transformVal);
34481
34482 titleEl.style({
34483 'font-family': fontFamily,
34484 'font-size': d3.round(fontSize, 2) + 'px',
34485 fill: Color.rgb(fontColor),
34486 opacity: opacity * Color.opacity(fontColor),
34487 'font-weight': Plots.fontWeight
34488 })
34489 .attr(attributes)
34490 .call(svgTextUtils.convertToTspans, gd);
34491
34492 return Plots.previousPromises(gd);
34493 }
34494
34495 function scootTitle(titleElIn) {
34496 var titleGroup = d3.select(titleElIn.node().parentNode);
34497
34498 if(avoid && avoid.selection && avoid.side && txt) {
34499 titleGroup.attr('transform', null);
34500
34501 // move toward avoid.side (= left, right, top, bottom) if needed
34502 // can include pad (pixels, default 2)
34503 var backside = OPPOSITE_SIDE[avoid.side];
34504 var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1;
34505 var pad = isNumeric(avoid.pad) ? avoid.pad : 2;
34506
34507 var titlebb = Drawing.bBox(titleGroup.node());
34508 var paperbb = {
34509 left: 0,
34510 top: 0,
34511 right: fullLayout.width,
34512 bottom: fullLayout.height
34513 };
34514
34515 var maxshift = avoid.maxShift ||
34516 shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]);
34517 var shift = 0;
34518
34519 // Prevent the title going off the paper
34520 if(maxshift < 0) {
34521 shift = maxshift;
34522 } else {
34523 // so we don't have to offset each avoided element,
34524 // give the title the opposite offset
34525 var offsetLeft = avoid.offsetLeft || 0;
34526 var offsetTop = avoid.offsetTop || 0;
34527 titlebb.left -= offsetLeft;
34528 titlebb.right -= offsetLeft;
34529 titlebb.top -= offsetTop;
34530 titlebb.bottom -= offsetTop;
34531
34532 // iterate over a set of elements (avoid.selection)
34533 // to avoid collisions with
34534 avoid.selection.each(function() {
34535 var avoidbb = Drawing.bBox(this);
34536
34537 if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) {
34538 shift = Math.max(shift, shiftSign * (
34539 avoidbb[avoid.side] - titlebb[backside]) + pad);
34540 }
34541 });
34542 shift = Math.min(maxshift, shift);
34543 }
34544
34545 if(shift > 0 || maxshift < 0) {
34546 var shiftTemplate = {
34547 left: [-shift, 0],
34548 right: [shift, 0],
34549 top: [0, -shift],
34550 bottom: [0, shift]
34551 }[avoid.side];
34552 titleGroup.attr('transform', 'translate(' + shiftTemplate + ')');
34553 }
34554 }
34555 }
34556
34557 el.call(titleLayout);
34558
34559 function setPlaceholder() {
34560 opacity = 0;
34561 isplaceholder = true;
34562 el.text(placeholder)
34563 .on('mouseover.opacity', function() {
34564 d3.select(this).transition()
34565 .duration(interactConstants.SHOW_PLACEHOLDER).style('opacity', 1);
34566 })
34567 .on('mouseout.opacity', function() {
34568 d3.select(this).transition()
34569 .duration(interactConstants.HIDE_PLACEHOLDER).style('opacity', 0);
34570 });
34571 }
34572
34573 if(editable) {
34574 if(!txt) setPlaceholder();
34575 else el.on('.opacity', null);
34576
34577 el.call(svgTextUtils.makeEditable, {gd: gd})
34578 .on('edit', function(text) {
34579 if(traceIndex !== undefined) {
34580 Registry.call('_guiRestyle', gd, prop, text, traceIndex);
34581 } else {
34582 Registry.call('_guiRelayout', gd, prop, text);
34583 }
34584 })
34585 .on('cancel', function() {
34586 this.text(this.attr('data-unformatted'))
34587 .call(titleLayout);
34588 })
34589 .on('input', function(d) {
34590 this.text(d || ' ')
34591 .call(svgTextUtils.positionText, attributes.x, attributes.y);
34592 });
34593 }
34594 el.classed('js-placeholder', isplaceholder);
34595
34596 return group;
34597}
34598
34599module.exports = {
34600 draw: draw
34601};
34602
34603},{"../../constants/alignment":154,"../../constants/interactions":157,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/plots":256,"../../registry":269,"../color":52,"../drawing":74,"d3":16,"fast-isnumeric":18}],148:[function(_dereq_,module,exports){
34604/**
34605* Copyright 2012-2020, Plotly, Inc.
34606* All rights reserved.
34607*
34608* This source code is licensed under the MIT license found in the
34609* LICENSE file in the root directory of this source tree.
34610*/
34611
34612'use strict';
34613
34614var fontAttrs = _dereq_('../../plots/font_attributes');
34615var colorAttrs = _dereq_('../color/attributes');
34616var extendFlat = _dereq_('../../lib/extend').extendFlat;
34617var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
34618var padAttrs = _dereq_('../../plots/pad_attributes');
34619var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
34620
34621var buttonsAttrs = templatedArray('button', {
34622 visible: {
34623 valType: 'boolean',
34624
34625
34626 },
34627 method: {
34628 valType: 'enumerated',
34629 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
34630 dflt: 'restyle',
34631
34632
34633 },
34634 args: {
34635 valType: 'info_array',
34636
34637 freeLength: true,
34638 items: [
34639 {valType: 'any'},
34640 {valType: 'any'},
34641 {valType: 'any'}
34642 ],
34643
34644 },
34645 args2: {
34646 valType: 'info_array',
34647
34648 freeLength: true,
34649 items: [
34650 {valType: 'any'},
34651 {valType: 'any'},
34652 {valType: 'any'}
34653 ],
34654
34655 },
34656 label: {
34657 valType: 'string',
34658
34659 dflt: '',
34660
34661 },
34662 execute: {
34663 valType: 'boolean',
34664
34665 dflt: true,
34666
34667 }
34668});
34669
34670module.exports = overrideAll(templatedArray('updatemenu', {
34671 _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],
34672
34673 visible: {
34674 valType: 'boolean',
34675
34676
34677 },
34678
34679 type: {
34680 valType: 'enumerated',
34681 values: ['dropdown', 'buttons'],
34682 dflt: 'dropdown',
34683
34684
34685 },
34686
34687 direction: {
34688 valType: 'enumerated',
34689 values: ['left', 'right', 'up', 'down'],
34690 dflt: 'down',
34691
34692
34693 },
34694
34695 active: {
34696 valType: 'integer',
34697
34698 min: -1,
34699 dflt: 0,
34700
34701 },
34702
34703 showactive: {
34704 valType: 'boolean',
34705
34706 dflt: true,
34707
34708 },
34709
34710 buttons: buttonsAttrs,
34711
34712 x: {
34713 valType: 'number',
34714 min: -2,
34715 max: 3,
34716 dflt: -0.05,
34717
34718
34719 },
34720 xanchor: {
34721 valType: 'enumerated',
34722 values: ['auto', 'left', 'center', 'right'],
34723 dflt: 'right',
34724
34725
34726 },
34727 y: {
34728 valType: 'number',
34729 min: -2,
34730 max: 3,
34731 dflt: 1,
34732
34733
34734 },
34735 yanchor: {
34736 valType: 'enumerated',
34737 values: ['auto', 'top', 'middle', 'bottom'],
34738 dflt: 'top',
34739
34740
34741 },
34742
34743 pad: extendFlat(padAttrs({editType: 'arraydraw'}), {
34744
34745 }),
34746
34747 font: fontAttrs({
34748
34749 }),
34750
34751 bgcolor: {
34752 valType: 'color',
34753
34754
34755 },
34756 bordercolor: {
34757 valType: 'color',
34758 dflt: colorAttrs.borderLine,
34759
34760
34761 },
34762 borderwidth: {
34763 valType: 'number',
34764 min: 0,
34765 dflt: 1,
34766
34767 editType: 'arraydraw',
34768
34769 }
34770}), 'arraydraw', 'from-root');
34771
34772},{"../../lib/extend":173,"../../plot_api/edit_types":205,"../../plot_api/plot_template":212,"../../plots/font_attributes":250,"../../plots/pad_attributes":255,"../color/attributes":51}],149:[function(_dereq_,module,exports){
34773/**
34774* Copyright 2012-2020, Plotly, Inc.
34775* All rights reserved.
34776*
34777* This source code is licensed under the MIT license found in the
34778* LICENSE file in the root directory of this source tree.
34779*/
34780
34781
34782'use strict';
34783
34784
34785module.exports = {
34786
34787 // layout attribute name
34788 name: 'updatemenus',
34789
34790 // class names
34791 containerClassName: 'updatemenu-container',
34792 headerGroupClassName: 'updatemenu-header-group',
34793 headerClassName: 'updatemenu-header',
34794 headerArrowClassName: 'updatemenu-header-arrow',
34795 dropdownButtonGroupClassName: 'updatemenu-dropdown-button-group',
34796 dropdownButtonClassName: 'updatemenu-dropdown-button',
34797 buttonClassName: 'updatemenu-button',
34798 itemRectClassName: 'updatemenu-item-rect',
34799 itemTextClassName: 'updatemenu-item-text',
34800
34801 // DOM attribute name in button group keeping track
34802 // of active update menu
34803 menuIndexAttrName: 'updatemenu-active-index',
34804
34805 // id root pass to Plots.autoMargin
34806 autoMarginIdRoot: 'updatemenu-',
34807
34808 // options when 'active: -1'
34809 blankHeaderOpts: { label: ' ' },
34810
34811 // min item width / height
34812 minWidth: 30,
34813 minHeight: 30,
34814
34815 // padding around item text
34816 textPadX: 24,
34817 arrowPadX: 16,
34818
34819 // item rect radii
34820 rx: 2,
34821 ry: 2,
34822
34823 // item text x offset off left edge
34824 textOffsetX: 12,
34825
34826 // item text y offset (w.r.t. middle)
34827 textOffsetY: 3,
34828
34829 // arrow offset off right edge
34830 arrowOffsetX: 4,
34831
34832 // gap between header and buttons
34833 gapButtonHeader: 5,
34834
34835 // gap between between buttons
34836 gapButton: 2,
34837
34838 // color given to active buttons
34839 activeColor: '#F4FAFF',
34840
34841 // color given to hovered buttons
34842 hoverColor: '#F4FAFF',
34843
34844 // symbol for menu open arrow
34845 arrowSymbol: {
34846 left: '◄',
34847 right: '►',
34848 up: '▲',
34849 down: '▼'
34850 }
34851};
34852
34853},{}],150:[function(_dereq_,module,exports){
34854/**
34855* Copyright 2012-2020, Plotly, Inc.
34856* All rights reserved.
34857*
34858* This source code is licensed under the MIT license found in the
34859* LICENSE file in the root directory of this source tree.
34860*/
34861
34862'use strict';
34863
34864var Lib = _dereq_('../../lib');
34865var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
34866
34867var attributes = _dereq_('./attributes');
34868var constants = _dereq_('./constants');
34869
34870var name = constants.name;
34871var buttonAttrs = attributes.buttons;
34872
34873
34874module.exports = function updateMenusDefaults(layoutIn, layoutOut) {
34875 var opts = {
34876 name: name,
34877 handleItemDefaults: menuDefaults
34878 };
34879
34880 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
34881};
34882
34883function menuDefaults(menuIn, menuOut, layoutOut) {
34884 function coerce(attr, dflt) {
34885 return Lib.coerce(menuIn, menuOut, attributes, attr, dflt);
34886 }
34887
34888 var buttons = handleArrayContainerDefaults(menuIn, menuOut, {
34889 name: 'buttons',
34890 handleItemDefaults: buttonDefaults
34891 });
34892
34893 var visible = coerce('visible', buttons.length > 0);
34894 if(!visible) return;
34895
34896 coerce('active');
34897 coerce('direction');
34898 coerce('type');
34899 coerce('showactive');
34900
34901 coerce('x');
34902 coerce('y');
34903 Lib.noneOrAll(menuIn, menuOut, ['x', 'y']);
34904
34905 coerce('xanchor');
34906 coerce('yanchor');
34907
34908 coerce('pad.t');
34909 coerce('pad.r');
34910 coerce('pad.b');
34911 coerce('pad.l');
34912
34913 Lib.coerceFont(coerce, 'font', layoutOut.font);
34914
34915 coerce('bgcolor', layoutOut.paper_bgcolor);
34916 coerce('bordercolor');
34917 coerce('borderwidth');
34918}
34919
34920function buttonDefaults(buttonIn, buttonOut) {
34921 function coerce(attr, dflt) {
34922 return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt);
34923 }
34924
34925 var visible = coerce('visible',
34926 (buttonIn.method === 'skip' || Array.isArray(buttonIn.args)));
34927 if(visible) {
34928 coerce('method');
34929 coerce('args');
34930 coerce('args2');
34931 coerce('label');
34932 coerce('execute');
34933 }
34934}
34935
34936},{"../../lib":178,"../../plots/array_container_defaults":218,"./attributes":148,"./constants":149}],151:[function(_dereq_,module,exports){
34937/**
34938* Copyright 2012-2020, Plotly, Inc.
34939* All rights reserved.
34940*
34941* This source code is licensed under the MIT license found in the
34942* LICENSE file in the root directory of this source tree.
34943*/
34944
34945
34946'use strict';
34947
34948var d3 = _dereq_('d3');
34949
34950var Plots = _dereq_('../../plots/plots');
34951var Color = _dereq_('../color');
34952var Drawing = _dereq_('../drawing');
34953var Lib = _dereq_('../../lib');
34954var svgTextUtils = _dereq_('../../lib/svg_text_utils');
34955var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
34956
34957var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
34958
34959var constants = _dereq_('./constants');
34960var ScrollBox = _dereq_('./scrollbox');
34961
34962module.exports = function draw(gd) {
34963 var fullLayout = gd._fullLayout;
34964 var menuData = Lib.filterVisible(fullLayout[constants.name]);
34965
34966 /* Update menu data is bound to the header-group.
34967 * The items in the header group are always present.
34968 *
34969 * Upon clicking on a header its corresponding button
34970 * data is bound to the button-group.
34971 *
34972 * We draw all headers in one group before all buttons
34973 * so that the buttons *always* appear above the headers.
34974 *
34975 * Note that only one set of buttons are visible at once.
34976 *
34977 * <g container />
34978 *
34979 * <g header-group />
34980 * <g item header />
34981 * <text item header-arrow />
34982 * <g header-group />
34983 * <g item header />
34984 * <text item header-arrow />
34985 * ...
34986 *
34987 * <g button-group />
34988 * <g item button />
34989 * <g item button />
34990 * ...
34991 */
34992
34993 function clearAutoMargin(menuOpts) {
34994 Plots.autoMargin(gd, autoMarginId(menuOpts));
34995 }
34996
34997 // draw update menu container
34998 var menus = fullLayout._menulayer
34999 .selectAll('g.' + constants.containerClassName)
35000 .data(menuData.length > 0 ? [0] : []);
35001
35002 menus.enter().append('g')
35003 .classed(constants.containerClassName, true)
35004 .style('cursor', 'pointer');
35005
35006 menus.exit().each(function() {
35007 // Most components don't need to explicitly remove autoMargin, because
35008 // marginPushers does this - but updatemenu updates don't go through
35009 // a full replot so we need to explicitly remove it.
35010 // This is for removing *all* updatemenus, removing individuals is
35011 // handled below, in headerGroups.exit
35012 d3.select(this).selectAll('g.' + constants.headerGroupClassName)
35013 .each(clearAutoMargin);
35014 }).remove();
35015
35016 // return early if no update menus are visible
35017 if(menuData.length === 0) return;
35018
35019 // join header group
35020 var headerGroups = menus.selectAll('g.' + constants.headerGroupClassName)
35021 .data(menuData, keyFunction);
35022
35023 headerGroups.enter().append('g')
35024 .classed(constants.headerGroupClassName, true);
35025
35026 // draw dropdown button container
35027 var gButton = Lib.ensureSingle(menus, 'g', constants.dropdownButtonGroupClassName, function(s) {
35028 s.style('pointer-events', 'all');
35029 });
35030
35031 // find dimensions before plotting anything (this mutates menuOpts)
35032 for(var i = 0; i < menuData.length; i++) {
35033 var menuOpts = menuData[i];
35034 findDimensions(gd, menuOpts);
35035 }
35036
35037 // setup scrollbox
35038 var scrollBoxId = 'updatemenus' + fullLayout._uid;
35039 var scrollBox = new ScrollBox(gd, gButton, scrollBoxId);
35040
35041 // remove exiting header, remove dropped buttons and reset margins
35042 if(headerGroups.enter().size()) {
35043 // make sure gButton is on top of all headers
35044 gButton.node().parentNode.appendChild(gButton.node());
35045 gButton.call(removeAllButtons);
35046 }
35047
35048 headerGroups.exit().each(function(menuOpts) {
35049 gButton.call(removeAllButtons);
35050 clearAutoMargin(menuOpts);
35051 }).remove();
35052
35053 // draw headers!
35054 headerGroups.each(function(menuOpts) {
35055 var gHeader = d3.select(this);
35056
35057 var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
35058
35059 Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
35060 setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true);
35061 });
35062
35063 if(menuOpts.type === 'dropdown') {
35064 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
35065
35066 // if this menu is active, update the dropdown container
35067 if(isActive(gButton, menuOpts)) {
35068 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
35069 }
35070 } else {
35071 drawButtons(gd, gHeader, null, null, menuOpts);
35072 }
35073 });
35074};
35075
35076// Note that '_index' is set at the default step,
35077// it corresponds to the menu index in the user layout update menu container.
35078// Because a menu can be set invisible,
35079// this is a more 'consistent' field than the index in the menuData.
35080function keyFunction(menuOpts) {
35081 return menuOpts._index;
35082}
35083
35084function isFolded(gButton) {
35085 return +gButton.attr(constants.menuIndexAttrName) === -1;
35086}
35087
35088function isActive(gButton, menuOpts) {
35089 return +gButton.attr(constants.menuIndexAttrName) === menuOpts._index;
35090}
35091
35092function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex, isSilentUpdate) {
35093 // update 'active' attribute in menuOpts
35094 menuOpts.active = buttonIndex;
35095
35096 // due to templating, it's possible this slider doesn't even exist yet
35097 arrayEditor(gd.layout, constants.name, menuOpts)
35098 .applyUpdate('active', buttonIndex);
35099
35100 if(menuOpts.type === 'buttons') {
35101 drawButtons(gd, gHeader, null, null, menuOpts);
35102 } else if(menuOpts.type === 'dropdown') {
35103 // fold up buttons and redraw header
35104 gButton.attr(constants.menuIndexAttrName, '-1');
35105
35106 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
35107
35108 if(!isSilentUpdate) {
35109 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
35110 }
35111 }
35112}
35113
35114function drawHeader(gd, gHeader, gButton, scrollBox, menuOpts) {
35115 var header = Lib.ensureSingle(gHeader, 'g', constants.headerClassName, function(s) {
35116 s.style('pointer-events', 'all');
35117 });
35118
35119 var dims = menuOpts._dims;
35120 var active = menuOpts.active;
35121 var headerOpts = menuOpts.buttons[active] || constants.blankHeaderOpts;
35122 var posOpts = { y: menuOpts.pad.t, yPad: 0, x: menuOpts.pad.l, xPad: 0, index: 0 };
35123 var positionOverrides = {
35124 width: dims.headerWidth,
35125 height: dims.headerHeight
35126 };
35127
35128 header
35129 .call(drawItem, menuOpts, headerOpts, gd)
35130 .call(setItemPosition, menuOpts, posOpts, positionOverrides);
35131
35132 // draw drop arrow at the right edge
35133 var arrow = Lib.ensureSingle(gHeader, 'text', constants.headerArrowClassName, function(s) {
35134 s.classed('user-select-none', true)
35135 .attr('text-anchor', 'end')
35136 .call(Drawing.font, menuOpts.font)
35137 .text(constants.arrowSymbol[menuOpts.direction]);
35138 });
35139
35140 arrow.attr({
35141 x: dims.headerWidth - constants.arrowOffsetX + menuOpts.pad.l,
35142 y: dims.headerHeight / 2 + constants.textOffsetY + menuOpts.pad.t
35143 });
35144
35145 header.on('click', function() {
35146 gButton.call(removeAllButtons,
35147 String(isActive(gButton, menuOpts) ? -1 : menuOpts._index)
35148 );
35149
35150 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
35151 });
35152
35153 header.on('mouseover', function() {
35154 header.call(styleOnMouseOver);
35155 });
35156
35157 header.on('mouseout', function() {
35158 header.call(styleOnMouseOut, menuOpts);
35159 });
35160
35161 // translate header group
35162 Drawing.setTranslate(gHeader, dims.lx, dims.ly);
35163}
35164
35165function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) {
35166 // If this is a set of buttons, set pointer events = all since we play
35167 // some minor games with which container is which in order to simplify
35168 // the drawing of *either* buttons or menus
35169 if(!gButton) {
35170 gButton = gHeader;
35171 gButton.attr('pointer-events', 'all');
35172 }
35173
35174 var buttonData = (!isFolded(gButton) || menuOpts.type === 'buttons') ?
35175 menuOpts.buttons :
35176 [];
35177
35178 var klass = menuOpts.type === 'dropdown' ? constants.dropdownButtonClassName : constants.buttonClassName;
35179
35180 var buttons = gButton.selectAll('g.' + klass)
35181 .data(Lib.filterVisible(buttonData));
35182
35183 var enter = buttons.enter().append('g')
35184 .classed(klass, true);
35185
35186 var exit = buttons.exit();
35187
35188 if(menuOpts.type === 'dropdown') {
35189 enter.attr('opacity', '0')
35190 .transition()
35191 .attr('opacity', '1');
35192
35193 exit.transition()
35194 .attr('opacity', '0')
35195 .remove();
35196 } else {
35197 exit.remove();
35198 }
35199
35200 var x0 = 0;
35201 var y0 = 0;
35202 var dims = menuOpts._dims;
35203
35204 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
35205
35206 if(menuOpts.type === 'dropdown') {
35207 if(isVertical) {
35208 y0 = dims.headerHeight + constants.gapButtonHeader;
35209 } else {
35210 x0 = dims.headerWidth + constants.gapButtonHeader;
35211 }
35212 }
35213
35214 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'up') {
35215 y0 = -constants.gapButtonHeader + constants.gapButton - dims.openHeight;
35216 }
35217
35218 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'left') {
35219 x0 = -constants.gapButtonHeader + constants.gapButton - dims.openWidth;
35220 }
35221
35222 var posOpts = {
35223 x: dims.lx + x0 + menuOpts.pad.l,
35224 y: dims.ly + y0 + menuOpts.pad.t,
35225 yPad: constants.gapButton,
35226 xPad: constants.gapButton,
35227 index: 0,
35228 };
35229
35230 var scrollBoxPosition = {
35231 l: posOpts.x + menuOpts.borderwidth,
35232 t: posOpts.y + menuOpts.borderwidth
35233 };
35234
35235 buttons.each(function(buttonOpts, buttonIndex) {
35236 var button = d3.select(this);
35237
35238 button
35239 .call(drawItem, menuOpts, buttonOpts, gd)
35240 .call(setItemPosition, menuOpts, posOpts);
35241
35242 button.on('click', function() {
35243 // skip `dragend` events
35244 if(d3.event.defaultPrevented) return;
35245
35246 if(buttonOpts.execute) {
35247 if(buttonOpts.args2 && menuOpts.active === buttonIndex) {
35248 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1);
35249 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2);
35250 } else {
35251 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex);
35252 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);
35253 }
35254 }
35255
35256 gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active});
35257 });
35258
35259 button.on('mouseover', function() {
35260 button.call(styleOnMouseOver);
35261 });
35262
35263 button.on('mouseout', function() {
35264 button.call(styleOnMouseOut, menuOpts);
35265 buttons.call(styleButtons, menuOpts);
35266 });
35267 });
35268
35269 buttons.call(styleButtons, menuOpts);
35270
35271 if(isVertical) {
35272 scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth);
35273 scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t;
35274 } else {
35275 scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l;
35276 scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight);
35277 }
35278
35279 scrollBoxPosition.direction = menuOpts.direction;
35280
35281 if(scrollBox) {
35282 if(buttons.size()) {
35283 drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition);
35284 } else {
35285 hideScrollBox(scrollBox);
35286 }
35287 }
35288}
35289
35290function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) {
35291 // enable the scrollbox
35292 var direction = menuOpts.direction;
35293 var isVertical = (direction === 'up' || direction === 'down');
35294 var dims = menuOpts._dims;
35295
35296 var active = menuOpts.active;
35297 var translateX, translateY;
35298 var i;
35299 if(isVertical) {
35300 translateY = 0;
35301 for(i = 0; i < active; i++) {
35302 translateY += dims.heights[i] + constants.gapButton;
35303 }
35304 } else {
35305 translateX = 0;
35306 for(i = 0; i < active; i++) {
35307 translateX += dims.widths[i] + constants.gapButton;
35308 }
35309 }
35310
35311 scrollBox.enable(position, translateX, translateY);
35312
35313 if(scrollBox.hbar) {
35314 scrollBox.hbar
35315 .attr('opacity', '0')
35316 .transition()
35317 .attr('opacity', '1');
35318 }
35319
35320 if(scrollBox.vbar) {
35321 scrollBox.vbar
35322 .attr('opacity', '0')
35323 .transition()
35324 .attr('opacity', '1');
35325 }
35326}
35327
35328function hideScrollBox(scrollBox) {
35329 var hasHBar = !!scrollBox.hbar;
35330 var hasVBar = !!scrollBox.vbar;
35331
35332 if(hasHBar) {
35333 scrollBox.hbar
35334 .transition()
35335 .attr('opacity', '0')
35336 .each('end', function() {
35337 hasHBar = false;
35338 if(!hasVBar) scrollBox.disable();
35339 });
35340 }
35341
35342 if(hasVBar) {
35343 scrollBox.vbar
35344 .transition()
35345 .attr('opacity', '0')
35346 .each('end', function() {
35347 hasVBar = false;
35348 if(!hasHBar) scrollBox.disable();
35349 });
35350 }
35351}
35352
35353function drawItem(item, menuOpts, itemOpts, gd) {
35354 item.call(drawItemRect, menuOpts)
35355 .call(drawItemText, menuOpts, itemOpts, gd);
35356}
35357
35358function drawItemRect(item, menuOpts) {
35359 var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) {
35360 s.attr({
35361 rx: constants.rx,
35362 ry: constants.ry,
35363 'shape-rendering': 'crispEdges'
35364 });
35365 });
35366
35367 rect.call(Color.stroke, menuOpts.bordercolor)
35368 .call(Color.fill, menuOpts.bgcolor)
35369 .style('stroke-width', menuOpts.borderwidth + 'px');
35370}
35371
35372function drawItemText(item, menuOpts, itemOpts, gd) {
35373 var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) {
35374 s.classed('user-select-none', true)
35375 .attr({
35376 'text-anchor': 'start',
35377 'data-notex': 1
35378 });
35379 });
35380
35381 var tx = itemOpts.label;
35382 var _meta = gd._fullLayout._meta;
35383 if(_meta) tx = Lib.templateString(tx, _meta);
35384
35385 text.call(Drawing.font, menuOpts.font)
35386 .text(tx)
35387 .call(svgTextUtils.convertToTspans, gd);
35388}
35389
35390function styleButtons(buttons, menuOpts) {
35391 var active = menuOpts.active;
35392
35393 buttons.each(function(buttonOpts, i) {
35394 var button = d3.select(this);
35395
35396 if(i === active && menuOpts.showactive) {
35397 button.select('rect.' + constants.itemRectClassName)
35398 .call(Color.fill, constants.activeColor);
35399 }
35400 });
35401}
35402
35403function styleOnMouseOver(item) {
35404 item.select('rect.' + constants.itemRectClassName)
35405 .call(Color.fill, constants.hoverColor);
35406}
35407
35408function styleOnMouseOut(item, menuOpts) {
35409 item.select('rect.' + constants.itemRectClassName)
35410 .call(Color.fill, menuOpts.bgcolor);
35411}
35412
35413// find item dimensions (this mutates menuOpts)
35414function findDimensions(gd, menuOpts) {
35415 var dims = menuOpts._dims = {
35416 width1: 0,
35417 height1: 0,
35418 heights: [],
35419 widths: [],
35420 totalWidth: 0,
35421 totalHeight: 0,
35422 openWidth: 0,
35423 openHeight: 0,
35424 lx: 0,
35425 ly: 0
35426 };
35427
35428 var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName)
35429 .data(Lib.filterVisible(menuOpts.buttons));
35430
35431 fakeButtons.enter().append('g')
35432 .classed(constants.dropdownButtonClassName, true);
35433
35434 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
35435
35436 // loop over fake buttons to find width / height
35437 fakeButtons.each(function(buttonOpts, i) {
35438 var button = d3.select(this);
35439
35440 button.call(drawItem, menuOpts, buttonOpts, gd);
35441
35442 var text = button.select('.' + constants.itemTextClassName);
35443
35444 // width is given by max width of all buttons
35445 var tWidth = text.node() && Drawing.bBox(text.node()).width;
35446 var wEff = Math.max(tWidth + constants.textPadX, constants.minWidth);
35447
35448 // height is determined by item text
35449 var tHeight = menuOpts.font.size * LINE_SPACING;
35450 var tLines = svgTextUtils.lineCount(text);
35451 var hEff = Math.max(tHeight * tLines, constants.minHeight) + constants.textOffsetY;
35452
35453 hEff = Math.ceil(hEff);
35454 wEff = Math.ceil(wEff);
35455
35456 // Store per-item sizes since a row of horizontal buttons, for example,
35457 // don't all need to be the same width:
35458 dims.widths[i] = wEff;
35459 dims.heights[i] = hEff;
35460
35461 // Height and width of individual element:
35462 dims.height1 = Math.max(dims.height1, hEff);
35463 dims.width1 = Math.max(dims.width1, wEff);
35464
35465 if(isVertical) {
35466 dims.totalWidth = Math.max(dims.totalWidth, wEff);
35467 dims.openWidth = dims.totalWidth;
35468 dims.totalHeight += hEff + constants.gapButton;
35469 dims.openHeight += hEff + constants.gapButton;
35470 } else {
35471 dims.totalWidth += wEff + constants.gapButton;
35472 dims.openWidth += wEff + constants.gapButton;
35473 dims.totalHeight = Math.max(dims.totalHeight, hEff);
35474 dims.openHeight = dims.totalHeight;
35475 }
35476 });
35477
35478 if(isVertical) {
35479 dims.totalHeight -= constants.gapButton;
35480 } else {
35481 dims.totalWidth -= constants.gapButton;
35482 }
35483
35484
35485 dims.headerWidth = dims.width1 + constants.arrowPadX;
35486 dims.headerHeight = dims.height1;
35487
35488 if(menuOpts.type === 'dropdown') {
35489 if(isVertical) {
35490 dims.width1 += constants.arrowPadX;
35491 dims.totalHeight = dims.height1;
35492 } else {
35493 dims.totalWidth = dims.width1;
35494 }
35495 dims.totalWidth += constants.arrowPadX;
35496 }
35497
35498 fakeButtons.remove();
35499
35500 var paddedWidth = dims.totalWidth + menuOpts.pad.l + menuOpts.pad.r;
35501 var paddedHeight = dims.totalHeight + menuOpts.pad.t + menuOpts.pad.b;
35502
35503 var graphSize = gd._fullLayout._size;
35504 dims.lx = graphSize.l + graphSize.w * menuOpts.x;
35505 dims.ly = graphSize.t + graphSize.h * (1 - menuOpts.y);
35506
35507 var xanchor = 'left';
35508 if(Lib.isRightAnchor(menuOpts)) {
35509 dims.lx -= paddedWidth;
35510 xanchor = 'right';
35511 }
35512 if(Lib.isCenterAnchor(menuOpts)) {
35513 dims.lx -= paddedWidth / 2;
35514 xanchor = 'center';
35515 }
35516
35517 var yanchor = 'top';
35518 if(Lib.isBottomAnchor(menuOpts)) {
35519 dims.ly -= paddedHeight;
35520 yanchor = 'bottom';
35521 }
35522 if(Lib.isMiddleAnchor(menuOpts)) {
35523 dims.ly -= paddedHeight / 2;
35524 yanchor = 'middle';
35525 }
35526
35527 dims.totalWidth = Math.ceil(dims.totalWidth);
35528 dims.totalHeight = Math.ceil(dims.totalHeight);
35529 dims.lx = Math.round(dims.lx);
35530 dims.ly = Math.round(dims.ly);
35531
35532 Plots.autoMargin(gd, autoMarginId(menuOpts), {
35533 x: menuOpts.x,
35534 y: menuOpts.y,
35535 l: paddedWidth * ({right: 1, center: 0.5}[xanchor] || 0),
35536 r: paddedWidth * ({left: 1, center: 0.5}[xanchor] || 0),
35537 b: paddedHeight * ({top: 1, middle: 0.5}[yanchor] || 0),
35538 t: paddedHeight * ({bottom: 1, middle: 0.5}[yanchor] || 0)
35539 });
35540}
35541
35542function autoMarginId(menuOpts) {
35543 return constants.autoMarginIdRoot + menuOpts._index;
35544}
35545
35546// set item positions (mutates posOpts)
35547function setItemPosition(item, menuOpts, posOpts, overrideOpts) {
35548 overrideOpts = overrideOpts || {};
35549 var rect = item.select('.' + constants.itemRectClassName);
35550 var text = item.select('.' + constants.itemTextClassName);
35551 var borderWidth = menuOpts.borderwidth;
35552 var index = posOpts.index;
35553 var dims = menuOpts._dims;
35554
35555 Drawing.setTranslate(item, borderWidth + posOpts.x, borderWidth + posOpts.y);
35556
35557 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
35558 var finalHeight = overrideOpts.height || (isVertical ? dims.heights[index] : dims.height1);
35559
35560 rect.attr({
35561 x: 0,
35562 y: 0,
35563 width: overrideOpts.width || (isVertical ? dims.width1 : dims.widths[index]),
35564 height: finalHeight
35565 });
35566
35567 var tHeight = menuOpts.font.size * LINE_SPACING;
35568 var tLines = svgTextUtils.lineCount(text);
35569 var spanOffset = ((tLines - 1) * tHeight / 2);
35570
35571 svgTextUtils.positionText(text, constants.textOffsetX,
35572 finalHeight / 2 - spanOffset + constants.textOffsetY);
35573
35574 if(isVertical) {
35575 posOpts.y += dims.heights[index] + posOpts.yPad;
35576 } else {
35577 posOpts.x += dims.widths[index] + posOpts.xPad;
35578 }
35579
35580 posOpts.index++;
35581}
35582
35583function removeAllButtons(gButton, newMenuIndexAttr) {
35584 gButton
35585 .attr(constants.menuIndexAttrName, newMenuIndexAttr || '-1')
35586 .selectAll('g.' + constants.dropdownButtonClassName).remove();
35587}
35588
35589},{"../../constants/alignment":154,"../../lib":178,"../../lib/svg_text_utils":199,"../../plot_api/plot_template":212,"../../plots/plots":256,"../color":52,"../drawing":74,"./constants":149,"./scrollbox":153,"d3":16}],152:[function(_dereq_,module,exports){
35590arguments[4][146][0].apply(exports,arguments)
35591},{"./attributes":148,"./constants":149,"./defaults":150,"./draw":151,"dup":146}],153:[function(_dereq_,module,exports){
35592/**
35593* Copyright 2012-2020, Plotly, Inc.
35594* All rights reserved.
35595*
35596* This source code is licensed under the MIT license found in the
35597* LICENSE file in the root directory of this source tree.
35598*/
35599
35600'use strict';
35601
35602module.exports = ScrollBox;
35603
35604var d3 = _dereq_('d3');
35605
35606var Color = _dereq_('../color');
35607var Drawing = _dereq_('../drawing');
35608
35609var Lib = _dereq_('../../lib');
35610
35611/**
35612 * Helper class to setup a scroll box
35613 *
35614 * @class
35615 * @param gd Plotly's graph div
35616 * @param container Container to be scroll-boxed (as a D3 selection)
35617 * @param {string} id Id for the clip path to implement the scroll box
35618 */
35619function ScrollBox(gd, container, id) {
35620 this.gd = gd;
35621 this.container = container;
35622 this.id = id;
35623
35624 // See ScrollBox.prototype.enable for further definition
35625 this.position = null; // scrollbox position
35626 this.translateX = null; // scrollbox horizontal translation
35627 this.translateY = null; // scrollbox vertical translation
35628 this.hbar = null; // horizontal scrollbar D3 selection
35629 this.vbar = null; // vertical scrollbar D3 selection
35630
35631 // <rect> element to capture pointer events
35632 this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
35633
35634 this.bg.exit()
35635 .on('.drag', null)
35636 .on('wheel', null)
35637 .remove();
35638
35639 this.bg.enter().append('rect')
35640 .classed('scrollbox-bg', true)
35641 .style('pointer-events', 'all')
35642 .attr({
35643 opacity: 0,
35644 x: 0,
35645 y: 0,
35646 width: 0,
35647 height: 0
35648 });
35649}
35650
35651// scroll bar dimensions
35652ScrollBox.barWidth = 2;
35653ScrollBox.barLength = 20;
35654ScrollBox.barRadius = 2;
35655ScrollBox.barPad = 1;
35656ScrollBox.barColor = '#808BA4';
35657
35658/**
35659 * If needed, setup a clip path and scrollbars
35660 *
35661 * @method
35662 * @param {Object} position
35663 * @param {number} position.l Left side position (in pixels)
35664 * @param {number} position.t Top side (in pixels)
35665 * @param {number} position.w Width (in pixels)
35666 * @param {number} position.h Height (in pixels)
35667 * @param {string} [position.direction='down']
35668 * Either 'down', 'left', 'right' or 'up'
35669 * @param {number} [translateX=0] Horizontal offset (in pixels)
35670 * @param {number} [translateY=0] Vertical offset (in pixels)
35671 */
35672ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
35673 var fullLayout = this.gd._fullLayout;
35674 var fullWidth = fullLayout.width;
35675 var fullHeight = fullLayout.height;
35676
35677 // compute position of scrollbox
35678 this.position = position;
35679
35680 var l = this.position.l;
35681 var w = this.position.w;
35682 var t = this.position.t;
35683 var h = this.position.h;
35684 var direction = this.position.direction;
35685 var isDown = (direction === 'down');
35686 var isLeft = (direction === 'left');
35687 var isRight = (direction === 'right');
35688 var isUp = (direction === 'up');
35689 var boxW = w;
35690 var boxH = h;
35691 var boxL, boxR;
35692 var boxT, boxB;
35693
35694 if(!isDown && !isLeft && !isRight && !isUp) {
35695 this.position.direction = 'down';
35696 isDown = true;
35697 }
35698
35699 var isVertical = isDown || isUp;
35700 if(isVertical) {
35701 boxL = l;
35702 boxR = boxL + boxW;
35703
35704 if(isDown) {
35705 // anchor to top side
35706 boxT = t;
35707 boxB = Math.min(boxT + boxH, fullHeight);
35708 boxH = boxB - boxT;
35709 } else {
35710 // anchor to bottom side
35711 boxB = t + boxH;
35712 boxT = Math.max(boxB - boxH, 0);
35713 boxH = boxB - boxT;
35714 }
35715 } else {
35716 boxT = t;
35717 boxB = boxT + boxH;
35718
35719 if(isLeft) {
35720 // anchor to right side
35721 boxR = l + boxW;
35722 boxL = Math.max(boxR - boxW, 0);
35723 boxW = boxR - boxL;
35724 } else {
35725 // anchor to left side
35726 boxL = l;
35727 boxR = Math.min(boxL + boxW, fullWidth);
35728 boxW = boxR - boxL;
35729 }
35730 }
35731
35732 this._box = {
35733 l: boxL,
35734 t: boxT,
35735 w: boxW,
35736 h: boxH
35737 };
35738
35739 // compute position of horizontal scroll bar
35740 var needsHorizontalScrollBar = (w > boxW);
35741 var hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad;
35742 var hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad;
35743 // draw horizontal scrollbar on the bottom side
35744 var hbarL = l;
35745 var hbarT = t + h;
35746
35747 if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH;
35748
35749 var hbar = this.container.selectAll('rect.scrollbar-horizontal').data(
35750 (needsHorizontalScrollBar) ? [0] : []);
35751
35752 hbar.exit()
35753 .on('.drag', null)
35754 .remove();
35755
35756 hbar.enter().append('rect')
35757 .classed('scrollbar-horizontal', true)
35758 .call(Color.fill, ScrollBox.barColor);
35759
35760 if(needsHorizontalScrollBar) {
35761 this.hbar = hbar.attr({
35762 'rx': ScrollBox.barRadius,
35763 'ry': ScrollBox.barRadius,
35764 'x': hbarL,
35765 'y': hbarT,
35766 'width': hbarW,
35767 'height': hbarH
35768 });
35769
35770 // hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax
35771 this._hbarXMin = hbarL + hbarW / 2;
35772 this._hbarTranslateMax = boxW - hbarW;
35773 } else {
35774 delete this.hbar;
35775 delete this._hbarXMin;
35776 delete this._hbarTranslateMax;
35777 }
35778
35779 // compute position of vertical scroll bar
35780 var needsVerticalScrollBar = (h > boxH);
35781 var vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad;
35782 var vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad;
35783 // draw vertical scrollbar on the right side
35784 var vbarL = l + w;
35785 var vbarT = t;
35786
35787 if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW;
35788
35789 var vbar = this.container.selectAll('rect.scrollbar-vertical').data(
35790 (needsVerticalScrollBar) ? [0] : []);
35791
35792 vbar.exit()
35793 .on('.drag', null)
35794 .remove();
35795
35796 vbar.enter().append('rect')
35797 .classed('scrollbar-vertical', true)
35798 .call(Color.fill, ScrollBox.barColor);
35799
35800 if(needsVerticalScrollBar) {
35801 this.vbar = vbar.attr({
35802 'rx': ScrollBox.barRadius,
35803 'ry': ScrollBox.barRadius,
35804 'x': vbarL,
35805 'y': vbarT,
35806 'width': vbarW,
35807 'height': vbarH
35808 });
35809
35810 // vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax
35811 this._vbarYMin = vbarT + vbarH / 2;
35812 this._vbarTranslateMax = boxH - vbarH;
35813 } else {
35814 delete this.vbar;
35815 delete this._vbarYMin;
35816 delete this._vbarTranslateMax;
35817 }
35818
35819 // setup a clip path (if scroll bars are needed)
35820 var clipId = this.id;
35821 var clipL = boxL - 0.5;
35822 var clipR = (needsVerticalScrollBar) ? boxR + vbarW + 0.5 : boxR + 0.5;
35823 var clipT = boxT - 0.5;
35824 var clipB = (needsHorizontalScrollBar) ? boxB + hbarH + 0.5 : boxB + 0.5;
35825
35826 var clipPath = fullLayout._topdefs.selectAll('#' + clipId)
35827 .data((needsHorizontalScrollBar || needsVerticalScrollBar) ? [0] : []);
35828
35829 clipPath.exit().remove();
35830
35831 clipPath.enter()
35832 .append('clipPath').attr('id', clipId)
35833 .append('rect');
35834
35835 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
35836 this._clipRect = clipPath.select('rect').attr({
35837 x: Math.floor(clipL),
35838 y: Math.floor(clipT),
35839 width: Math.ceil(clipR) - Math.floor(clipL),
35840 height: Math.ceil(clipB) - Math.floor(clipT)
35841 });
35842
35843 this.container.call(Drawing.setClipUrl, clipId, this.gd);
35844
35845 this.bg.attr({
35846 x: l,
35847 y: t,
35848 width: w,
35849 height: h
35850 });
35851 } else {
35852 this.bg.attr({
35853 width: 0,
35854 height: 0
35855 });
35856 this.container
35857 .on('wheel', null)
35858 .on('.drag', null)
35859 .call(Drawing.setClipUrl, null);
35860 delete this._clipRect;
35861 }
35862
35863 // set up drag listeners (if scroll bars are needed)
35864 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
35865 var onBoxDrag = d3.behavior.drag()
35866 .on('dragstart', function() {
35867 d3.event.sourceEvent.preventDefault();
35868 })
35869 .on('drag', this._onBoxDrag.bind(this));
35870
35871 this.container
35872 .on('wheel', null)
35873 .on('wheel', this._onBoxWheel.bind(this))
35874 .on('.drag', null)
35875 .call(onBoxDrag);
35876
35877 var onBarDrag = d3.behavior.drag()
35878 .on('dragstart', function() {
35879 d3.event.sourceEvent.preventDefault();
35880 d3.event.sourceEvent.stopPropagation();
35881 })
35882 .on('drag', this._onBarDrag.bind(this));
35883
35884 if(needsHorizontalScrollBar) {
35885 this.hbar
35886 .on('.drag', null)
35887 .call(onBarDrag);
35888 }
35889
35890 if(needsVerticalScrollBar) {
35891 this.vbar
35892 .on('.drag', null)
35893 .call(onBarDrag);
35894 }
35895 }
35896
35897 // set scrollbox translation
35898 this.setTranslate(translateX, translateY);
35899};
35900
35901/**
35902 * If present, remove clip-path and scrollbars
35903 *
35904 * @method
35905 */
35906ScrollBox.prototype.disable = function disable() {
35907 if(this.hbar || this.vbar) {
35908 this.bg.attr({
35909 width: 0,
35910 height: 0
35911 });
35912 this.container
35913 .on('wheel', null)
35914 .on('.drag', null)
35915 .call(Drawing.setClipUrl, null);
35916 delete this._clipRect;
35917 }
35918
35919 if(this.hbar) {
35920 this.hbar.on('.drag', null);
35921 this.hbar.remove();
35922 delete this.hbar;
35923 delete this._hbarXMin;
35924 delete this._hbarTranslateMax;
35925 }
35926
35927 if(this.vbar) {
35928 this.vbar.on('.drag', null);
35929 this.vbar.remove();
35930 delete this.vbar;
35931 delete this._vbarYMin;
35932 delete this._vbarTranslateMax;
35933 }
35934};
35935
35936/**
35937 * Handles scroll box drag events
35938 *
35939 * @method
35940 */
35941ScrollBox.prototype._onBoxDrag = function _onBoxDrag() {
35942 var translateX = this.translateX;
35943 var translateY = this.translateY;
35944
35945 if(this.hbar) {
35946 translateX -= d3.event.dx;
35947 }
35948
35949 if(this.vbar) {
35950 translateY -= d3.event.dy;
35951 }
35952
35953 this.setTranslate(translateX, translateY);
35954};
35955
35956/**
35957 * Handles scroll box wheel events
35958 *
35959 * @method
35960 */
35961ScrollBox.prototype._onBoxWheel = function _onBoxWheel() {
35962 var translateX = this.translateX;
35963 var translateY = this.translateY;
35964
35965 if(this.hbar) {
35966 translateX += d3.event.deltaY;
35967 }
35968
35969 if(this.vbar) {
35970 translateY += d3.event.deltaY;
35971 }
35972
35973 this.setTranslate(translateX, translateY);
35974};
35975
35976/**
35977 * Handles scroll bar drag events
35978 *
35979 * @method
35980 */
35981ScrollBox.prototype._onBarDrag = function _onBarDrag() {
35982 var translateX = this.translateX;
35983 var translateY = this.translateY;
35984
35985 if(this.hbar) {
35986 var xMin = translateX + this._hbarXMin;
35987 var xMax = xMin + this._hbarTranslateMax;
35988 var x = Lib.constrain(d3.event.x, xMin, xMax);
35989 var xf = (x - xMin) / (xMax - xMin);
35990
35991 var translateXMax = this.position.w - this._box.w;
35992
35993 translateX = xf * translateXMax;
35994 }
35995
35996 if(this.vbar) {
35997 var yMin = translateY + this._vbarYMin;
35998 var yMax = yMin + this._vbarTranslateMax;
35999 var y = Lib.constrain(d3.event.y, yMin, yMax);
36000 var yf = (y - yMin) / (yMax - yMin);
36001
36002 var translateYMax = this.position.h - this._box.h;
36003
36004 translateY = yf * translateYMax;
36005 }
36006
36007 this.setTranslate(translateX, translateY);
36008};
36009
36010/**
36011 * Set clip path and scroll bar translate transform
36012 *
36013 * @method
36014 * @param {number} [translateX=0] Horizontal offset (in pixels)
36015 * @param {number} [translateY=0] Vertical offset (in pixels)
36016 */
36017ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) {
36018 // store translateX and translateY (needed by mouse event handlers)
36019 var translateXMax = this.position.w - this._box.w;
36020 var translateYMax = this.position.h - this._box.h;
36021
36022 translateX = Lib.constrain(translateX || 0, 0, translateXMax);
36023 translateY = Lib.constrain(translateY || 0, 0, translateYMax);
36024
36025 this.translateX = translateX;
36026 this.translateY = translateY;
36027
36028 this.container.call(Drawing.setTranslate,
36029 this._box.l - this.position.l - translateX,
36030 this._box.t - this.position.t - translateY);
36031
36032 if(this._clipRect) {
36033 this._clipRect.attr({
36034 x: Math.floor(this.position.l + translateX - 0.5),
36035 y: Math.floor(this.position.t + translateY - 0.5)
36036 });
36037 }
36038
36039 if(this.hbar) {
36040 var xf = translateX / translateXMax;
36041
36042 this.hbar.call(Drawing.setTranslate,
36043 translateX + xf * this._hbarTranslateMax,
36044 translateY);
36045 }
36046
36047 if(this.vbar) {
36048 var yf = translateY / translateYMax;
36049
36050 this.vbar.call(Drawing.setTranslate,
36051 translateX,
36052 translateY + yf * this._vbarTranslateMax);
36053 }
36054};
36055
36056},{"../../lib":178,"../color":52,"../drawing":74,"d3":16}],154:[function(_dereq_,module,exports){
36057/**
36058* Copyright 2012-2020, Plotly, Inc.
36059* All rights reserved.
36060*
36061* This source code is licensed under the MIT license found in the
36062* LICENSE file in the root directory of this source tree.
36063*/
36064
36065'use strict';
36066
36067// fraction of some size to get to a named position
36068module.exports = {
36069 // from bottom left: this is the origin of our paper-reference
36070 // positioning system
36071 FROM_BL: {
36072 left: 0,
36073 center: 0.5,
36074 right: 1,
36075 bottom: 0,
36076 middle: 0.5,
36077 top: 1
36078 },
36079 // from top left: this is the screen pixel positioning origin
36080 FROM_TL: {
36081 left: 0,
36082 center: 0.5,
36083 right: 1,
36084 bottom: 1,
36085 middle: 0.5,
36086 top: 0
36087 },
36088 // from bottom right: sometimes you just need the opposite of ^^
36089 FROM_BR: {
36090 left: 1,
36091 center: 0.5,
36092 right: 0,
36093 bottom: 0,
36094 middle: 0.5,
36095 top: 1
36096 },
36097 // multiple of fontSize to get the vertical offset between lines
36098 LINE_SPACING: 1.3,
36099
36100 // multiple of fontSize to shift from the baseline
36101 // to the cap (captical letter) line
36102 // (to use when we don't calculate this shift from Drawing.bBox)
36103 // This is an approximation since in reality cap height can differ
36104 // from font to font. However, according to Wikipedia
36105 // an "average" font might have a cap height of 70% of the em
36106 // https://en.wikipedia.org/wiki/Em_(typography)#History
36107 CAP_SHIFT: 0.70,
36108
36109 // half the cap height (distance between baseline and cap line)
36110 // of an "average" font (for more info see above).
36111 MID_SHIFT: 0.35,
36112
36113 OPPOSITE_SIDE: {
36114 left: 'right',
36115 right: 'left',
36116 top: 'bottom',
36117 bottom: 'top'
36118 }
36119};
36120
36121},{}],155:[function(_dereq_,module,exports){
36122/**
36123* Copyright 2012-2020, Plotly, Inc.
36124* All rights reserved.
36125*
36126* This source code is licensed under the MIT license found in the
36127* LICENSE file in the root directory of this source tree.
36128*/
36129
36130'use strict';
36131
36132module.exports = {
36133 FORMAT_LINK: 'https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format',
36134 DATE_FORMAT_LINK: 'https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format'
36135};
36136
36137},{}],156:[function(_dereq_,module,exports){
36138/**
36139* Copyright 2012-2020, Plotly, Inc.
36140* All rights reserved.
36141*
36142* This source code is licensed under the MIT license found in the
36143* LICENSE file in the root directory of this source tree.
36144*/
36145
36146'use strict';
36147
36148module.exports = {
36149 COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='],
36150 COMPARISON_OPS2: ['=', '<', '>=', '>', '<='],
36151 INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['],
36152 SET_OPS: ['{}', '}{'],
36153 CONSTRAINT_REDUCTION: {
36154 // for contour constraints, open/closed endpoints are equivalent
36155 '=': '=',
36156
36157 '<': '<',
36158 '<=': '<',
36159
36160 '>': '>',
36161 '>=': '>',
36162
36163 '[]': '[]',
36164 '()': '[]',
36165 '[)': '[]',
36166 '(]': '[]',
36167
36168 '][': '][',
36169 ')(': '][',
36170 '](': '][',
36171 ')[': ']['
36172 }
36173};
36174
36175},{}],157:[function(_dereq_,module,exports){
36176/**
36177* Copyright 2012-2020, Plotly, Inc.
36178* All rights reserved.
36179*
36180* This source code is licensed under the MIT license found in the
36181* LICENSE file in the root directory of this source tree.
36182*/
36183
36184'use strict';
36185
36186
36187module.exports = {
36188 /**
36189 * Timing information for interactive elements
36190 */
36191 SHOW_PLACEHOLDER: 100,
36192 HIDE_PLACEHOLDER: 1000,
36193
36194 // opacity dimming fraction for points that are not in selection
36195 DESELECTDIM: 0.2
36196};
36197
36198},{}],158:[function(_dereq_,module,exports){
36199/**
36200* Copyright 2012-2020, Plotly, Inc.
36201* All rights reserved.
36202*
36203* This source code is licensed under the MIT license found in the
36204* LICENSE file in the root directory of this source tree.
36205*/
36206
36207'use strict';
36208
36209
36210module.exports = {
36211 /**
36212 * Standardize all missing data in calcdata to use undefined
36213 * never null or NaN.
36214 * That way we can use !==undefined, or !== BADNUM,
36215 * to test for real data
36216 */
36217 BADNUM: undefined,
36218
36219 /*
36220 * Limit certain operations to well below floating point max value
36221 * to avoid glitches: Make sure that even when you multiply it by the
36222 * number of pixels on a giant screen it still works
36223 */
36224 FP_SAFE: Number.MAX_VALUE / 10000,
36225
36226 /*
36227 * conversion of date units to milliseconds
36228 * year and month constants are marked "AVG"
36229 * to remind us that not all years and months
36230 * have the same length
36231 */
36232 ONEAVGYEAR: 31557600000, // 365.25 days
36233 ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR
36234 ONEDAY: 86400000,
36235 ONEHOUR: 3600000,
36236 ONEMIN: 60000,
36237 ONESEC: 1000,
36238
36239 /*
36240 * For fast conversion btwn world calendars and epoch ms, the Julian Day Number
36241 * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD()
36242 */
36243 EPOCHJD: 2440587.5,
36244
36245 /*
36246 * Are two values nearly equal? Compare to 1PPM
36247 */
36248 ALMOST_EQUAL: 1 - 1e-6,
36249
36250 /*
36251 * If we're asked to clip a non-positive log value, how far off-screen
36252 * do we put it?
36253 */
36254 LOG_CLIP: 10,
36255
36256 /*
36257 * not a number, but for displaying numbers: the "minus sign" symbol is
36258 * wider than the regular ascii dash "-"
36259 */
36260 MINUS_SIGN: '\u2212'
36261};
36262
36263},{}],159:[function(_dereq_,module,exports){
36264/**
36265* Copyright 2012-2020, Plotly, Inc.
36266* All rights reserved.
36267*
36268* This source code is licensed under the MIT license found in the
36269* LICENSE file in the root directory of this source tree.
36270*/
36271
36272
36273'use strict';
36274
36275
36276exports.xmlns = 'http://www.w3.org/2000/xmlns/';
36277exports.svg = 'http://www.w3.org/2000/svg';
36278exports.xlink = 'http://www.w3.org/1999/xlink';
36279
36280// the 'old' d3 quirk got fix in v3.5.7
36281// https://github.com/mbostock/d3/commit/a6f66e9dd37f764403fc7c1f26be09ab4af24fed
36282exports.svgAttrs = {
36283 xmlns: exports.svg,
36284 'xmlns:xlink': exports.xlink
36285};
36286
36287},{}],160:[function(_dereq_,module,exports){
36288/**
36289* Copyright 2012-2020, Plotly, Inc.
36290* All rights reserved.
36291*
36292* This source code is licensed under the MIT license found in the
36293* LICENSE file in the root directory of this source tree.
36294*/
36295
36296'use strict';
36297
36298exports.version = _dereq_('./version').version;
36299
36300// inject promise polyfill
36301_dereq_('es6-promise').polyfill();
36302
36303// inject plot css
36304_dereq_('../build/plotcss');
36305
36306// inject default MathJax config
36307_dereq_('./fonts/mathjax_config')();
36308
36309// include registry module and expose register method
36310var Registry = _dereq_('./registry');
36311var register = exports.register = Registry.register;
36312
36313// expose plot api methods
36314var plotApi = _dereq_('./plot_api');
36315var methodNames = Object.keys(plotApi);
36316for(var i = 0; i < methodNames.length; i++) {
36317 var name = methodNames[i];
36318 // _ -> private API methods, but still registered for internal use
36319 if(name.charAt(0) !== '_') exports[name] = plotApi[name];
36320 register({
36321 moduleType: 'apiMethod',
36322 name: name,
36323 fn: plotApi[name]
36324 });
36325}
36326
36327// scatter is the only trace included by default
36328register(_dereq_('./traces/scatter'));
36329
36330// register all registrable components modules
36331register([
36332 _dereq_('./components/legend'),
36333 _dereq_('./components/fx'), // fx needs to come after legend
36334 _dereq_('./components/annotations'),
36335 _dereq_('./components/annotations3d'),
36336 _dereq_('./components/shapes'),
36337 _dereq_('./components/images'),
36338 _dereq_('./components/updatemenus'),
36339 _dereq_('./components/sliders'),
36340 _dereq_('./components/rangeslider'),
36341 _dereq_('./components/rangeselector'),
36342 _dereq_('./components/grid'),
36343 _dereq_('./components/errorbars'),
36344 _dereq_('./components/colorscale'),
36345 _dereq_('./components/colorbar')
36346]);
36347
36348// locales en and en-US are required for default behavior
36349register([
36350 _dereq_('./locale-en'),
36351 _dereq_('./locale-en-us')
36352]);
36353
36354// locales that are present in the window should be loaded
36355if(window.PlotlyLocales && Array.isArray(window.PlotlyLocales)) {
36356 register(window.PlotlyLocales);
36357 delete window.PlotlyLocales;
36358}
36359
36360// plot icons
36361exports.Icons = _dereq_('./fonts/ploticon');
36362
36363// unofficial 'beta' plot methods, use at your own risk
36364exports.Plots = _dereq_('./plots/plots');
36365exports.Fx = _dereq_('./components/fx');
36366exports.Snapshot = _dereq_('./snapshot');
36367exports.PlotSchema = _dereq_('./plot_api/plot_schema');
36368exports.Queue = _dereq_('./lib/queue');
36369
36370// export d3 used in the bundle
36371exports.d3 = _dereq_('d3');
36372
36373},{"../build/plotcss":1,"./components/annotations":45,"./components/annotations3d":50,"./components/colorbar":58,"./components/colorscale":64,"./components/errorbars":80,"./components/fx":92,"./components/grid":96,"./components/images":101,"./components/legend":109,"./components/rangeselector":120,"./components/rangeslider":127,"./components/shapes":141,"./components/sliders":146,"./components/updatemenus":152,"./fonts/mathjax_config":161,"./fonts/ploticon":162,"./lib/queue":192,"./locale-en":203,"./locale-en-us":202,"./plot_api":207,"./plot_api/plot_schema":211,"./plots/plots":256,"./registry":269,"./snapshot":274,"./traces/scatter":401,"./version":435,"d3":16,"es6-promise":17}],161:[function(_dereq_,module,exports){
36374/**
36375* Copyright 2012-2020, Plotly, Inc.
36376* All rights reserved.
36377*
36378* This source code is licensed under the MIT license found in the
36379* LICENSE file in the root directory of this source tree.
36380*/
36381
36382'use strict';
36383
36384/* global MathJax:false */
36385
36386module.exports = function() {
36387 if(typeof MathJax !== 'undefined') {
36388 var globalConfig = (window.PlotlyConfig || {}).MathJaxConfig !== 'local';
36389
36390 if(globalConfig) {
36391 MathJax.Hub.Config({
36392 messageStyle: 'none',
36393 skipStartupTypeset: true,
36394 displayAlign: 'left',
36395 tex2jax: {
36396 inlineMath: [['$', '$'], ['\\(', '\\)']]
36397 }
36398 });
36399 MathJax.Hub.Configured();
36400 }
36401 }
36402};
36403
36404},{}],162:[function(_dereq_,module,exports){
36405/**
36406* Copyright 2012-2020, Plotly, Inc.
36407* All rights reserved.
36408*
36409* This source code is licensed under the MIT license found in the
36410* LICENSE file in the root directory of this source tree.
36411*/
36412
36413'use strict';
36414
36415module.exports = {
36416 'undo': {
36417 'width': 857.1,
36418 'height': 1000,
36419 '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',
36420 'transform': 'matrix(1 0 0 -1 0 850)'
36421 },
36422 'home': {
36423 'width': 928.6,
36424 'height': 1000,
36425 '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',
36426 'transform': 'matrix(1 0 0 -1 0 850)'
36427 },
36428 'camera-retro': {
36429 'width': 1000,
36430 'height': 1000,
36431 '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',
36432 'transform': 'matrix(1 0 0 -1 0 850)'
36433 },
36434 'zoombox': {
36435 'width': 1000,
36436 'height': 1000,
36437 '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',
36438 'transform': 'matrix(1 0 0 -1 0 850)'
36439 },
36440 'pan': {
36441 'width': 1000,
36442 'height': 1000,
36443 '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',
36444 'transform': 'matrix(1 0 0 -1 0 850)'
36445 },
36446 'zoom_plus': {
36447 'width': 875,
36448 'height': 1000,
36449 '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',
36450 'transform': 'matrix(1 0 0 -1 0 850)'
36451 },
36452 'zoom_minus': {
36453 'width': 875,
36454 'height': 1000,
36455 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
36456 'transform': 'matrix(1 0 0 -1 0 850)'
36457 },
36458 'autoscale': {
36459 'width': 1000,
36460 'height': 1000,
36461 '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',
36462 'transform': 'matrix(1 0 0 -1 0 850)'
36463 },
36464 'tooltip_basic': {
36465 'width': 1500,
36466 'height': 1000,
36467 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z',
36468 'transform': 'matrix(1 0 0 -1 0 850)'
36469 },
36470 'tooltip_compare': {
36471 'width': 1125,
36472 'height': 1000,
36473 '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',
36474 'transform': 'matrix(1 0 0 -1 0 850)'
36475 },
36476 'plotlylogo': {
36477 'width': 1542,
36478 'height': 1000,
36479 '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',
36480 'transform': 'matrix(1 0 0 -1 0 850)'
36481 },
36482 'z-axis': {
36483 'width': 1000,
36484 'height': 1000,
36485 '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',
36486 'transform': 'matrix(1 0 0 -1 0 850)'
36487 },
36488 '3d_rotate': {
36489 'width': 1000,
36490 'height': 1000,
36491 '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',
36492 'transform': 'matrix(1 0 0 -1 0 850)'
36493 },
36494 'camera': {
36495 'width': 1000,
36496 'height': 1000,
36497 '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',
36498 'transform': 'matrix(1 0 0 -1 0 850)'
36499 },
36500 'movie': {
36501 'width': 1000,
36502 'height': 1000,
36503 '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',
36504 'transform': 'matrix(1 0 0 -1 0 850)'
36505 },
36506 'question': {
36507 'width': 857.1,
36508 'height': 1000,
36509 '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',
36510 'transform': 'matrix(1 0 0 -1 0 850)'
36511 },
36512 'disk': {
36513 'width': 857.1,
36514 'height': 1000,
36515 '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',
36516 'transform': 'matrix(1 0 0 -1 0 850)'
36517 },
36518 'drawopenpath': {
36519 'width': 70,
36520 'height': 70,
36521 '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',
36522 'transform': 'matrix(1 0 0 1 -15 -15)'
36523 },
36524 'drawclosedpath': {
36525 'width': 90,
36526 'height': 90,
36527 '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',
36528 'transform': 'matrix(1 0 0 1 -5 -5)'
36529 },
36530 'lasso': {
36531 'width': 1031,
36532 'height': 1000,
36533 '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',
36534 'transform': 'matrix(1 0 0 -1 0 850)'
36535 },
36536 'selectbox': {
36537 'width': 1000,
36538 'height': 1000,
36539 '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',
36540 'transform': 'matrix(1 0 0 -1 0 850)'
36541 },
36542 'drawline': {
36543 'width': 70,
36544 'height': 70,
36545 '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',
36546 'transform': 'matrix(1 0 0 1 -15 -15)'
36547 },
36548 'drawrect': {
36549 'width': 80,
36550 'height': 80,
36551 'path': 'M78,22V79H21V22H78m9-9H12V88H87V13ZM68,46.22H31V54H68ZM53,32H45.22V69H53Z',
36552 'transform': 'matrix(1 0 0 1 -10 -10)'
36553 },
36554 'drawcircle': {
36555 'width': 80,
36556 'height': 80,
36557 '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',
36558 'transform': 'matrix(1 0 0 1 -10 -10)'
36559 },
36560 'eraseshape': {
36561 'width': 80,
36562 'height': 80,
36563 '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',
36564 'transform': 'matrix(1 0 0 1 -10 -10)'
36565 },
36566 'spikeline': {
36567 'width': 1000,
36568 'height': 1000,
36569 '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',
36570 'transform': 'matrix(1.5 0 0 -1.5 0 850)'
36571 },
36572 'pencil': {
36573 'width': 1792,
36574 'height': 1792,
36575 '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',
36576 'transform': 'matrix(1 0 0 1 0 1)'
36577 },
36578 'newplotlylogo': {
36579 'name': 'newplotlylogo',
36580 '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>'
36581 }
36582};
36583
36584},{}],163:[function(_dereq_,module,exports){
36585/**
36586* Copyright 2012-2020, Plotly, Inc.
36587* All rights reserved.
36588*
36589* This source code is licensed under the MIT license found in the
36590* LICENSE file in the root directory of this source tree.
36591*/
36592
36593
36594'use strict';
36595
36596
36597/**
36598 * Determine the position anchor property of x/y xanchor/yanchor components.
36599 *
36600 * - values < 1/3 align the low side at that fraction,
36601 * - values [1/3, 2/3] align the center at that fraction,
36602 * - values > 2/3 align the right at that fraction.
36603 */
36604
36605
36606exports.isLeftAnchor = function isLeftAnchor(opts) {
36607 return (
36608 opts.xanchor === 'left' ||
36609 (opts.xanchor === 'auto' && opts.x <= 1 / 3)
36610 );
36611};
36612
36613exports.isCenterAnchor = function isCenterAnchor(opts) {
36614 return (
36615 opts.xanchor === 'center' ||
36616 (opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3)
36617 );
36618};
36619
36620exports.isRightAnchor = function isRightAnchor(opts) {
36621 return (
36622 opts.xanchor === 'right' ||
36623 (opts.xanchor === 'auto' && opts.x >= 2 / 3)
36624 );
36625};
36626
36627exports.isTopAnchor = function isTopAnchor(opts) {
36628 return (
36629 opts.yanchor === 'top' ||
36630 (opts.yanchor === 'auto' && opts.y >= 2 / 3)
36631 );
36632};
36633
36634exports.isMiddleAnchor = function isMiddleAnchor(opts) {
36635 return (
36636 opts.yanchor === 'middle' ||
36637 (opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3)
36638 );
36639};
36640
36641exports.isBottomAnchor = function isBottomAnchor(opts) {
36642 return (
36643 opts.yanchor === 'bottom' ||
36644 (opts.yanchor === 'auto' && opts.y <= 1 / 3)
36645 );
36646};
36647
36648},{}],164:[function(_dereq_,module,exports){
36649/**
36650* Copyright 2012-2020, Plotly, Inc.
36651* All rights reserved.
36652*
36653* This source code is licensed under the MIT license found in the
36654* LICENSE file in the root directory of this source tree.
36655*/
36656
36657'use strict';
36658
36659var modModule = _dereq_('./mod');
36660var mod = modModule.mod;
36661var modHalf = modModule.modHalf;
36662
36663var PI = Math.PI;
36664var twoPI = 2 * PI;
36665
36666function deg2rad(deg) { return deg / 180 * PI; }
36667
36668function rad2deg(rad) { return rad / PI * 180; }
36669
36670/**
36671 * is sector a full circle?
36672 * ... this comes up a lot in SVG path-drawing routines
36673 *
36674 * N.B. we consider all sectors that span more that 2pi 'full' circles
36675 *
36676 * @param {2-item array} aBnds : angular bounds in *radians*
36677 * @return {boolean}
36678 */
36679function isFullCircle(aBnds) {
36680 return Math.abs(aBnds[1] - aBnds[0]) > twoPI - 1e-14;
36681}
36682
36683/**
36684 * angular delta between angle 'a' and 'b'
36685 * solution taken from: https://stackoverflow.com/a/2007279
36686 *
36687 * @param {number} a : first angle in *radians*
36688 * @param {number} b : second angle in *radians*
36689 * @return {number} angular delta in *radians*
36690 */
36691function angleDelta(a, b) {
36692 return modHalf(b - a, twoPI);
36693}
36694
36695/**
36696 * angular distance between angle 'a' and 'b'
36697 *
36698 * @param {number} a : first angle in *radians*
36699 * @param {number} b : second angle in *radians*
36700 * @return {number} angular distance in *radians*
36701 */
36702function angleDist(a, b) {
36703 return Math.abs(angleDelta(a, b));
36704}
36705
36706/**
36707 * is angle inside sector?
36708 *
36709 * @param {number} a : angle to test in *radians*
36710 * @param {2-item array} aBnds : sector's angular bounds in *radians*
36711 * @param {boolean}
36712 */
36713function isAngleInsideSector(a, aBnds) {
36714 if(isFullCircle(aBnds)) return true;
36715
36716 var s0, s1;
36717
36718 if(aBnds[0] < aBnds[1]) {
36719 s0 = aBnds[0];
36720 s1 = aBnds[1];
36721 } else {
36722 s0 = aBnds[1];
36723 s1 = aBnds[0];
36724 }
36725
36726 s0 = mod(s0, twoPI);
36727 s1 = mod(s1, twoPI);
36728 if(s0 > s1) s1 += twoPI;
36729
36730 var a0 = mod(a, twoPI);
36731 var a1 = a0 + twoPI;
36732
36733 return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1);
36734}
36735
36736/**
36737 * is pt (r,a) inside sector?
36738 *
36739 * @param {number} r : pt's radial coordinate
36740 * @param {number} a : pt's angular coordinate in *radians*
36741 * @param {2-item array} rBnds : sector's radial bounds
36742 * @param {2-item array} aBnds : sector's angular bounds in *radians*
36743 * @return {boolean}
36744 */
36745function isPtInsideSector(r, a, rBnds, aBnds) {
36746 if(!isAngleInsideSector(a, aBnds)) return false;
36747
36748 var r0, r1;
36749
36750 if(rBnds[0] < rBnds[1]) {
36751 r0 = rBnds[0];
36752 r1 = rBnds[1];
36753 } else {
36754 r0 = rBnds[1];
36755 r1 = rBnds[0];
36756 }
36757
36758 return r >= r0 && r <= r1;
36759}
36760
36761// common to pathArc, pathSector and pathAnnulus
36762function _path(r0, r1, a0, a1, cx, cy, isClosed) {
36763 cx = cx || 0;
36764 cy = cy || 0;
36765
36766 var isCircle = isFullCircle([a0, a1]);
36767 var aStart, aMid, aEnd;
36768 var rStart, rEnd;
36769
36770 if(isCircle) {
36771 aStart = 0;
36772 aMid = PI;
36773 aEnd = twoPI;
36774 } else {
36775 if(a0 < a1) {
36776 aStart = a0;
36777 aEnd = a1;
36778 } else {
36779 aStart = a1;
36780 aEnd = a0;
36781 }
36782 }
36783
36784 if(r0 < r1) {
36785 rStart = r0;
36786 rEnd = r1;
36787 } else {
36788 rStart = r1;
36789 rEnd = r0;
36790 }
36791
36792 // N.B. svg coordinates here, where y increases downward
36793 function pt(r, a) {
36794 return [r * Math.cos(a) + cx, cy - r * Math.sin(a)];
36795 }
36796
36797 var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1;
36798 function arc(r, a, cw) {
36799 return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a);
36800 }
36801
36802 var p;
36803
36804 if(isCircle) {
36805 if(rStart === null) {
36806 p = 'M' + pt(rEnd, aStart) +
36807 arc(rEnd, aMid, 0) +
36808 arc(rEnd, aEnd, 0) + 'Z';
36809 } else {
36810 p = 'M' + pt(rStart, aStart) +
36811 arc(rStart, aMid, 0) +
36812 arc(rStart, aEnd, 0) + 'Z' +
36813 'M' + pt(rEnd, aStart) +
36814 arc(rEnd, aMid, 1) +
36815 arc(rEnd, aEnd, 1) + 'Z';
36816 }
36817 } else {
36818 if(rStart === null) {
36819 p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0);
36820 if(isClosed) p += 'L0,0Z';
36821 } else {
36822 p = 'M' + pt(rStart, aStart) +
36823 'L' + pt(rEnd, aStart) +
36824 arc(rEnd, aEnd, 0) +
36825 'L' + pt(rStart, aEnd) +
36826 arc(rStart, aStart, 1) + 'Z';
36827 }
36828 }
36829
36830 return p;
36831}
36832
36833/**
36834 * path an arc
36835 *
36836 * @param {number} r : radius
36837 * @param {number} a0 : first angular coordinate in *radians*
36838 * @param {number} a1 : second angular coordinate in *radians*
36839 * @param {number (optional)} cx : x coordinate of center
36840 * @param {number (optional)} cy : y coordinate of center
36841 * @return {string} svg path
36842 */
36843function pathArc(r, a0, a1, cx, cy) {
36844 return _path(null, r, a0, a1, cx, cy, 0);
36845}
36846
36847/**
36848 * path a sector
36849 *
36850 * @param {number} r : radius
36851 * @param {number} a0 : first angular coordinate in *radians*
36852 * @param {number} a1 : second angular coordinate in *radians*
36853 * @param {number (optional)} cx : x coordinate of center
36854 * @param {number (optional)} cy : y coordinate of center
36855 * @return {string} svg path
36856 */
36857function pathSector(r, a0, a1, cx, cy) {
36858 return _path(null, r, a0, a1, cx, cy, 1);
36859}
36860
36861/**
36862 * path an annulus
36863 *
36864 * @param {number} r0 : first radial coordinate
36865 * @param {number} r1 : second radial coordinate
36866 * @param {number} a0 : first angular coordinate in *radians*
36867 * @param {number} a1 : second angular coordinate in *radians*
36868 * @param {number (optional)} cx : x coordinate of center
36869 * @param {number (optional)} cy : y coordinate of center
36870 * @return {string} svg path
36871 */
36872function pathAnnulus(r0, r1, a0, a1, cx, cy) {
36873 return _path(r0, r1, a0, a1, cx, cy, 1);
36874}
36875
36876module.exports = {
36877 deg2rad: deg2rad,
36878 rad2deg: rad2deg,
36879 angleDelta: angleDelta,
36880 angleDist: angleDist,
36881 isFullCircle: isFullCircle,
36882 isAngleInsideSector: isAngleInsideSector,
36883 isPtInsideSector: isPtInsideSector,
36884 pathArc: pathArc,
36885 pathSector: pathSector,
36886 pathAnnulus: pathAnnulus
36887};
36888
36889},{"./mod":185}],165:[function(_dereq_,module,exports){
36890/**
36891* Copyright 2012-2020, Plotly, Inc.
36892* All rights reserved.
36893*
36894* This source code is licensed under the MIT license found in the
36895* LICENSE file in the root directory of this source tree.
36896*/
36897
36898'use strict';
36899
36900var isArray = Array.isArray;
36901
36902// IE9 fallbacks
36903
36904var ab = (typeof ArrayBuffer === 'undefined' || !ArrayBuffer.isView) ?
36905 {isView: function() { return false; }} :
36906 ArrayBuffer;
36907
36908var dv = (typeof DataView === 'undefined') ?
36909 function() {} :
36910 DataView;
36911
36912function isTypedArray(a) {
36913 return ab.isView(a) && !(a instanceof dv);
36914}
36915exports.isTypedArray = isTypedArray;
36916
36917function isArrayOrTypedArray(a) {
36918 return isArray(a) || isTypedArray(a);
36919}
36920exports.isArrayOrTypedArray = isArrayOrTypedArray;
36921
36922/*
36923 * Test whether an input object is 1D.
36924 *
36925 * Assumes we already know the object is an array.
36926 *
36927 * Looks only at the first element, if the dimensionality is
36928 * not consistent we won't figure that out here.
36929 */
36930function isArray1D(a) {
36931 return !isArrayOrTypedArray(a[0]);
36932}
36933exports.isArray1D = isArray1D;
36934
36935/*
36936 * Ensures an array has the right amount of storage space. If it doesn't
36937 * exist, it creates an array. If it does exist, it returns it if too
36938 * short or truncates it in-place.
36939 *
36940 * The goal is to just reuse memory to avoid a bit of excessive garbage
36941 * collection.
36942 */
36943exports.ensureArray = function(out, n) {
36944 // TODO: typed array support here? This is only used in
36945 // traces/carpet/compute_control_points
36946 if(!isArray(out)) out = [];
36947
36948 // If too long, truncate. (If too short, it will grow
36949 // automatically so we don't care about that case)
36950 out.length = n;
36951
36952 return out;
36953};
36954
36955/*
36956 * TypedArray-compatible concatenation of n arrays
36957 * if all arrays are the same type it will preserve that type,
36958 * otherwise it falls back on Array.
36959 * Also tries to avoid copying, in case one array has zero length
36960 * But never mutates an existing array
36961 */
36962exports.concat = function() {
36963 var args = [];
36964 var allArray = true;
36965 var totalLen = 0;
36966
36967 var _constructor, arg0, i, argi, posi, leni, out, j;
36968
36969 for(i = 0; i < arguments.length; i++) {
36970 argi = arguments[i];
36971 leni = argi.length;
36972 if(leni) {
36973 if(arg0) args.push(argi);
36974 else {
36975 arg0 = argi;
36976 posi = leni;
36977 }
36978
36979 if(isArray(argi)) {
36980 _constructor = false;
36981 } else {
36982 allArray = false;
36983 if(!totalLen) {
36984 _constructor = argi.constructor;
36985 } else if(_constructor !== argi.constructor) {
36986 // TODO: in principle we could upgrade here,
36987 // ie keep typed array but convert all to Float64Array?
36988 _constructor = false;
36989 }
36990 }
36991
36992 totalLen += leni;
36993 }
36994 }
36995
36996 if(!totalLen) return [];
36997 if(!args.length) return arg0;
36998
36999 if(allArray) return arg0.concat.apply(arg0, args);
37000 if(_constructor) {
37001 // matching typed arrays
37002 out = new _constructor(totalLen);
37003 out.set(arg0);
37004 for(i = 0; i < args.length; i++) {
37005 argi = args[i];
37006 out.set(argi, posi);
37007 posi += argi.length;
37008 }
37009 return out;
37010 }
37011
37012 // mismatched types or Array + typed
37013 out = new Array(totalLen);
37014 for(j = 0; j < arg0.length; j++) out[j] = arg0[j];
37015 for(i = 0; i < args.length; i++) {
37016 argi = args[i];
37017 for(j = 0; j < argi.length; j++) out[posi + j] = argi[j];
37018 posi += j;
37019 }
37020 return out;
37021};
37022
37023exports.maxRowLength = function(z) {
37024 return _rowLength(z, Math.max, 0);
37025};
37026
37027exports.minRowLength = function(z) {
37028 return _rowLength(z, Math.min, Infinity);
37029};
37030
37031function _rowLength(z, fn, len0) {
37032 if(isArrayOrTypedArray(z)) {
37033 if(isArrayOrTypedArray(z[0])) {
37034 var len = len0;
37035 for(var i = 0; i < z.length; i++) {
37036 len = fn(len, z[i].length);
37037 }
37038 return len;
37039 } else {
37040 return z.length;
37041 }
37042 }
37043 return 0;
37044}
37045
37046},{}],166:[function(_dereq_,module,exports){
37047/**
37048* Copyright 2012-2020, Plotly, Inc.
37049* All rights reserved.
37050*
37051* This source code is licensed under the MIT license found in the
37052* LICENSE file in the root directory of this source tree.
37053*/
37054
37055
37056'use strict';
37057
37058var isNumeric = _dereq_('fast-isnumeric');
37059
37060var BADNUM = _dereq_('../constants/numerical').BADNUM;
37061
37062// precompile for speed
37063var JUNK = /^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g;
37064
37065/**
37066 * cleanNumber: remove common leading and trailing cruft
37067 * Always returns either a number or BADNUM.
37068 */
37069module.exports = function cleanNumber(v) {
37070 if(typeof v === 'string') {
37071 v = v.replace(JUNK, '');
37072 }
37073
37074 if(isNumeric(v)) return Number(v);
37075
37076 return BADNUM;
37077};
37078
37079},{"../constants/numerical":158,"fast-isnumeric":18}],167:[function(_dereq_,module,exports){
37080/**
37081* Copyright 2012-2020, Plotly, Inc.
37082* All rights reserved.
37083*
37084* This source code is licensed under the MIT license found in the
37085* LICENSE file in the root directory of this source tree.
37086*/
37087
37088'use strict';
37089
37090/**
37091 * Clear gl frame (if any). This is a common pattern as
37092 * we usually set `preserveDrawingBuffer: true` during
37093 * gl context creation (e.g. via `reglUtils.prepare`).
37094 *
37095 * @param {DOM node or object} gd : graph div object
37096 */
37097module.exports = function clearGlCanvases(gd) {
37098 var fullLayout = gd._fullLayout;
37099
37100 if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
37101 fullLayout._glcanvas.each(function(d) {
37102 if(d.regl) d.regl.clear({color: true, depth: true});
37103 });
37104 }
37105};
37106
37107},{}],168:[function(_dereq_,module,exports){
37108/**
37109* Copyright 2012-2020, Plotly, Inc.
37110* All rights reserved.
37111*
37112* This source code is licensed under the MIT license found in the
37113* LICENSE file in the root directory of this source tree.
37114*/
37115
37116'use strict';
37117
37118/**
37119 * Clear responsive handlers (if any).
37120 *
37121 * @param {DOM node or object} gd : graph div object
37122 */
37123module.exports = function clearResponsive(gd) {
37124 if(gd._responsiveChartHandler) {
37125 window.removeEventListener('resize', gd._responsiveChartHandler);
37126 delete gd._responsiveChartHandler;
37127 }
37128};
37129
37130},{}],169:[function(_dereq_,module,exports){
37131/**
37132* Copyright 2012-2020, Plotly, Inc.
37133* All rights reserved.
37134*
37135* This source code is licensed under the MIT license found in the
37136* LICENSE file in the root directory of this source tree.
37137*/
37138
37139'use strict';
37140
37141var isNumeric = _dereq_('fast-isnumeric');
37142var tinycolor = _dereq_('tinycolor2');
37143
37144var baseTraceAttrs = _dereq_('../plots/attributes');
37145var colorscales = _dereq_('../components/colorscale/scales');
37146var DESELECTDIM = _dereq_('../constants/interactions').DESELECTDIM;
37147
37148var nestedProperty = _dereq_('./nested_property');
37149var counterRegex = _dereq_('./regex').counter;
37150var modHalf = _dereq_('./mod').modHalf;
37151var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
37152
37153exports.valObjectMeta = {
37154 data_array: {
37155 // You can use *dflt=[] to force said array to exist though.
37156
37157
37158
37159 coerceFunction: function(v, propOut, dflt) {
37160 // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also
37161 if(isArrayOrTypedArray(v)) propOut.set(v);
37162 else if(dflt !== undefined) propOut.set(dflt);
37163 }
37164 },
37165 enumerated: {
37166
37167
37168
37169 coerceFunction: function(v, propOut, dflt, opts) {
37170 if(opts.coerceNumber) v = +v;
37171 if(opts.values.indexOf(v) === -1) propOut.set(dflt);
37172 else propOut.set(v);
37173 },
37174 validateFunction: function(v, opts) {
37175 if(opts.coerceNumber) v = +v;
37176
37177 var values = opts.values;
37178 for(var i = 0; i < values.length; i++) {
37179 var k = String(values[i]);
37180
37181 if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) {
37182 var regex = new RegExp(k.substr(1, k.length - 2));
37183 if(regex.test(v)) return true;
37184 } else if(v === values[i]) return true;
37185 }
37186 return false;
37187 }
37188 },
37189 'boolean': {
37190
37191
37192
37193 coerceFunction: function(v, propOut, dflt) {
37194 if(v === true || v === false) propOut.set(v);
37195 else propOut.set(dflt);
37196 }
37197 },
37198 number: {
37199
37200
37201
37202 coerceFunction: function(v, propOut, dflt, opts) {
37203 if(!isNumeric(v) ||
37204 (opts.min !== undefined && v < opts.min) ||
37205 (opts.max !== undefined && v > opts.max)) {
37206 propOut.set(dflt);
37207 } else propOut.set(+v);
37208 }
37209 },
37210 integer: {
37211
37212
37213
37214 coerceFunction: function(v, propOut, dflt, opts) {
37215 if(v % 1 || !isNumeric(v) ||
37216 (opts.min !== undefined && v < opts.min) ||
37217 (opts.max !== undefined && v > opts.max)) {
37218 propOut.set(dflt);
37219 } else propOut.set(+v);
37220 }
37221 },
37222 string: {
37223
37224
37225 // TODO 'values shouldn't be in there (edge case: 'dash' in Scatter)
37226
37227 coerceFunction: function(v, propOut, dflt, opts) {
37228 if(typeof v !== 'string') {
37229 var okToCoerce = (typeof v === 'number');
37230
37231 if(opts.strict === true || !okToCoerce) propOut.set(dflt);
37232 else propOut.set(String(v));
37233 } else if(opts.noBlank && !v) propOut.set(dflt);
37234 else propOut.set(v);
37235 }
37236 },
37237 color: {
37238
37239
37240
37241 coerceFunction: function(v, propOut, dflt) {
37242 if(tinycolor(v).isValid()) propOut.set(v);
37243 else propOut.set(dflt);
37244 }
37245 },
37246 colorlist: {
37247
37248
37249
37250 coerceFunction: function(v, propOut, dflt) {
37251 function isColor(color) {
37252 return tinycolor(color).isValid();
37253 }
37254 if(!Array.isArray(v) || !v.length) propOut.set(dflt);
37255 else if(v.every(isColor)) propOut.set(v);
37256 else propOut.set(dflt);
37257 }
37258 },
37259 colorscale: {
37260
37261
37262
37263 coerceFunction: function(v, propOut, dflt) {
37264 propOut.set(colorscales.get(v, dflt));
37265 }
37266 },
37267 angle: {
37268
37269
37270
37271 coerceFunction: function(v, propOut, dflt) {
37272 if(v === 'auto') propOut.set('auto');
37273 else if(!isNumeric(v)) propOut.set(dflt);
37274 else propOut.set(modHalf(+v, 360));
37275 }
37276 },
37277 subplotid: {
37278
37279
37280
37281 coerceFunction: function(v, propOut, dflt, opts) {
37282 var regex = opts.regex || counterRegex(dflt);
37283 if(typeof v === 'string' && regex.test(v)) {
37284 propOut.set(v);
37285 return;
37286 }
37287 propOut.set(dflt);
37288 },
37289 validateFunction: function(v, opts) {
37290 var dflt = opts.dflt;
37291
37292 if(v === dflt) return true;
37293 if(typeof v !== 'string') return false;
37294 if(counterRegex(dflt).test(v)) return true;
37295
37296 return false;
37297 }
37298 },
37299 flaglist: {
37300
37301
37302
37303 coerceFunction: function(v, propOut, dflt, opts) {
37304 if(typeof v !== 'string') {
37305 propOut.set(dflt);
37306 return;
37307 }
37308 if((opts.extras || []).indexOf(v) !== -1) {
37309 propOut.set(v);
37310 return;
37311 }
37312 var vParts = v.split('+');
37313 var i = 0;
37314 while(i < vParts.length) {
37315 var vi = vParts[i];
37316 if(opts.flags.indexOf(vi) === -1 || vParts.indexOf(vi) < i) {
37317 vParts.splice(i, 1);
37318 } else i++;
37319 }
37320 if(!vParts.length) propOut.set(dflt);
37321 else propOut.set(vParts.join('+'));
37322 }
37323 },
37324 any: {
37325
37326
37327
37328 coerceFunction: function(v, propOut, dflt) {
37329 if(v === undefined) propOut.set(dflt);
37330 else propOut.set(v);
37331 }
37332 },
37333 info_array: {
37334
37335
37336 // set `dimensions=2` for a 2D array or '1-2' for either
37337 // `items` may be a single object instead of an array, in which case
37338 // `freeLength` must be true.
37339 // if `dimensions='1-2'` and items is a 1D array, then the value can
37340 // either be a matching 1D array or an array of such matching 1D arrays
37341
37342 coerceFunction: function(v, propOut, dflt, opts) {
37343 // simplified coerce function just for array items
37344 function coercePart(v, opts, dflt) {
37345 var out;
37346 var propPart = {set: function(v) { out = v; }};
37347
37348 if(dflt === undefined) dflt = opts.dflt;
37349
37350 exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
37351
37352 return out;
37353 }
37354
37355 var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0]));
37356
37357 if(!Array.isArray(v)) {
37358 propOut.set(dflt);
37359 return;
37360 }
37361
37362 var items = opts.items;
37363 var vOut = [];
37364 var arrayItems = Array.isArray(items);
37365 var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]);
37366 var innerItemsOnly = twoD && arrayItems && !arrayItems2D;
37367 var len = (arrayItems && !innerItemsOnly) ? items.length : v.length;
37368
37369 var i, j, row, item, len2, vNew;
37370
37371 dflt = Array.isArray(dflt) ? dflt : [];
37372
37373 if(twoD) {
37374 for(i = 0; i < len; i++) {
37375 vOut[i] = [];
37376 row = Array.isArray(v[i]) ? v[i] : [];
37377 if(innerItemsOnly) len2 = items.length;
37378 else if(arrayItems) len2 = items[i].length;
37379 else len2 = row.length;
37380
37381 for(j = 0; j < len2; j++) {
37382 if(innerItemsOnly) item = items[j];
37383 else if(arrayItems) item = items[i][j];
37384 else item = items;
37385
37386 vNew = coercePart(row[j], item, (dflt[i] || [])[j]);
37387 if(vNew !== undefined) vOut[i][j] = vNew;
37388 }
37389 }
37390 } else {
37391 for(i = 0; i < len; i++) {
37392 vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
37393 if(vNew !== undefined) vOut[i] = vNew;
37394 }
37395 }
37396
37397 propOut.set(vOut);
37398 },
37399 validateFunction: function(v, opts) {
37400 if(!Array.isArray(v)) return false;
37401
37402 var items = opts.items;
37403 var arrayItems = Array.isArray(items);
37404 var twoD = opts.dimensions === 2;
37405
37406 // when free length is off, input and declared lengths must match
37407 if(!opts.freeLength && v.length !== items.length) return false;
37408
37409 // valid when all input items are valid
37410 for(var i = 0; i < v.length; i++) {
37411 if(twoD) {
37412 if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
37413 return false;
37414 }
37415 for(var j = 0; j < v[i].length; j++) {
37416 if(!validate(v[i][j], arrayItems ? items[i][j] : items)) {
37417 return false;
37418 }
37419 }
37420 } else if(!validate(v[i], arrayItems ? items[i] : items)) return false;
37421 }
37422
37423 return true;
37424 }
37425 }
37426};
37427
37428/**
37429 * Ensures that container[attribute] has a valid value.
37430 *
37431 * attributes[attribute] is an object with possible keys:
37432 * - valType: data_array, enumerated, boolean, ... as in valObjectMeta
37433 * - values: (enumerated only) array of allowed vals
37434 * - min, max: (number, integer only) inclusive bounds on allowed vals
37435 * either or both may be omitted
37436 * - dflt: if attribute is invalid or missing, use this default
37437 * if dflt is provided as an argument to lib.coerce it takes precedence
37438 * as a convenience, returns the value it finally set
37439 */
37440exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
37441 var opts = nestedProperty(attributes, attribute).get();
37442 var propIn = nestedProperty(containerIn, attribute);
37443 var propOut = nestedProperty(containerOut, attribute);
37444 var v = propIn.get();
37445
37446 var template = containerOut._template;
37447 if(v === undefined && template) {
37448 v = nestedProperty(template, attribute).get();
37449 // already used the template value, so short-circuit the second check
37450 template = 0;
37451 }
37452
37453 if(dflt === undefined) dflt = opts.dflt;
37454
37455 /**
37456 * arrayOk: value MAY be an array, then we do no value checking
37457 * at this point, because it can be more complicated than the
37458 * individual form (eg. some array vals can be numbers, even if the
37459 * single values must be color strings)
37460 */
37461 if(opts.arrayOk && isArrayOrTypedArray(v)) {
37462 propOut.set(v);
37463 return v;
37464 }
37465
37466 var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction;
37467 coerceFunction(v, propOut, dflt, opts);
37468
37469 var out = propOut.get();
37470 // in case v was provided but invalid, try the template again so it still
37471 // overrides the regular default
37472 if(template && out === dflt && !validate(v, opts)) {
37473 v = nestedProperty(template, attribute).get();
37474 coerceFunction(v, propOut, dflt, opts);
37475 out = propOut.get();
37476 }
37477 return out;
37478};
37479
37480/**
37481 * Variation on coerce
37482 *
37483 * Uses coerce to get attribute value if user input is valid,
37484 * returns attribute default if user input it not valid or
37485 * returns false if there is no user input.
37486 */
37487exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) {
37488 var propIn = nestedProperty(containerIn, attribute);
37489 var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt);
37490 var valIn = propIn.get();
37491
37492 return (valIn !== undefined && valIn !== null) ? propOut : false;
37493};
37494
37495/*
37496 * Shortcut to coerce the three font attributes
37497 *
37498 * 'coerce' is a lib.coerce wrapper with implied first three arguments
37499 */
37500exports.coerceFont = function(coerce, attr, dfltObj) {
37501 var out = {};
37502
37503 dfltObj = dfltObj || {};
37504
37505 out.family = coerce(attr + '.family', dfltObj.family);
37506 out.size = coerce(attr + '.size', dfltObj.size);
37507 out.color = coerce(attr + '.color', dfltObj.color);
37508
37509 return out;
37510};
37511
37512/** Coerce shortcut for 'hoverinfo'
37513 * handling 1-vs-multi-trace dflt logic
37514 *
37515 * @param {object} traceIn : user trace object
37516 * @param {object} traceOut : full trace object (requires _module ref)
37517 * @param {object} layoutOut : full layout object (require _dataLength ref)
37518 * @return {any} : the coerced value
37519 */
37520exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) {
37521 var moduleAttrs = traceOut._module.attributes;
37522 var attrs = moduleAttrs.hoverinfo ? moduleAttrs : baseTraceAttrs;
37523
37524 var valObj = attrs.hoverinfo;
37525 var dflt;
37526
37527 if(layoutOut._dataLength === 1) {
37528 var flags = valObj.dflt === 'all' ?
37529 valObj.flags.slice() :
37530 valObj.dflt.split('+');
37531
37532 flags.splice(flags.indexOf('name'), 1);
37533 dflt = flags.join('+');
37534 }
37535
37536 return exports.coerce(traceIn, traceOut, attrs, 'hoverinfo', dflt);
37537};
37538
37539/** Coerce shortcut for [un]selected.marker.opacity,
37540 * which has special default logic, to ensure that it corresponds to the
37541 * default selection behavior while allowing to be overtaken by any other
37542 * [un]selected attribute.
37543 *
37544 * N.B. This must be called *after* coercing all the other [un]selected attrs,
37545 * to give the intended result.
37546 *
37547 * @param {object} traceOut : fullData item
37548 * @param {function} coerce : lib.coerce wrapper with implied first three arguments
37549 */
37550exports.coerceSelectionMarkerOpacity = function(traceOut, coerce) {
37551 if(!traceOut.marker) return;
37552
37553 var mo = traceOut.marker.opacity;
37554 // you can still have a `marker` container with no markers if there's text
37555 if(mo === undefined) return;
37556
37557 var smoDflt;
37558 var usmoDflt;
37559
37560 // Don't give [un]selected.marker.opacity a default value if
37561 // marker.opacity is an array: handle this during style step.
37562 //
37563 // Only give [un]selected.marker.opacity a default value if you don't
37564 // set any other [un]selected attributes.
37565 if(!isArrayOrTypedArray(mo) && !traceOut.selected && !traceOut.unselected) {
37566 smoDflt = mo;
37567 usmoDflt = DESELECTDIM * mo;
37568 }
37569
37570 coerce('selected.marker.opacity', smoDflt);
37571 coerce('unselected.marker.opacity', usmoDflt);
37572};
37573
37574function validate(value, opts) {
37575 var valObjectDef = exports.valObjectMeta[opts.valType];
37576
37577 if(opts.arrayOk && isArrayOrTypedArray(value)) return true;
37578
37579 if(valObjectDef.validateFunction) {
37580 return valObjectDef.validateFunction(value, opts);
37581 }
37582
37583 var failed = {};
37584 var out = failed;
37585 var propMock = { set: function(v) { out = v; } };
37586
37587 // 'failed' just something mutable that won't be === anything else
37588
37589 valObjectDef.coerceFunction(value, propMock, failed, opts);
37590 return out !== failed;
37591}
37592exports.validate = validate;
37593
37594},{"../components/colorscale/scales":67,"../constants/interactions":157,"../plots/attributes":219,"./array":165,"./mod":185,"./nested_property":186,"./regex":193,"fast-isnumeric":18,"tinycolor2":35}],170:[function(_dereq_,module,exports){
37595/**
37596* Copyright 2012-2020, Plotly, Inc.
37597* All rights reserved.
37598*
37599* This source code is licensed under the MIT license found in the
37600* LICENSE file in the root directory of this source tree.
37601*/
37602
37603
37604'use strict';
37605
37606var d3 = _dereq_('d3');
37607var isNumeric = _dereq_('fast-isnumeric');
37608
37609var Loggers = _dereq_('./loggers');
37610var mod = _dereq_('./mod').mod;
37611
37612var constants = _dereq_('../constants/numerical');
37613var BADNUM = constants.BADNUM;
37614var ONEDAY = constants.ONEDAY;
37615var ONEHOUR = constants.ONEHOUR;
37616var ONEMIN = constants.ONEMIN;
37617var ONESEC = constants.ONESEC;
37618var EPOCHJD = constants.EPOCHJD;
37619
37620var Registry = _dereq_('../registry');
37621
37622var utcFormat = d3.time.format.utc;
37623
37624var 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;
37625// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months
37626var 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;
37627
37628// for 2-digit years, the first year we map them onto
37629var YFIRST = new Date().getFullYear() - 70;
37630
37631function isWorldCalendar(calendar) {
37632 return (
37633 calendar &&
37634 Registry.componentsRegistry.calendars &&
37635 typeof calendar === 'string' && calendar !== 'gregorian'
37636 );
37637}
37638
37639/*
37640 * dateTick0: get the canonical tick for this calendar
37641 *
37642 * bool sunday is for week ticks, shift it to a Sunday.
37643 */
37644exports.dateTick0 = function(calendar, sunday) {
37645 if(isWorldCalendar(calendar)) {
37646 return sunday ?
37647 Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
37648 Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
37649 } else {
37650 return sunday ? '2000-01-02' : '2000-01-01';
37651 }
37652};
37653
37654/*
37655 * dfltRange: for each calendar, give a valid default range
37656 */
37657exports.dfltRange = function(calendar) {
37658 if(isWorldCalendar(calendar)) {
37659 return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
37660 } else {
37661 return ['2000-01-01', '2001-01-01'];
37662 }
37663};
37664
37665// is an object a javascript date?
37666exports.isJSDate = function(v) {
37667 return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
37668};
37669
37670// The absolute limits of our date-time system
37671// This is a little weird: we use MIN_MS and MAX_MS in dateTime2ms
37672// but we use dateTime2ms to calculate them (after defining it!)
37673var MIN_MS, MAX_MS;
37674
37675/**
37676 * dateTime2ms - turn a date object or string s into milliseconds
37677 * (relative to 1970-01-01, per javascript standard)
37678 * optional calendar (string) to use a non-gregorian calendar
37679 *
37680 * Returns BADNUM if it doesn't find a date
37681 *
37682 * strings should have the form:
37683 *
37684 * -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
37685 *
37686 * <sep>: space (our normal standard) or T or t (ISO-8601)
37687 * <tzInfo>: Z, z, or [+\-]HH:?MM and we THROW IT AWAY
37688 * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
37689 * but we allow it even with a space as the separator
37690 *
37691 * May truncate after any full field, and sss can be any length
37692 * even >3 digits, though javascript dates truncate to milliseconds,
37693 * we keep as much as javascript numeric precision can hold, but we only
37694 * report back up to 100 microsecond precision, because most dates support
37695 * this precision (close to 1970 support more, very far away support less)
37696 *
37697 * Expanded to support negative years to -9999 but you must always
37698 * give 4 digits, except for 2-digit positive years which we assume are
37699 * near the present time.
37700 * Note that we follow ISO 8601:2004: there *is* a year 0, which
37701 * is 1BC/BCE, and -1===2BC etc.
37702 *
37703 * World calendars: not all of these *have* agreed extensions to this full range,
37704 * if you have another calendar system but want a date range outside its validity,
37705 * you can use a gregorian date string prefixed with 'G' or 'g'.
37706 *
37707 * Where to cut off 2-digit years between 1900s and 2000s?
37708 * from http://support.microsoft.com/kb/244664:
37709 * 1930-2029 (the most retro of all...)
37710 * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
37711 * 1950-2049
37712 * by Java, from http://stackoverflow.com/questions/2024273/:
37713 * now-80 - now+19
37714 * or FileMaker Pro, from
37715 * http://www.filemaker.com/12help/html/add_view_data.4.21.html:
37716 * now-70 - now+29
37717 * but python strptime etc, via
37718 * http://docs.python.org/py3k/library/time.html:
37719 * 1969-2068 (super forward-looking, but static, not sliding!)
37720 *
37721 * lets go with now-70 to now+29, and if anyone runs into this problem
37722 * they can learn the hard way not to use 2-digit years, as no choice we
37723 * make now will cover all possibilities. mostly this will all be taken
37724 * care of in initial parsing, should only be an issue for hand-entered data
37725 * currently (2016) this range is:
37726 * 1946-2045
37727 */
37728exports.dateTime2ms = function(s, calendar) {
37729 // first check if s is a date object
37730 if(exports.isJSDate(s)) {
37731 // Convert to the UTC milliseconds that give the same
37732 // hours as this date has in the local timezone
37733 var tzOffset = s.getTimezoneOffset() * ONEMIN;
37734 var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN +
37735 (s.getUTCSeconds() - s.getSeconds()) * ONESEC +
37736 (s.getUTCMilliseconds() - s.getMilliseconds());
37737
37738 if(offsetTweak) {
37739 var comb = 3 * ONEMIN;
37740 tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb);
37741 }
37742 s = Number(s) - tzOffset;
37743 if(s >= MIN_MS && s <= MAX_MS) return s;
37744 return BADNUM;
37745 }
37746 // otherwise only accept strings and numbers
37747 if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
37748
37749 s = String(s);
37750
37751 var isWorld = isWorldCalendar(calendar);
37752
37753 // to handle out-of-range dates in international calendars, accept
37754 // 'G' as a prefix to force the built-in gregorian calendar.
37755 var s0 = s.charAt(0);
37756 if(isWorld && (s0 === 'G' || s0 === 'g')) {
37757 s = s.substr(1);
37758 calendar = '';
37759 }
37760
37761 var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
37762
37763 var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
37764 if(!match) return BADNUM;
37765 var y = match[1];
37766 var m = match[3] || '1';
37767 var d = Number(match[5] || 1);
37768 var H = Number(match[7] || 0);
37769 var M = Number(match[9] || 0);
37770 var S = Number(match[11] || 0);
37771
37772 if(isWorld) {
37773 // disallow 2-digit years for world calendars
37774 if(y.length === 2) return BADNUM;
37775 y = Number(y);
37776
37777 var cDate;
37778 try {
37779 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
37780 if(isChinese) {
37781 var isIntercalary = m.charAt(m.length - 1) === 'i';
37782 m = parseInt(m, 10);
37783 cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
37784 } else {
37785 cDate = calInstance.newDate(y, Number(m), d);
37786 }
37787 } catch(e) { return BADNUM; } // Invalid ... date
37788
37789 if(!cDate) return BADNUM;
37790
37791 return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
37792 (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
37793 }
37794
37795 if(y.length === 2) {
37796 y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
37797 } else y = Number(y);
37798
37799 // new Date uses months from 0; subtract 1 here just so we
37800 // don't have to do it again during the validity test below
37801 m -= 1;
37802
37803 // javascript takes new Date(0..99,m,d) to mean 1900-1999, so
37804 // to support years 0-99 we need to use setFullYear explicitly
37805 // Note that 2000 is a leap year.
37806 var date = new Date(Date.UTC(2000, m, d, H, M));
37807 date.setUTCFullYear(y);
37808
37809 if(date.getUTCMonth() !== m) return BADNUM;
37810 if(date.getUTCDate() !== d) return BADNUM;
37811
37812 return date.getTime() + S * ONESEC;
37813};
37814
37815MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
37816MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
37817
37818// is string s a date? (see above)
37819exports.isDateTime = function(s, calendar) {
37820 return (exports.dateTime2ms(s, calendar) !== BADNUM);
37821};
37822
37823// pad a number with zeroes, to given # of digits before the decimal point
37824function lpad(val, digits) {
37825 return String(val + Math.pow(10, digits)).substr(1);
37826}
37827
37828/**
37829 * Turn ms into string of the form YYYY-mm-dd HH:MM:SS.ssss
37830 * Crop any trailing zeros in time, except never stop right after hours
37831 * (we could choose to crop '-01' from date too but for now we always
37832 * show the whole date)
37833 * Optional range r is the data range that applies, also in ms.
37834 * If rng is big, the later parts of time will be omitted
37835 */
37836var NINETYDAYS = 90 * ONEDAY;
37837var THREEHOURS = 3 * ONEHOUR;
37838var FIVEMIN = 5 * ONEMIN;
37839exports.ms2DateTime = function(ms, r, calendar) {
37840 if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
37841
37842 if(!r) r = 0;
37843
37844 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
37845 var msRounded = Math.round(ms - msecTenths / 10);
37846 var dateStr, h, m, s, msec10, d;
37847
37848 if(isWorldCalendar(calendar)) {
37849 var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD;
37850 var timeMs = Math.floor(mod(ms, ONEDAY));
37851 try {
37852 dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
37853 .fromJD(dateJD).formatDate('yyyy-mm-dd');
37854 } catch(e) {
37855 // invalid date in this calendar - fall back to Gyyyy-mm-dd
37856 dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
37857 }
37858
37859 // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
37860 // other things for a few calendars, so we can't trust it. Just pad
37861 // it manually (after the '-' if there is one)
37862 if(dateStr.charAt(0) === '-') {
37863 while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
37864 } else {
37865 while(dateStr.length < 10) dateStr = '0' + dateStr;
37866 }
37867
37868 // TODO: if this is faster, we could use this block for extracting
37869 // the time components of regular gregorian too
37870 h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
37871 m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
37872 s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
37873 msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
37874 } else {
37875 d = new Date(msRounded);
37876
37877 dateStr = utcFormat('%Y-%m-%d')(d);
37878
37879 // <90 days: add hours and minutes - never *only* add hours
37880 h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
37881 m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
37882 // <3 hours: add seconds
37883 s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
37884 // <5 minutes: add ms (plus one extra digit, this is msec*10)
37885 msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
37886 }
37887
37888 return includeTime(dateStr, h, m, s, msec10);
37889};
37890
37891// For converting old-style milliseconds to date strings,
37892// we use the local timezone rather than UTC like we use
37893// everywhere else, both for backward compatibility and
37894// because that's how people mostly use javasript date objects.
37895// Clip one extra day off our date range though so we can't get
37896// thrown beyond the range by the timezone shift.
37897exports.ms2DateTimeLocal = function(ms) {
37898 if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
37899
37900 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
37901 var d = new Date(Math.round(ms - msecTenths / 10));
37902 var dateStr = d3.time.format('%Y-%m-%d')(d);
37903 var h = d.getHours();
37904 var m = d.getMinutes();
37905 var s = d.getSeconds();
37906 var msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
37907
37908 return includeTime(dateStr, h, m, s, msec10);
37909};
37910
37911function includeTime(dateStr, h, m, s, msec10) {
37912 // include each part that has nonzero data in or after it
37913 if(h || m || s || msec10) {
37914 dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2);
37915 if(s || msec10) {
37916 dateStr += ':' + lpad(s, 2);
37917 if(msec10) {
37918 var digits = 4;
37919 while(msec10 % 10 === 0) {
37920 digits -= 1;
37921 msec10 /= 10;
37922 }
37923 dateStr += '.' + lpad(msec10, digits);
37924 }
37925 }
37926 }
37927 return dateStr;
37928}
37929
37930// normalize date format to date string, in case it starts as
37931// a Date object or milliseconds
37932// optional dflt is the return value if cleaning fails
37933exports.cleanDate = function(v, dflt, calendar) {
37934 // let us use cleanDate to provide a missing default without an error
37935 if(v === BADNUM) return dflt;
37936 if(exports.isJSDate(v) || (typeof v === 'number' && isFinite(v))) {
37937 // do not allow milliseconds (old) or jsdate objects (inherently
37938 // described as gregorian dates) with world calendars
37939 if(isWorldCalendar(calendar)) {
37940 Loggers.error('JS Dates and milliseconds are incompatible with world calendars', v);
37941 return dflt;
37942 }
37943
37944 // NOTE: if someone puts in a year as a number rather than a string,
37945 // this will mistakenly convert it thinking it's milliseconds from 1970
37946 // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
37947 v = exports.ms2DateTimeLocal(+v);
37948 if(!v && dflt !== undefined) return dflt;
37949 } else if(!exports.isDateTime(v, calendar)) {
37950 Loggers.error('unrecognized date', v);
37951 return dflt;
37952 }
37953 return v;
37954};
37955
37956/*
37957 * Date formatting for ticks and hovertext
37958 */
37959
37960/*
37961 * modDateFormat: Support world calendars, and add one item to
37962 * d3's vocabulary:
37963 * %{n}f where n is the max number of digits of fractional seconds
37964 */
37965var fracMatch = /%\d?f/g;
37966function modDateFormat(fmt, x, formatter, calendar) {
37967 fmt = fmt.replace(fracMatch, function(match) {
37968 var digits = Math.min(+(match.charAt(1)) || 6, 6);
37969 var fracSecs = ((x / 1000 % 1) + 2)
37970 .toFixed(digits)
37971 .substr(2).replace(/0+$/, '') || '0';
37972 return fracSecs;
37973 });
37974
37975 var d = new Date(Math.floor(x + 0.05));
37976
37977 if(isWorldCalendar(calendar)) {
37978 try {
37979 fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar);
37980 } catch(e) {
37981 return 'Invalid';
37982 }
37983 }
37984 return formatter(fmt)(d);
37985}
37986
37987/*
37988 * formatTime: create a time string from:
37989 * x: milliseconds
37990 * tr: tickround ('M', 'S', or # digits)
37991 * only supports UTC times (where every day is 24 hours and 0 is at midnight)
37992 */
37993var MAXSECONDS = [59, 59.9, 59.99, 59.999, 59.9999];
37994function formatTime(x, tr) {
37995 var timePart = mod(x + 0.05, ONEDAY);
37996
37997 var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' +
37998 lpad(mod(Math.floor(timePart / ONEMIN), 60), 2);
37999
38000 if(tr !== 'M') {
38001 if(!isNumeric(tr)) tr = 0; // should only be 'S'
38002
38003 /*
38004 * this is a weird one - and shouldn't come up unless people
38005 * monkey with tick0 in weird ways, but we need to do something!
38006 * IN PARTICULAR we had better not display garbage (see below)
38007 * for numbers we always round to the nearest increment of the
38008 * precision we're showing, and this seems like the right way to
38009 * handle seconds and milliseconds, as they have a decimal point
38010 * and people will interpret that to mean rounding like numbers.
38011 * but for larger increments we floor the value: it's always
38012 * 2013 until the ball drops on the new year. We could argue about
38013 * which field it is where we start rounding (should 12:08:59
38014 * round to 12:09 if we're stopping at minutes?) but for now I'll
38015 * say we round seconds but floor everything else. BUT that means
38016 * we need to never round up to 60 seconds, ie 23:59:60
38017 */
38018 var sec = Math.min(mod(x / ONESEC, 60), MAXSECONDS[tr]);
38019
38020 var secStr = (100 + sec).toFixed(tr).substr(1);
38021 if(tr > 0) {
38022 secStr = secStr.replace(/0+$/, '').replace(/[\.]$/, '');
38023 }
38024
38025 timeStr += ':' + secStr;
38026 }
38027 return timeStr;
38028}
38029
38030/*
38031 * formatDate: turn a date into tick or hover label text.
38032 *
38033 * x: milliseconds, the value to convert
38034 * fmt: optional, an explicit format string (d3 format, even for world calendars)
38035 * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits)
38036 * used if no explicit fmt is provided
38037 * formatter: locale-aware d3 date formatter for standard gregorian calendars
38038 * should be the result of exports.getD3DateFormat(gd)
38039 * calendar: optional string, the world calendar system to use
38040 *
38041 * returns the date/time as a string, potentially with the leading portion
38042 * on a separate line (after '\n')
38043 * Note that this means if you provide an explicit format which includes '\n'
38044 * the axis may choose to strip things after it when they don't change from
38045 * one tick to the next (as it does with automatic formatting)
38046 */
38047exports.formatDate = function(x, fmt, tr, formatter, calendar, extraFormat) {
38048 calendar = isWorldCalendar(calendar) && calendar;
38049
38050 if(!fmt) {
38051 if(tr === 'y') fmt = extraFormat.year;
38052 else if(tr === 'm') fmt = extraFormat.month;
38053 else if(tr === 'd') {
38054 fmt = extraFormat.dayMonth + '\n' + extraFormat.year;
38055 } else {
38056 return formatTime(x, tr) + '\n' + modDateFormat(extraFormat.dayMonthYear, x, formatter, calendar);
38057 }
38058 }
38059
38060 return modDateFormat(fmt, x, formatter, calendar);
38061};
38062
38063/*
38064 * incrementMonth: make a new milliseconds value from the given one,
38065 * having changed the month
38066 *
38067 * special case for world calendars: multiples of 12 are treated as years,
38068 * even for calendar systems that don't have (always or ever) 12 months/year
38069 * TODO: perhaps we need a different code for year increments to support this?
38070 *
38071 * ms (number): the initial millisecond value
38072 * dMonth (int): the (signed) number of months to shift
38073 * calendar (string): the calendar system to use
38074 *
38075 * changing month does not (and CANNOT) always preserve day, since
38076 * months have different lengths. The worst example of this is:
38077 * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3
38078 *
38079 * But we want to be able to iterate over the last day of each month,
38080 * regardless of what its number is.
38081 * So shift 3 days forward, THEN set the new month, then unshift:
38082 * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ...
38083 *
38084 * Note that odd behavior still exists if you start from the 26th-28th:
38085 * 1/28 -> 2/28 -> 3/31
38086 * but at least you can't shift any dates into the wrong month,
38087 * and ticks on these days incrementing by month would be very unusual
38088 */
38089var THREEDAYS = 3 * ONEDAY;
38090exports.incrementMonth = function(ms, dMonth, calendar) {
38091 calendar = isWorldCalendar(calendar) && calendar;
38092
38093 // pull time out and operate on pure dates, then add time back at the end
38094 // this gives maximum precision - not that we *normally* care if we're
38095 // incrementing by month, but better to be safe!
38096 var timeMs = mod(ms, ONEDAY);
38097 ms = Math.round(ms - timeMs);
38098
38099 if(calendar) {
38100 try {
38101 var dateJD = Math.round(ms / ONEDAY) + EPOCHJD;
38102 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
38103 var cDate = calInstance.fromJD(dateJD);
38104
38105 if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
38106 else calInstance.add(cDate, dMonth / 12, 'y');
38107
38108 return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
38109 } catch(e) {
38110 Loggers.error('invalid ms ' + ms + ' in calendar ' + calendar);
38111 // then keep going in gregorian even though the result will be 'Invalid'
38112 }
38113 }
38114
38115 var y = new Date(ms + THREEDAYS);
38116 return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
38117};
38118
38119/*
38120 * findExactDates: what fraction of data is exact days, months, or years?
38121 *
38122 * data: array of millisecond values
38123 * calendar (string) the calendar to test against
38124 */
38125exports.findExactDates = function(data, calendar) {
38126 var exactYears = 0;
38127 var exactMonths = 0;
38128 var exactDays = 0;
38129 var blankCount = 0;
38130 var d;
38131 var di;
38132
38133 var calInstance = (
38134 isWorldCalendar(calendar) &&
38135 Registry.getComponentMethod('calendars', 'getCal')(calendar)
38136 );
38137
38138 for(var i = 0; i < data.length; i++) {
38139 di = data[i];
38140
38141 // not date data at all
38142 if(!isNumeric(di)) {
38143 blankCount ++;
38144 continue;
38145 }
38146
38147 // not an exact date
38148 if(di % ONEDAY) continue;
38149
38150 if(calInstance) {
38151 try {
38152 d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
38153 if(d.day() === 1) {
38154 if(d.month() === 1) exactYears++;
38155 else exactMonths++;
38156 } else exactDays++;
38157 } catch(e) {
38158 // invalid date in this calendar - ignore it here.
38159 }
38160 } else {
38161 d = new Date(di);
38162 if(d.getUTCDate() === 1) {
38163 if(d.getUTCMonth() === 0) exactYears++;
38164 else exactMonths++;
38165 } else exactDays++;
38166 }
38167 }
38168 exactMonths += exactYears;
38169 exactDays += exactMonths;
38170
38171 var dataCount = data.length - blankCount;
38172
38173 return {
38174 exactYears: exactYears / dataCount,
38175 exactMonths: exactMonths / dataCount,
38176 exactDays: exactDays / dataCount
38177 };
38178};
38179
38180},{"../constants/numerical":158,"../registry":269,"./loggers":182,"./mod":185,"d3":16,"fast-isnumeric":18}],171:[function(_dereq_,module,exports){
38181/**
38182* Copyright 2012-2020, Plotly, Inc.
38183* All rights reserved.
38184*
38185* This source code is licensed under the MIT license found in the
38186* LICENSE file in the root directory of this source tree.
38187*/
38188
38189'use strict';
38190
38191var d3 = _dereq_('d3');
38192var loggers = _dereq_('./loggers');
38193
38194/**
38195 * Allow referencing a graph DOM element either directly
38196 * or by its id string
38197 *
38198 * @param {HTMLDivElement|string} gd: a graph element or its id
38199 *
38200 * @returns {HTMLDivElement} the DOM element of the graph
38201 */
38202function getGraphDiv(gd) {
38203 var gdElement;
38204
38205 if(typeof gd === 'string') {
38206 gdElement = document.getElementById(gd);
38207
38208 if(gdElement === null) {
38209 throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
38210 }
38211
38212 return gdElement;
38213 } else if(gd === null || gd === undefined) {
38214 throw new Error('DOM element provided is null or undefined');
38215 }
38216
38217 // otherwise assume that gd is a DOM element
38218 return gd;
38219}
38220
38221function isPlotDiv(el) {
38222 var el3 = d3.select(el);
38223 return el3.node() instanceof HTMLElement &&
38224 el3.size() &&
38225 el3.classed('js-plotly-plot');
38226}
38227
38228function removeElement(el) {
38229 var elParent = el && el.parentNode;
38230 if(elParent) elParent.removeChild(el);
38231}
38232
38233/**
38234 * for dynamically adding style rules
38235 * makes one stylesheet that contains all rules added
38236 * by all calls to this function
38237 */
38238function addStyleRule(selector, styleString) {
38239 addRelatedStyleRule('global', selector, styleString);
38240}
38241
38242/**
38243 * for dynamically adding style rules
38244 * to a stylesheet uniquely identified by a uid
38245 */
38246function addRelatedStyleRule(uid, selector, styleString) {
38247 var id = 'plotly.js-style-' + uid;
38248 var style = document.getElementById(id);
38249 if(!style) {
38250 style = document.createElement('style');
38251 style.setAttribute('id', id);
38252 // WebKit hack :(
38253 style.appendChild(document.createTextNode(''));
38254 document.head.appendChild(style);
38255 }
38256 var styleSheet = style.sheet;
38257
38258 if(styleSheet.insertRule) {
38259 styleSheet.insertRule(selector + '{' + styleString + '}', 0);
38260 } else if(styleSheet.addRule) {
38261 styleSheet.addRule(selector, styleString, 0);
38262 } else loggers.warn('addStyleRule failed');
38263}
38264
38265/**
38266 * to remove from the page a stylesheet identified by a given uid
38267 */
38268function deleteRelatedStyleRule(uid) {
38269 var id = 'plotly.js-style-' + uid;
38270 var style = document.getElementById(id);
38271 if(style) removeElement(style);
38272}
38273
38274module.exports = {
38275 getGraphDiv: getGraphDiv,
38276 isPlotDiv: isPlotDiv,
38277 removeElement: removeElement,
38278 addStyleRule: addStyleRule,
38279 addRelatedStyleRule: addRelatedStyleRule,
38280 deleteRelatedStyleRule: deleteRelatedStyleRule
38281};
38282
38283},{"./loggers":182,"d3":16}],172:[function(_dereq_,module,exports){
38284/**
38285* Copyright 2012-2020, Plotly, Inc.
38286* All rights reserved.
38287*
38288* This source code is licensed under the MIT license found in the
38289* LICENSE file in the root directory of this source tree.
38290*/
38291
38292
38293'use strict';
38294
38295/* global jQuery:false */
38296
38297var EventEmitter = _dereq_('events').EventEmitter;
38298
38299var Events = {
38300
38301 init: function(plotObj) {
38302 /*
38303 * If we have already instantiated an emitter for this plot
38304 * return early.
38305 */
38306 if(plotObj._ev instanceof EventEmitter) return plotObj;
38307
38308 var ev = new EventEmitter();
38309 var internalEv = new EventEmitter();
38310
38311 /*
38312 * Assign to plot._ev while we still live in a land
38313 * where plot is a DOM element with stuff attached to it.
38314 * In the future we can make plot the event emitter itself.
38315 */
38316 plotObj._ev = ev;
38317
38318 /*
38319 * Create a second event handler that will manage events *internally*.
38320 * This allows parts of plotly to respond to thing like relayout without
38321 * having to use the user-facing event handler. They cannot peacefully
38322 * coexist on the same handler because a user invoking
38323 * plotObj.removeAllListeners() would detach internal events, breaking
38324 * plotly.
38325 */
38326 plotObj._internalEv = internalEv;
38327
38328 /*
38329 * Assign bound methods from the ev to the plot object. These methods
38330 * will reference the 'this' of plot._ev even though they are methods
38331 * of plot. This will keep the event machinery away from the plot object
38332 * which currently is often a DOM element but presents an API that will
38333 * continue to function when plot becomes an emitter. Not all EventEmitter
38334 * methods have been bound to `plot` as some do not currently add value to
38335 * the Plotly event API.
38336 */
38337 plotObj.on = ev.on.bind(ev);
38338 plotObj.once = ev.once.bind(ev);
38339 plotObj.removeListener = ev.removeListener.bind(ev);
38340 plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
38341
38342 /*
38343 * Create functions for managing internal events. These are *only* triggered
38344 * by the mirroring of external events via the emit function.
38345 */
38346 plotObj._internalOn = internalEv.on.bind(internalEv);
38347 plotObj._internalOnce = internalEv.once.bind(internalEv);
38348 plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
38349 plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
38350
38351 /*
38352 * We must wrap emit to continue to support JQuery events. The idea
38353 * is to check to see if the user is using JQuery events, if they are
38354 * we emit JQuery events to trigger user handlers as well as the EventEmitter
38355 * events.
38356 */
38357 plotObj.emit = function(event, data) {
38358 if(typeof jQuery !== 'undefined') {
38359 jQuery(plotObj).trigger(event, data);
38360 }
38361
38362 ev.emit(event, data);
38363 internalEv.emit(event, data);
38364 };
38365
38366 return plotObj;
38367 },
38368
38369 /*
38370 * This function behaves like jQuery's triggerHandler. It calls
38371 * all handlers for a particular event and returns the return value
38372 * of the LAST handler. This function also triggers jQuery's
38373 * triggerHandler for backwards compatibility.
38374 */
38375 triggerHandler: function(plotObj, event, data) {
38376 var jQueryHandlerValue;
38377 var nodeEventHandlerValue;
38378
38379 /*
38380 * If jQuery exists run all its handlers for this event and
38381 * collect the return value of the LAST handler function
38382 */
38383 if(typeof jQuery !== 'undefined') {
38384 jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
38385 }
38386
38387 /*
38388 * Now run all the node style event handlers
38389 */
38390 var ev = plotObj._ev;
38391 if(!ev) return jQueryHandlerValue;
38392
38393 var handlers = ev._events[event];
38394 if(!handlers) return jQueryHandlerValue;
38395
38396 // making sure 'this' is the EventEmitter instance
38397 function apply(handler) {
38398 // The 'once' case, we can't just call handler() as we need
38399 // the return value here. So,
38400 // - remove handler
38401 // - call listener and grab return value!
38402 // - stash 'fired' key to not call handler twice
38403 if(handler.listener) {
38404 ev.removeListener(event, handler.listener);
38405 if(!handler.fired) {
38406 handler.fired = true;
38407 return handler.listener.apply(ev, [data]);
38408 }
38409 } else {
38410 return handler.apply(ev, [data]);
38411 }
38412 }
38413
38414 // handlers can be function or an array of functions
38415 handlers = Array.isArray(handlers) ? handlers : [handlers];
38416
38417 var i;
38418 for(i = 0; i < handlers.length - 1; i++) {
38419 apply(handlers[i]);
38420 }
38421 // now call the final handler and collect its value
38422 nodeEventHandlerValue = apply(handlers[i]);
38423
38424 /*
38425 * Return either the jQuery handler value if it exists or the
38426 * nodeEventHandler value. jQuery event value supersedes nodejs
38427 * events for backwards compatibility reasons.
38428 */
38429 return jQueryHandlerValue !== undefined ?
38430 jQueryHandlerValue :
38431 nodeEventHandlerValue;
38432 },
38433
38434 purge: function(plotObj) {
38435 delete plotObj._ev;
38436 delete plotObj.on;
38437 delete plotObj.once;
38438 delete plotObj.removeListener;
38439 delete plotObj.removeAllListeners;
38440 delete plotObj.emit;
38441
38442 delete plotObj._ev;
38443 delete plotObj._internalEv;
38444 delete plotObj._internalOn;
38445 delete plotObj._internalOnce;
38446 delete plotObj._removeInternalListener;
38447 delete plotObj._removeAllInternalListeners;
38448
38449 return plotObj;
38450 }
38451
38452};
38453
38454module.exports = Events;
38455
38456},{"events":15}],173:[function(_dereq_,module,exports){
38457/**
38458* Copyright 2012-2020, Plotly, Inc.
38459* All rights reserved.
38460*
38461* This source code is licensed under the MIT license found in the
38462* LICENSE file in the root directory of this source tree.
38463*/
38464
38465
38466'use strict';
38467
38468var isPlainObject = _dereq_('./is_plain_object.js');
38469var isArray = Array.isArray;
38470
38471function primitivesLoopSplice(source, target) {
38472 var i, value;
38473 for(i = 0; i < source.length; i++) {
38474 value = source[i];
38475 if(value !== null && typeof(value) === 'object') {
38476 return false;
38477 }
38478 if(value !== void(0)) {
38479 target[i] = value;
38480 }
38481 }
38482 return true;
38483}
38484
38485exports.extendFlat = function() {
38486 return _extend(arguments, false, false, false);
38487};
38488
38489exports.extendDeep = function() {
38490 return _extend(arguments, true, false, false);
38491};
38492
38493exports.extendDeepAll = function() {
38494 return _extend(arguments, true, true, false);
38495};
38496
38497exports.extendDeepNoArrays = function() {
38498 return _extend(arguments, true, false, true);
38499};
38500
38501/*
38502 * Inspired by https://github.com/justmoon/node-extend/blob/master/index.js
38503 * All credit to the jQuery authors for perfecting this amazing utility.
38504 *
38505 * API difference with jQuery version:
38506 * - No optional boolean (true -> deep extend) first argument,
38507 * use `extendFlat` for first-level only extend and
38508 * use `extendDeep` for a deep extend.
38509 *
38510 * Other differences with jQuery version:
38511 * - Uses a modern (and faster) isPlainObject routine.
38512 * - Expected to work with object {} and array [] arguments only.
38513 * - Does not check for circular structure.
38514 * FYI: jQuery only does a check across one level.
38515 * Warning: this might result in infinite loops.
38516 *
38517 */
38518function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) {
38519 var target = inputs[0];
38520 var length = inputs.length;
38521
38522 var input, key, src, copy, copyIsArray, clone, allPrimitives;
38523
38524 // TODO does this do the right thing for typed arrays?
38525
38526 if(length === 2 && isArray(target) && isArray(inputs[1]) && target.length === 0) {
38527 allPrimitives = primitivesLoopSplice(inputs[1], target);
38528
38529 if(allPrimitives) {
38530 return target;
38531 } else {
38532 target.splice(0, target.length); // reset target and continue to next block
38533 }
38534 }
38535
38536 for(var i = 1; i < length; i++) {
38537 input = inputs[i];
38538
38539 for(key in input) {
38540 src = target[key];
38541 copy = input[key];
38542
38543 if(noArrayCopies && isArray(copy)) {
38544 // Stop early and just transfer the array if array copies are disallowed:
38545
38546 target[key] = copy;
38547 } else if(isDeep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
38548 // recurse if we're merging plain objects or arrays
38549
38550 if(copyIsArray) {
38551 copyIsArray = false;
38552 clone = src && isArray(src) ? src : [];
38553 } else {
38554 clone = src && isPlainObject(src) ? src : {};
38555 }
38556
38557 // never move original objects, clone them
38558 target[key] = _extend([clone, copy], isDeep, keepAllKeys, noArrayCopies);
38559 } else if(typeof copy !== 'undefined' || keepAllKeys) {
38560 // don't bring in undefined values, except for extendDeepAll
38561
38562 target[key] = copy;
38563 }
38564 }
38565 }
38566
38567 return target;
38568}
38569
38570},{"./is_plain_object.js":179}],174:[function(_dereq_,module,exports){
38571/**
38572* Copyright 2012-2020, Plotly, Inc.
38573* All rights reserved.
38574*
38575* This source code is licensed under the MIT license found in the
38576* LICENSE file in the root directory of this source tree.
38577*/
38578
38579
38580'use strict';
38581
38582
38583/**
38584 * Return news array containing only the unique items
38585 * found in input array.
38586 *
38587 * IMPORTANT: Note that items are considered unique
38588 * if `String({})` is unique. For example;
38589 *
38590 * Lib.filterUnique([ { a: 1 }, { b: 2 } ])
38591 *
38592 * returns [{ a: 1 }]
38593 *
38594 * and
38595 *
38596 * Lib.filterUnique([ '1', 1 ])
38597 *
38598 * returns ['1']
38599 *
38600 *
38601 * @param {array} array base array
38602 * @return {array} new filtered array
38603 */
38604module.exports = function filterUnique(array) {
38605 var seen = {};
38606 var out = [];
38607 var j = 0;
38608
38609 for(var i = 0; i < array.length; i++) {
38610 var item = array[i];
38611
38612 if(seen[item] !== 1) {
38613 seen[item] = 1;
38614 out[j++] = item;
38615 }
38616 }
38617
38618 return out;
38619};
38620
38621},{}],175:[function(_dereq_,module,exports){
38622/**
38623* Copyright 2012-2020, Plotly, Inc.
38624* All rights reserved.
38625*
38626* This source code is licensed under the MIT license found in the
38627* LICENSE file in the root directory of this source tree.
38628*/
38629
38630'use strict';
38631
38632/** Filter out object items with visible !== true
38633 * insider array container.
38634 *
38635 * @param {array of objects} container
38636 * @return {array of objects} of length <= container
38637 *
38638 */
38639module.exports = function filterVisible(container) {
38640 var filterFn = isCalcData(container) ? calcDataFilter : baseFilter;
38641 var out = [];
38642
38643 for(var i = 0; i < container.length; i++) {
38644 var item = container[i];
38645 if(filterFn(item)) out.push(item);
38646 }
38647
38648 return out;
38649};
38650
38651function baseFilter(item) {
38652 return item.visible === true;
38653}
38654
38655function calcDataFilter(item) {
38656 var trace = item[0].trace;
38657 return trace.visible === true && trace._length !== 0;
38658}
38659
38660function isCalcData(cont) {
38661 return (
38662 Array.isArray(cont) &&
38663 Array.isArray(cont[0]) &&
38664 cont[0][0] &&
38665 cont[0][0].trace
38666 );
38667}
38668
38669},{}],176:[function(_dereq_,module,exports){
38670/**
38671* Copyright 2012-2020, Plotly, Inc.
38672* All rights reserved.
38673*
38674* This source code is licensed under the MIT license found in the
38675* LICENSE file in the root directory of this source tree.
38676*/
38677
38678'use strict';
38679
38680var mod = _dereq_('./mod').mod;
38681
38682/*
38683 * look for intersection of two line segments
38684 * (1->2 and 3->4) - returns array [x,y] if they do, null if not
38685 */
38686exports.segmentsIntersect = segmentsIntersect;
38687function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
38688 var a = x2 - x1;
38689 var b = x3 - x1;
38690 var c = x4 - x3;
38691 var d = y2 - y1;
38692 var e = y3 - y1;
38693 var f = y4 - y3;
38694 var det = a * f - c * d;
38695 // parallel lines? intersection is undefined
38696 // ignore the case where they are colinear
38697 if(det === 0) return null;
38698 var t = (b * f - c * e) / det;
38699 var u = (b * d - a * e) / det;
38700 // segments do not intersect?
38701 if(u < 0 || u > 1 || t < 0 || t > 1) return null;
38702
38703 return {x: x1 + a * t, y: y1 + d * t};
38704}
38705
38706/*
38707 * find the minimum distance between two line segments (1->2 and 3->4)
38708 */
38709exports.segmentDistance = function segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4) {
38710 if(segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return 0;
38711
38712 // the two segments and their lengths squared
38713 var x12 = x2 - x1;
38714 var y12 = y2 - y1;
38715 var x34 = x4 - x3;
38716 var y34 = y4 - y3;
38717 var ll12 = x12 * x12 + y12 * y12;
38718 var ll34 = x34 * x34 + y34 * y34;
38719
38720 // calculate distance squared, then take the sqrt at the very end
38721 var dist2 = Math.min(
38722 perpDistance2(x12, y12, ll12, x3 - x1, y3 - y1),
38723 perpDistance2(x12, y12, ll12, x4 - x1, y4 - y1),
38724 perpDistance2(x34, y34, ll34, x1 - x3, y1 - y3),
38725 perpDistance2(x34, y34, ll34, x2 - x3, y2 - y3)
38726 );
38727
38728 return Math.sqrt(dist2);
38729};
38730
38731/*
38732 * distance squared from segment ab to point c
38733 * [xab, yab] is the vector b-a
38734 * [xac, yac] is the vector c-a
38735 * llab is the length squared of (b-a), just to simplify calculation
38736 */
38737function perpDistance2(xab, yab, llab, xac, yac) {
38738 var fcAB = (xac * xab + yac * yab);
38739 if(fcAB < 0) {
38740 // point c is closer to point a
38741 return xac * xac + yac * yac;
38742 } else if(fcAB > llab) {
38743 // point c is closer to point b
38744 var xbc = xac - xab;
38745 var ybc = yac - yab;
38746 return xbc * xbc + ybc * ybc;
38747 } else {
38748 // perpendicular distance is the shortest
38749 var crossProduct = xac * yab - yac * xab;
38750 return crossProduct * crossProduct / llab;
38751 }
38752}
38753
38754// a very short-term cache for getTextLocation, just because
38755// we're often looping over the same locations multiple times
38756// invalidated as soon as we look at a different path
38757var locationCache, workingPath, workingTextWidth;
38758
38759// turn a path and position along it into x, y, and angle for the given text
38760exports.getTextLocation = function getTextLocation(path, totalPathLen, positionOnPath, textWidth) {
38761 if(path !== workingPath || textWidth !== workingTextWidth) {
38762 locationCache = {};
38763 workingPath = path;
38764 workingTextWidth = textWidth;
38765 }
38766 if(locationCache[positionOnPath]) {
38767 return locationCache[positionOnPath];
38768 }
38769
38770 // for the angle, use points on the path separated by the text width
38771 // even though due to curvature, the text will cover a bit more than that
38772 var p0 = path.getPointAtLength(mod(positionOnPath - textWidth / 2, totalPathLen));
38773 var p1 = path.getPointAtLength(mod(positionOnPath + textWidth / 2, totalPathLen));
38774 // note: atan handles 1/0 nicely
38775 var theta = Math.atan((p1.y - p0.y) / (p1.x - p0.x));
38776 // center the text at 2/3 of the center position plus 1/3 the p0/p1 midpoint
38777 // that's the average position of this segment, assuming it's roughly quadratic
38778 var pCenter = path.getPointAtLength(mod(positionOnPath, totalPathLen));
38779 var x = (pCenter.x * 4 + p0.x + p1.x) / 6;
38780 var y = (pCenter.y * 4 + p0.y + p1.y) / 6;
38781
38782 var out = {x: x, y: y, theta: theta};
38783 locationCache[positionOnPath] = out;
38784 return out;
38785};
38786
38787exports.clearLocationCache = function() {
38788 workingPath = null;
38789};
38790
38791/*
38792 * Find the segment of `path` that's within the visible area
38793 * given by `bounds` {left, right, top, bottom}, to within a
38794 * precision of `buffer` px
38795 *
38796 * returns: undefined if nothing is visible, else object:
38797 * {
38798 * min: position where the path first enters bounds, or 0 if it
38799 * starts within bounds
38800 * max: position where the path last exits bounds, or the path length
38801 * if it finishes within bounds
38802 * len: max - min, ie the length of visible path
38803 * total: the total path length - just included so the caller doesn't
38804 * need to call path.getTotalLength() again
38805 * isClosed: true iff the start and end points of the path are both visible
38806 * and are at the same point
38807 * }
38808 *
38809 * Works by starting from either end and repeatedly finding the distance from
38810 * that point to the plot area, and if it's outside the plot, moving along the
38811 * path by that distance (because the plot must be at least that far away on
38812 * the path). Note that if a path enters, exits, and re-enters the plot, we
38813 * will not capture this behavior.
38814 */
38815exports.getVisibleSegment = function getVisibleSegment(path, bounds, buffer) {
38816 var left = bounds.left;
38817 var right = bounds.right;
38818 var top = bounds.top;
38819 var bottom = bounds.bottom;
38820
38821 var pMin = 0;
38822 var pTotal = path.getTotalLength();
38823 var pMax = pTotal;
38824
38825 var pt0, ptTotal;
38826
38827 function getDistToPlot(len) {
38828 var pt = path.getPointAtLength(len);
38829
38830 // hold on to the start and end points for `closed`
38831 if(len === 0) pt0 = pt;
38832 else if(len === pTotal) ptTotal = pt;
38833
38834 var dx = (pt.x < left) ? left - pt.x : (pt.x > right ? pt.x - right : 0);
38835 var dy = (pt.y < top) ? top - pt.y : (pt.y > bottom ? pt.y - bottom : 0);
38836 return Math.sqrt(dx * dx + dy * dy);
38837 }
38838
38839 var distToPlot = getDistToPlot(pMin);
38840 while(distToPlot) {
38841 pMin += distToPlot + buffer;
38842 if(pMin > pMax) return;
38843 distToPlot = getDistToPlot(pMin);
38844 }
38845
38846 distToPlot = getDistToPlot(pMax);
38847 while(distToPlot) {
38848 pMax -= distToPlot + buffer;
38849 if(pMin > pMax) return;
38850 distToPlot = getDistToPlot(pMax);
38851 }
38852
38853 return {
38854 min: pMin,
38855 max: pMax,
38856 len: pMax - pMin,
38857 total: pTotal,
38858 isClosed: pMin === 0 && pMax === pTotal &&
38859 Math.abs(pt0.x - ptTotal.x) < 0.1 &&
38860 Math.abs(pt0.y - ptTotal.y) < 0.1
38861 };
38862};
38863
38864/**
38865 * Find point on SVG path corresponding to a given constraint coordinate
38866 *
38867 * @param {SVGPathElement} path
38868 * @param {Number} val : constraint coordinate value
38869 * @param {String} coord : 'x' or 'y' the constraint coordinate
38870 * @param {Object} opts :
38871 * - {Number} pathLength : supply total path length before hand
38872 * - {Number} tolerance
38873 * - {Number} iterationLimit
38874 * @return {SVGPoint}
38875 */
38876exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) {
38877 opts = opts || {};
38878
38879 var pathLength = opts.pathLength || path.getTotalLength();
38880 var tolerance = opts.tolerance || 1e-3;
38881 var iterationLimit = opts.iterationLimit || 30;
38882
38883 // if path starts at a val greater than the path tail (like on vertical violins),
38884 // we must flip the sign of the computed diff.
38885 var mul = path.getPointAtLength(0)[coord] > path.getPointAtLength(pathLength)[coord] ? -1 : 1;
38886
38887 var i = 0;
38888 var b0 = 0;
38889 var b1 = pathLength;
38890 var mid;
38891 var pt;
38892 var diff;
38893
38894 while(i < iterationLimit) {
38895 mid = (b0 + b1) / 2;
38896 pt = path.getPointAtLength(mid);
38897 diff = pt[coord] - val;
38898
38899 if(Math.abs(diff) < tolerance) {
38900 return pt;
38901 } else {
38902 if(mul * diff > 0) {
38903 b1 = mid;
38904 } else {
38905 b0 = mid;
38906 }
38907 i++;
38908 }
38909 }
38910 return pt;
38911};
38912
38913},{"./mod":185}],177:[function(_dereq_,module,exports){
38914/**
38915* Copyright 2012-2020, Plotly, Inc.
38916* All rights reserved.
38917*
38918* This source code is licensed under the MIT license found in the
38919* LICENSE file in the root directory of this source tree.
38920*/
38921
38922'use strict';
38923
38924// Simple helper functions
38925// none of these need any external deps
38926
38927module.exports = function identity(d) { return d; };
38928
38929},{}],178:[function(_dereq_,module,exports){
38930/**
38931* Copyright 2012-2020, Plotly, Inc.
38932* All rights reserved.
38933*
38934* This source code is licensed under the MIT license found in the
38935* LICENSE file in the root directory of this source tree.
38936*/
38937
38938'use strict';
38939
38940var d3 = _dereq_('d3');
38941var isNumeric = _dereq_('fast-isnumeric');
38942
38943var numConstants = _dereq_('../constants/numerical');
38944var FP_SAFE = numConstants.FP_SAFE;
38945var BADNUM = numConstants.BADNUM;
38946
38947var lib = module.exports = {};
38948
38949lib.nestedProperty = _dereq_('./nested_property');
38950lib.keyedContainer = _dereq_('./keyed_container');
38951lib.relativeAttr = _dereq_('./relative_attr');
38952lib.isPlainObject = _dereq_('./is_plain_object');
38953lib.toLogRange = _dereq_('./to_log_range');
38954lib.relinkPrivateKeys = _dereq_('./relink_private');
38955
38956var arrayModule = _dereq_('./array');
38957lib.isTypedArray = arrayModule.isTypedArray;
38958lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray;
38959lib.isArray1D = arrayModule.isArray1D;
38960lib.ensureArray = arrayModule.ensureArray;
38961lib.concat = arrayModule.concat;
38962lib.maxRowLength = arrayModule.maxRowLength;
38963lib.minRowLength = arrayModule.minRowLength;
38964
38965var modModule = _dereq_('./mod');
38966lib.mod = modModule.mod;
38967lib.modHalf = modModule.modHalf;
38968
38969var coerceModule = _dereq_('./coerce');
38970lib.valObjectMeta = coerceModule.valObjectMeta;
38971lib.coerce = coerceModule.coerce;
38972lib.coerce2 = coerceModule.coerce2;
38973lib.coerceFont = coerceModule.coerceFont;
38974lib.coerceHoverinfo = coerceModule.coerceHoverinfo;
38975lib.coerceSelectionMarkerOpacity = coerceModule.coerceSelectionMarkerOpacity;
38976lib.validate = coerceModule.validate;
38977
38978var datesModule = _dereq_('./dates');
38979lib.dateTime2ms = datesModule.dateTime2ms;
38980lib.isDateTime = datesModule.isDateTime;
38981lib.ms2DateTime = datesModule.ms2DateTime;
38982lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal;
38983lib.cleanDate = datesModule.cleanDate;
38984lib.isJSDate = datesModule.isJSDate;
38985lib.formatDate = datesModule.formatDate;
38986lib.incrementMonth = datesModule.incrementMonth;
38987lib.dateTick0 = datesModule.dateTick0;
38988lib.dfltRange = datesModule.dfltRange;
38989lib.findExactDates = datesModule.findExactDates;
38990lib.MIN_MS = datesModule.MIN_MS;
38991lib.MAX_MS = datesModule.MAX_MS;
38992
38993var searchModule = _dereq_('./search');
38994lib.findBin = searchModule.findBin;
38995lib.sorterAsc = searchModule.sorterAsc;
38996lib.sorterDes = searchModule.sorterDes;
38997lib.distinctVals = searchModule.distinctVals;
38998lib.roundUp = searchModule.roundUp;
38999lib.sort = searchModule.sort;
39000lib.findIndexOfMin = searchModule.findIndexOfMin;
39001
39002var statsModule = _dereq_('./stats');
39003lib.aggNums = statsModule.aggNums;
39004lib.len = statsModule.len;
39005lib.mean = statsModule.mean;
39006lib.median = statsModule.median;
39007lib.midRange = statsModule.midRange;
39008lib.variance = statsModule.variance;
39009lib.stdev = statsModule.stdev;
39010lib.interp = statsModule.interp;
39011
39012var matrixModule = _dereq_('./matrix');
39013lib.init2dArray = matrixModule.init2dArray;
39014lib.transposeRagged = matrixModule.transposeRagged;
39015lib.dot = matrixModule.dot;
39016lib.translationMatrix = matrixModule.translationMatrix;
39017lib.rotationMatrix = matrixModule.rotationMatrix;
39018lib.rotationXYMatrix = matrixModule.rotationXYMatrix;
39019lib.apply2DTransform = matrixModule.apply2DTransform;
39020lib.apply2DTransform2 = matrixModule.apply2DTransform2;
39021
39022var anglesModule = _dereq_('./angles');
39023lib.deg2rad = anglesModule.deg2rad;
39024lib.rad2deg = anglesModule.rad2deg;
39025lib.angleDelta = anglesModule.angleDelta;
39026lib.angleDist = anglesModule.angleDist;
39027lib.isFullCircle = anglesModule.isFullCircle;
39028lib.isAngleInsideSector = anglesModule.isAngleInsideSector;
39029lib.isPtInsideSector = anglesModule.isPtInsideSector;
39030lib.pathArc = anglesModule.pathArc;
39031lib.pathSector = anglesModule.pathSector;
39032lib.pathAnnulus = anglesModule.pathAnnulus;
39033
39034var anchorUtils = _dereq_('./anchor_utils');
39035lib.isLeftAnchor = anchorUtils.isLeftAnchor;
39036lib.isCenterAnchor = anchorUtils.isCenterAnchor;
39037lib.isRightAnchor = anchorUtils.isRightAnchor;
39038lib.isTopAnchor = anchorUtils.isTopAnchor;
39039lib.isMiddleAnchor = anchorUtils.isMiddleAnchor;
39040lib.isBottomAnchor = anchorUtils.isBottomAnchor;
39041
39042var geom2dModule = _dereq_('./geometry2d');
39043lib.segmentsIntersect = geom2dModule.segmentsIntersect;
39044lib.segmentDistance = geom2dModule.segmentDistance;
39045lib.getTextLocation = geom2dModule.getTextLocation;
39046lib.clearLocationCache = geom2dModule.clearLocationCache;
39047lib.getVisibleSegment = geom2dModule.getVisibleSegment;
39048lib.findPointOnPath = geom2dModule.findPointOnPath;
39049
39050var extendModule = _dereq_('./extend');
39051lib.extendFlat = extendModule.extendFlat;
39052lib.extendDeep = extendModule.extendDeep;
39053lib.extendDeepAll = extendModule.extendDeepAll;
39054lib.extendDeepNoArrays = extendModule.extendDeepNoArrays;
39055
39056var loggersModule = _dereq_('./loggers');
39057lib.log = loggersModule.log;
39058lib.warn = loggersModule.warn;
39059lib.error = loggersModule.error;
39060
39061var regexModule = _dereq_('./regex');
39062lib.counterRegex = regexModule.counter;
39063
39064var throttleModule = _dereq_('./throttle');
39065lib.throttle = throttleModule.throttle;
39066lib.throttleDone = throttleModule.done;
39067lib.clearThrottle = throttleModule.clear;
39068
39069var domModule = _dereq_('./dom');
39070lib.getGraphDiv = domModule.getGraphDiv;
39071lib.isPlotDiv = domModule.isPlotDiv;
39072lib.removeElement = domModule.removeElement;
39073lib.addStyleRule = domModule.addStyleRule;
39074lib.addRelatedStyleRule = domModule.addRelatedStyleRule;
39075lib.deleteRelatedStyleRule = domModule.deleteRelatedStyleRule;
39076
39077lib.clearResponsive = _dereq_('./clear_responsive');
39078
39079lib.makeTraceGroups = _dereq_('./make_trace_groups');
39080
39081lib._ = _dereq_('./localize');
39082
39083lib.notifier = _dereq_('./notifier');
39084
39085lib.filterUnique = _dereq_('./filter_unique');
39086lib.filterVisible = _dereq_('./filter_visible');
39087lib.pushUnique = _dereq_('./push_unique');
39088
39089lib.cleanNumber = _dereq_('./clean_number');
39090
39091lib.ensureNumber = function ensureNumber(v) {
39092 if(!isNumeric(v)) return BADNUM;
39093 v = Number(v);
39094 if(v < -FP_SAFE || v > FP_SAFE) return BADNUM;
39095 return isNumeric(v) ? Number(v) : BADNUM;
39096};
39097
39098/**
39099 * Is v a valid array index? Accepts numeric strings as well as numbers.
39100 *
39101 * @param {any} v: the value to test
39102 * @param {Optional[integer]} len: the array length we are indexing
39103 *
39104 * @return {bool}: v is a valid array index
39105 */
39106lib.isIndex = function(v, len) {
39107 if(len !== undefined && v >= len) return false;
39108 return isNumeric(v) && (v >= 0) && (v % 1 === 0);
39109};
39110
39111lib.noop = _dereq_('./noop');
39112lib.identity = _dereq_('./identity');
39113
39114/**
39115 * create an array of length 'cnt' filled with 'v' at all indices
39116 *
39117 * @param {any} v
39118 * @param {number} cnt
39119 * @return {array}
39120 */
39121lib.repeat = function(v, cnt) {
39122 var out = new Array(cnt);
39123 for(var i = 0; i < cnt; i++) {
39124 out[i] = v;
39125 }
39126 return out;
39127};
39128
39129/**
39130 * swap x and y of the same attribute in container cont
39131 * specify attr with a ? in place of x/y
39132 * you can also swap other things than x/y by providing part1 and part2
39133 */
39134lib.swapAttrs = function(cont, attrList, part1, part2) {
39135 if(!part1) part1 = 'x';
39136 if(!part2) part2 = 'y';
39137 for(var i = 0; i < attrList.length; i++) {
39138 var attr = attrList[i];
39139 var xp = lib.nestedProperty(cont, attr.replace('?', part1));
39140 var yp = lib.nestedProperty(cont, attr.replace('?', part2));
39141 var temp = xp.get();
39142 xp.set(yp.get());
39143 yp.set(temp);
39144 }
39145};
39146
39147/**
39148 * SVG painter's algo worked around with reinsertion
39149 */
39150lib.raiseToTop = function raiseToTop(elem) {
39151 elem.parentNode.appendChild(elem);
39152};
39153
39154/**
39155 * cancel a possibly pending transition; returned selection may be used by caller
39156 */
39157lib.cancelTransition = function(selection) {
39158 return selection.transition().duration(0);
39159};
39160
39161// constrain - restrict a number v to be between v0 and v1
39162lib.constrain = function(v, v0, v1) {
39163 if(v0 > v1) return Math.max(v1, Math.min(v0, v));
39164 return Math.max(v0, Math.min(v1, v));
39165};
39166
39167/**
39168 * do two bounding boxes from getBoundingClientRect,
39169 * ie {left,right,top,bottom,width,height}, overlap?
39170 * takes optional padding pixels
39171 */
39172lib.bBoxIntersect = function(a, b, pad) {
39173 pad = pad || 0;
39174 return (a.left <= b.right + pad &&
39175 b.left <= a.right + pad &&
39176 a.top <= b.bottom + pad &&
39177 b.top <= a.bottom + pad);
39178};
39179
39180/*
39181 * simpleMap: alternative to Array.map that only
39182 * passes on the element and up to 2 extra args you
39183 * provide (but not the array index or the whole array)
39184 *
39185 * array: the array to map it to
39186 * func: the function to apply
39187 * x1, x2: optional extra args
39188 */
39189lib.simpleMap = function(array, func, x1, x2) {
39190 var len = array.length;
39191 var out = new Array(len);
39192 for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2);
39193 return out;
39194};
39195
39196/**
39197 * Random string generator
39198 *
39199 * @param {object} existing
39200 * pass in strings to avoid as keys with truthy values
39201 * @param {int} bits
39202 * bits of information in the output string, default 24
39203 * @param {int} base
39204 * base of string representation, default 16. Should be a power of 2.
39205 */
39206lib.randstr = function randstr(existing, bits, base, _recursion) {
39207 if(!base) base = 16;
39208 if(bits === undefined) bits = 24;
39209 if(bits <= 0) return '0';
39210
39211 var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
39212 var res = '';
39213 var i, b, x;
39214
39215 for(i = 2; digits === Infinity; i *= 2) {
39216 digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
39217 }
39218
39219 var rem = digits - Math.floor(digits);
39220
39221 for(i = 0; i < Math.floor(digits); i++) {
39222 x = Math.floor(Math.random() * base).toString(base);
39223 res = x + res;
39224 }
39225
39226 if(rem) {
39227 b = Math.pow(base, rem);
39228 x = Math.floor(Math.random() * b).toString(base);
39229 res = x + res;
39230 }
39231
39232 var parsed = parseInt(res, base);
39233 if((existing && existing[res]) ||
39234 (parsed !== Infinity && parsed >= Math.pow(2, bits))) {
39235 if(_recursion > 10) {
39236 lib.warn('randstr failed uniqueness');
39237 return res;
39238 }
39239 return randstr(existing, bits, base, (_recursion || 0) + 1);
39240 } else return res;
39241};
39242
39243lib.OptionControl = function(opt, optname) {
39244 /*
39245 * An environment to contain all option setters and
39246 * getters that collectively modify opts.
39247 *
39248 * You can call up opts from any function in new object
39249 * as this.optname || this.opt
39250 *
39251 * See FitOpts for example of usage
39252 */
39253 if(!opt) opt = {};
39254 if(!optname) optname = 'opt';
39255
39256 var self = {};
39257 self.optionList = [];
39258
39259 self._newoption = function(optObj) {
39260 optObj[optname] = opt;
39261 self[optObj.name] = optObj;
39262 self.optionList.push(optObj);
39263 };
39264
39265 self['_' + optname] = opt;
39266 return self;
39267};
39268
39269/**
39270 * lib.smooth: smooth arrayIn by convolving with
39271 * a hann window with given full width at half max
39272 * bounce the ends in, so the output has the same length as the input
39273 */
39274lib.smooth = function(arrayIn, FWHM) {
39275 FWHM = Math.round(FWHM) || 0; // only makes sense for integers
39276 if(FWHM < 2) return arrayIn;
39277
39278 var alen = arrayIn.length;
39279 var alen2 = 2 * alen;
39280 var wlen = 2 * FWHM - 1;
39281 var w = new Array(wlen);
39282 var arrayOut = new Array(alen);
39283 var i;
39284 var j;
39285 var k;
39286 var v;
39287
39288 // first make the window array
39289 for(i = 0; i < wlen; i++) {
39290 w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM);
39291 }
39292
39293 // now do the convolution
39294 for(i = 0; i < alen; i++) {
39295 v = 0;
39296 for(j = 0; j < wlen; j++) {
39297 k = i + j + 1 - FWHM;
39298
39299 // multibounce
39300 if(k < -alen) k -= alen2 * Math.round(k / alen2);
39301 else if(k >= alen2) k -= alen2 * Math.floor(k / alen2);
39302
39303 // single bounce
39304 if(k < 0) k = - 1 - k;
39305 else if(k >= alen) k = alen2 - 1 - k;
39306
39307 v += arrayIn[k] * w[j];
39308 }
39309 arrayOut[i] = v;
39310 }
39311
39312 return arrayOut;
39313};
39314
39315/**
39316 * syncOrAsync: run a sequence of functions synchronously
39317 * as long as its returns are not promises (ie have no .then)
39318 * includes one argument arg to send to all functions...
39319 * this is mainly just to prevent us having to make wrapper functions
39320 * when the only purpose of the wrapper is to reference gd
39321 * and a final step to be executed at the end
39322 * TODO: if there's an error and everything is sync,
39323 * this doesn't happen yet because we want to make sure
39324 * that it gets reported
39325 */
39326lib.syncOrAsync = function(sequence, arg, finalStep) {
39327 var ret, fni;
39328
39329 function continueAsync() {
39330 return lib.syncOrAsync(sequence, arg, finalStep);
39331 }
39332
39333 while(sequence.length) {
39334 fni = sequence.splice(0, 1)[0];
39335 ret = fni(arg);
39336
39337 if(ret && ret.then) {
39338 return ret.then(continueAsync)
39339 .then(undefined, lib.promiseError);
39340 }
39341 }
39342
39343 return finalStep && finalStep(arg);
39344};
39345
39346
39347/**
39348 * Helper to strip trailing slash, from
39349 * http://stackoverflow.com/questions/6680825/return-string-without-trailing-slash
39350 */
39351lib.stripTrailingSlash = function(str) {
39352 if(str.substr(-1) === '/') return str.substr(0, str.length - 1);
39353 return str;
39354};
39355
39356lib.noneOrAll = function(containerIn, containerOut, attrList) {
39357 /**
39358 * some attributes come together, so if you have one of them
39359 * in the input, you should copy the default values of the others
39360 * to the input as well.
39361 */
39362 if(!containerIn) return;
39363
39364 var hasAny = false;
39365 var hasAll = true;
39366 var i;
39367 var val;
39368
39369 for(i = 0; i < attrList.length; i++) {
39370 val = containerIn[attrList[i]];
39371 if(val !== undefined && val !== null) hasAny = true;
39372 else hasAll = false;
39373 }
39374
39375 if(hasAny && !hasAll) {
39376 for(i = 0; i < attrList.length; i++) {
39377 containerIn[attrList[i]] = containerOut[attrList[i]];
39378 }
39379 }
39380};
39381
39382/** merges calcdata field (given by cdAttr) with traceAttr values
39383 *
39384 * N.B. Loop over minimum of cd.length and traceAttr.length
39385 * i.e. it does not try to fill in beyond traceAttr.length-1
39386 *
39387 * @param {array} traceAttr : trace attribute
39388 * @param {object} cd : calcdata trace
39389 * @param {string} cdAttr : calcdata key
39390 */
39391lib.mergeArray = function(traceAttr, cd, cdAttr, fn) {
39392 var hasFn = typeof fn === 'function';
39393 if(lib.isArrayOrTypedArray(traceAttr)) {
39394 var imax = Math.min(traceAttr.length, cd.length);
39395 for(var i = 0; i < imax; i++) {
39396 var v = traceAttr[i];
39397 cd[i][cdAttr] = hasFn ? fn(v) : v;
39398 }
39399 }
39400};
39401
39402// cast numbers to positive numbers, returns 0 if not greater than 0
39403lib.mergeArrayCastPositive = function(traceAttr, cd, cdAttr) {
39404 return lib.mergeArray(traceAttr, cd, cdAttr, function(v) {
39405 var w = +v;
39406 return !isFinite(w) ? 0 : w > 0 ? w : 0;
39407 });
39408};
39409
39410/** fills calcdata field (given by cdAttr) with traceAttr values
39411 * or function of traceAttr values (e.g. some fallback)
39412 *
39413 * N.B. Loops over all cd items.
39414 *
39415 * @param {array} traceAttr : trace attribute
39416 * @param {object} cd : calcdata trace
39417 * @param {string} cdAttr : calcdata key
39418 * @param {function} [fn] : optional function to apply to each array item
39419 */
39420lib.fillArray = function(traceAttr, cd, cdAttr, fn) {
39421 fn = fn || lib.identity;
39422
39423 if(lib.isArrayOrTypedArray(traceAttr)) {
39424 for(var i = 0; i < cd.length; i++) {
39425 cd[i][cdAttr] = fn(traceAttr[i]);
39426 }
39427 }
39428};
39429
39430/** Handler for trace-wide vs per-point options
39431 *
39432 * @param {object} trace : (full) trace object
39433 * @param {number} ptNumber : index of the point in question
39434 * @param {string} astr : attribute string
39435 * @param {function} [fn] : optional function to apply to each array item
39436 *
39437 * @return {any}
39438 */
39439lib.castOption = function(trace, ptNumber, astr, fn) {
39440 fn = fn || lib.identity;
39441
39442 var val = lib.nestedProperty(trace, astr).get();
39443
39444 if(lib.isArrayOrTypedArray(val)) {
39445 if(Array.isArray(ptNumber) && lib.isArrayOrTypedArray(val[ptNumber[0]])) {
39446 return fn(val[ptNumber[0]][ptNumber[1]]);
39447 } else {
39448 return fn(val[ptNumber]);
39449 }
39450 } else {
39451 return val;
39452 }
39453};
39454
39455/** Extract option from calcdata item, correctly falling back to
39456 * trace value if not found.
39457 *
39458 * @param {object} calcPt : calcdata[i][j] item
39459 * @param {object} trace : (full) trace object
39460 * @param {string} calcKey : calcdata key
39461 * @param {string} traceKey : aka trace attribute string
39462 * @return {any}
39463 */
39464lib.extractOption = function(calcPt, trace, calcKey, traceKey) {
39465 if(calcKey in calcPt) return calcPt[calcKey];
39466
39467 // fallback to trace value,
39468 // must check if value isn't itself an array
39469 // which means the trace attribute has a corresponding
39470 // calcdata key, but its value is falsy
39471 var traceVal = lib.nestedProperty(trace, traceKey).get();
39472 if(!Array.isArray(traceVal)) return traceVal;
39473};
39474
39475function makePtIndex2PtNumber(indexToPoints) {
39476 var ptIndex2ptNumber = {};
39477 for(var k in indexToPoints) {
39478 var pts = indexToPoints[k];
39479 for(var j = 0; j < pts.length; j++) {
39480 ptIndex2ptNumber[pts[j]] = +k;
39481 }
39482 }
39483 return ptIndex2ptNumber;
39484}
39485
39486/** Tag selected calcdata items
39487 *
39488 * N.B. note that point 'index' corresponds to input data array index
39489 * whereas 'number' is its post-transform version.
39490 *
39491 * @param {array} calcTrace
39492 * @param {object} trace
39493 * - selectedpoints {array}
39494 * - _indexToPoints {object}
39495 * @param {ptNumber2cdIndex} ptNumber2cdIndex (optional)
39496 * optional map object for trace types that do not have 1-to-1 point number to
39497 * calcdata item index correspondence (e.g. histogram)
39498 */
39499lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) {
39500 var selectedpoints = trace.selectedpoints;
39501 var indexToPoints = trace._indexToPoints;
39502 var ptIndex2ptNumber;
39503
39504 // make pt index-to-number map object, which takes care of transformed traces
39505 if(indexToPoints) {
39506 ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
39507 }
39508
39509 function isCdIndexValid(v) {
39510 return v !== undefined && v < calcTrace.length;
39511 }
39512
39513 for(var i = 0; i < selectedpoints.length; i++) {
39514 var ptIndex = selectedpoints[i];
39515
39516 if(lib.isIndex(ptIndex) ||
39517 (lib.isArrayOrTypedArray(ptIndex) && lib.isIndex(ptIndex[0]) && lib.isIndex(ptIndex[1]))
39518 ) {
39519 var ptNumber = ptIndex2ptNumber ? ptIndex2ptNumber[ptIndex] : ptIndex;
39520 var cdIndex = ptNumber2cdIndex ? ptNumber2cdIndex[ptNumber] : ptNumber;
39521
39522 if(isCdIndexValid(cdIndex)) {
39523 calcTrace[cdIndex].selected = 1;
39524 }
39525 }
39526 }
39527};
39528
39529lib.selIndices2selPoints = function(trace) {
39530 var selectedpoints = trace.selectedpoints;
39531 var indexToPoints = trace._indexToPoints;
39532
39533 if(indexToPoints) {
39534 var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
39535 var out = [];
39536
39537 for(var i = 0; i < selectedpoints.length; i++) {
39538 var ptIndex = selectedpoints[i];
39539 if(lib.isIndex(ptIndex)) {
39540 var ptNumber = ptIndex2ptNumber[ptIndex];
39541 if(lib.isIndex(ptNumber)) {
39542 out.push(ptNumber);
39543 }
39544 }
39545 }
39546
39547 return out;
39548 } else {
39549 return selectedpoints;
39550 }
39551};
39552
39553/** Returns target as set by 'target' transform attribute
39554 *
39555 * @param {object} trace : full trace object
39556 * @param {object} transformOpts : transform option object
39557 * - target (string} :
39558 * either an attribute string referencing an array in the trace object, or
39559 * a set array.
39560 *
39561 * @return {array or false} : the target array (NOT a copy!!) or false if invalid
39562 */
39563lib.getTargetArray = function(trace, transformOpts) {
39564 var target = transformOpts.target;
39565
39566 if(typeof target === 'string' && target) {
39567 var array = lib.nestedProperty(trace, target).get();
39568 return Array.isArray(array) ? array : false;
39569 } else if(Array.isArray(target)) {
39570 return target;
39571 }
39572
39573 return false;
39574};
39575
39576/**
39577 * modified version of jQuery's extend to strip out private objs and functions,
39578 * and cut arrays down to first <arraylen> or 1 elements
39579 * because extend-like algorithms are hella slow
39580 * obj2 is assumed to already be clean of these things (including no arrays)
39581 */
39582lib.minExtend = function(obj1, obj2) {
39583 var objOut = {};
39584 if(typeof obj2 !== 'object') obj2 = {};
39585 var arrayLen = 3;
39586 var keys = Object.keys(obj1);
39587 var i, k, v;
39588
39589 for(i = 0; i < keys.length; i++) {
39590 k = keys[i];
39591 v = obj1[k];
39592 if(k.charAt(0) === '_' || typeof v === 'function') continue;
39593 else if(k === 'module') objOut[k] = v;
39594 else if(Array.isArray(v)) {
39595 if(k === 'colorscale') {
39596 objOut[k] = v.slice();
39597 } else {
39598 objOut[k] = v.slice(0, arrayLen);
39599 }
39600 } else if(lib.isTypedArray(v)) {
39601 objOut[k] = v.subarray(0, arrayLen);
39602 } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
39603 else objOut[k] = v;
39604 }
39605
39606 keys = Object.keys(obj2);
39607 for(i = 0; i < keys.length; i++) {
39608 k = keys[i];
39609 v = obj2[k];
39610 if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') {
39611 objOut[k] = v;
39612 }
39613 }
39614
39615 return objOut;
39616};
39617
39618lib.titleCase = function(s) {
39619 return s.charAt(0).toUpperCase() + s.substr(1);
39620};
39621
39622lib.containsAny = function(s, fragments) {
39623 for(var i = 0; i < fragments.length; i++) {
39624 if(s.indexOf(fragments[i]) !== -1) return true;
39625 }
39626 return false;
39627};
39628
39629lib.isIE = function() {
39630 return typeof window.navigator.msSaveBlob !== 'undefined';
39631};
39632
39633var IS_IE9_OR_BELOW_REGEX = /MSIE [1-9]\./;
39634lib.isIE9orBelow = function() {
39635 return lib.isIE() && IS_IE9_OR_BELOW_REGEX.test(window.navigator.userAgent);
39636};
39637
39638var IS_SAFARI_REGEX = /Version\/[\d\.]+.*Safari/;
39639lib.isSafari = function() {
39640 return IS_SAFARI_REGEX.test(window.navigator.userAgent);
39641};
39642
39643/**
39644 * Duck typing to recognize a d3 selection, mostly for IE9's benefit
39645 * because it doesn't handle instanceof like modern browsers
39646 */
39647lib.isD3Selection = function(obj) {
39648 return obj && (typeof obj.classed === 'function');
39649};
39650
39651/**
39652 * Append element to DOM only if not present.
39653 *
39654 * @param {d3 selection} parent : parent selection of the element in question
39655 * @param {string} nodeType : node type of element to append
39656 * @param {string} className (optional) : class name of element in question
39657 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
39658 * @return {d3 selection} selection of new layer
39659 *
39660 * Previously, we were using the following pattern:
39661 *
39662 * ```
39663 * var sel = parent.selectAll('.' + className)
39664 * .data([0]);
39665 *
39666 * sel.enter().append(nodeType)
39667 * .classed(className, true);
39668 *
39669 * return sel;
39670 * ```
39671 *
39672 * in numerous places in our codebase to achieve the same behavior.
39673 *
39674 * The logic below performs much better, mostly as we are using
39675 * `.select` instead `.selectAll` that is `querySelector` instead of
39676 * `querySelectorAll`.
39677 *
39678 */
39679lib.ensureSingle = function(parent, nodeType, className, enterFn) {
39680 var sel = parent.select(nodeType + (className ? '.' + className : ''));
39681 if(sel.size()) return sel;
39682
39683 var layer = parent.append(nodeType);
39684 if(className) layer.classed(className, true);
39685 if(enterFn) layer.call(enterFn);
39686
39687 return layer;
39688};
39689
39690/**
39691 * Same as Lib.ensureSingle, but using id as selector.
39692 * This version is mostly used for clipPath nodes.
39693 *
39694 * @param {d3 selection} parent : parent selection of the element in question
39695 * @param {string} nodeType : node type of element to append
39696 * @param {string} id : id of element in question
39697 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
39698 * @return {d3 selection} selection of new layer
39699 */
39700lib.ensureSingleById = function(parent, nodeType, id, enterFn) {
39701 var sel = parent.select(nodeType + '#' + id);
39702 if(sel.size()) return sel;
39703
39704 var layer = parent.append(nodeType).attr('id', id);
39705 if(enterFn) layer.call(enterFn);
39706
39707 return layer;
39708};
39709
39710/**
39711 * Converts a string path to an object.
39712 *
39713 * When given a string containing an array element, it will create a `null`
39714 * filled array of the given size.
39715 *
39716 * @example
39717 * lib.objectFromPath('nested.test[2].path', 'value');
39718 * // returns { nested: { test: [null, null, { path: 'value' }]}
39719 *
39720 * @param {string} path to nested value
39721 * @param {*} any value to be set
39722 *
39723 * @return {Object} the constructed object with a full nested path
39724 */
39725lib.objectFromPath = function(path, value) {
39726 var keys = path.split('.');
39727 var tmpObj;
39728 var obj = tmpObj = {};
39729
39730 for(var i = 0; i < keys.length; i++) {
39731 var key = keys[i];
39732 var el = null;
39733
39734 var parts = keys[i].match(/(.*)\[([0-9]+)\]/);
39735
39736 if(parts) {
39737 key = parts[1];
39738 el = parts[2];
39739
39740 tmpObj = tmpObj[key] = [];
39741
39742 if(i === keys.length - 1) {
39743 tmpObj[el] = value;
39744 } else {
39745 tmpObj[el] = {};
39746 }
39747
39748 tmpObj = tmpObj[el];
39749 } else {
39750 if(i === keys.length - 1) {
39751 tmpObj[key] = value;
39752 } else {
39753 tmpObj[key] = {};
39754 }
39755
39756 tmpObj = tmpObj[key];
39757 }
39758 }
39759
39760 return obj;
39761};
39762
39763/**
39764 * Iterate through an object in-place, converting dotted properties to objects.
39765 *
39766 * Examples:
39767 *
39768 * lib.expandObjectPaths({'nested.test.path': 'value'});
39769 * => { nested: { test: {path: 'value'}}}
39770 *
39771 * It also handles array notation, e.g.:
39772 *
39773 * lib.expandObjectPaths({'foo[1].bar': 'value'});
39774 * => { foo: [null, {bar: value}] }
39775 *
39776 * It handles merges the results when two properties are specified in parallel:
39777 *
39778 * lib.expandObjectPaths({'foo[1].bar': 10, 'foo[0].bar': 20});
39779 * => { foo: [{bar: 10}, {bar: 20}] }
39780 *
39781 * It does NOT, however, merge mulitple mutliply-nested arrays::
39782 *
39783 * lib.expandObjectPaths({'marker[1].range[1]': 5, 'marker[1].range[0]': 4})
39784 * => { marker: [null, {range: 4}] }
39785 */
39786
39787// Store this to avoid recompiling regex on *every* prop since this may happen many
39788// many times for animations. Could maybe be inside the function. Not sure about
39789// scoping vs. recompilation tradeoff, but at least it's not just inlining it into
39790// the inner loop.
39791var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/;
39792var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/;
39793
39794lib.expandObjectPaths = function(data) {
39795 var match, key, prop, datum, idx, dest, trailingPath;
39796 if(typeof data === 'object' && !Array.isArray(data)) {
39797 for(key in data) {
39798 if(data.hasOwnProperty(key)) {
39799 if((match = key.match(dottedPropertyRegex))) {
39800 datum = data[key];
39801 prop = match[1];
39802
39803 delete data[key];
39804
39805 data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[prop]);
39806 } else if((match = key.match(indexedPropertyRegex))) {
39807 datum = data[key];
39808
39809 prop = match[1];
39810 idx = parseInt(match[2]);
39811
39812 delete data[key];
39813
39814 data[prop] = data[prop] || [];
39815
39816 if(match[3] === '.') {
39817 // This is the case where theere are subsequent properties into which
39818 // we must recurse, e.g. transforms[0].value
39819 trailingPath = match[4];
39820 dest = data[prop][idx] = data[prop][idx] || {};
39821
39822 // NB: Extend deep no arrays prevents this from working on multiple
39823 // nested properties in the same object, e.g.
39824 //
39825 // {
39826 // foo[0].bar[1].range
39827 // foo[0].bar[0].range
39828 // }
39829 //
39830 // In this case, the extendDeepNoArrays will overwrite one array with
39831 // the other, so that both properties *will not* be present in the
39832 // result. Fixing this would require a more intelligent tracking
39833 // of changes and merging than extendDeepNoArrays currently accomplishes.
39834 lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum)));
39835 } else {
39836 // This is the case where this property is the end of the line,
39837 // e.g. xaxis.range[0]
39838 data[prop][idx] = lib.expandObjectPaths(datum);
39839 }
39840 } else {
39841 data[key] = lib.expandObjectPaths(data[key]);
39842 }
39843 }
39844 }
39845 }
39846
39847 return data;
39848};
39849
39850/**
39851 * Converts value to string separated by the provided separators.
39852 *
39853 * @example
39854 * lib.numSeparate(2016, '.,');
39855 * // returns '2016'
39856 *
39857 * @example
39858 * lib.numSeparate(3000, '.,', true);
39859 * // returns '3,000'
39860 *
39861 * @example
39862 * lib.numSeparate(1234.56, '|,')
39863 * // returns '1,234|56'
39864 *
39865 * @param {string|number} value the value to be converted
39866 * @param {string} separators string of decimal, then thousands separators
39867 * @param {boolean} separatethousands boolean, 4-digit integers are separated if true
39868 *
39869 * @return {string} the value that has been separated
39870 */
39871lib.numSeparate = function(value, separators, separatethousands) {
39872 if(!separatethousands) separatethousands = false;
39873
39874 if(typeof separators !== 'string' || separators.length === 0) {
39875 throw new Error('Separator string required for formatting!');
39876 }
39877
39878 if(typeof value === 'number') {
39879 value = String(value);
39880 }
39881
39882 var thousandsRe = /(\d+)(\d{3})/;
39883 var decimalSep = separators.charAt(0);
39884 var thouSep = separators.charAt(1);
39885
39886 var x = value.split('.');
39887 var x1 = x[0];
39888 var x2 = x.length > 1 ? decimalSep + x[1] : '';
39889
39890 // Years are ignored for thousands separators
39891 if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) {
39892 while(thousandsRe.test(x1)) {
39893 x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2');
39894 }
39895 }
39896
39897 return x1 + x2;
39898};
39899
39900lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)([:|\|][^}]*)?}/g;
39901var SIMPLE_PROPERTY_REGEX = /^\w*$/;
39902
39903/**
39904 * Substitute values from an object into a string
39905 *
39906 * Examples:
39907 * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
39908 * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
39909 *
39910 * @param {string} input string containing %{...} template strings
39911 * @param {obj} data object containing substitution values
39912 *
39913 * @return {string} templated string
39914 */
39915lib.templateString = function(string, obj) {
39916 // Not all that useful, but cache nestedProperty instantiation
39917 // just in case it speeds things up *slightly*:
39918 var getterCache = {};
39919
39920 return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) {
39921 var v;
39922 if(SIMPLE_PROPERTY_REGEX.test(key)) {
39923 v = obj[key];
39924 } else {
39925 getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
39926 v = getterCache[key]();
39927 }
39928 return lib.isValidTextValue(v) ? v : '';
39929 });
39930};
39931
39932var hovertemplateWarnings = {
39933 max: 10,
39934 count: 0,
39935 name: 'hovertemplate'
39936};
39937lib.hovertemplateString = function() {
39938 return templateFormatString.apply(hovertemplateWarnings, arguments);
39939};
39940
39941var texttemplateWarnings = {
39942 max: 10,
39943 count: 0,
39944 name: 'texttemplate'
39945};
39946lib.texttemplateString = function() {
39947 return templateFormatString.apply(texttemplateWarnings, arguments);
39948};
39949
39950var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/;
39951/**
39952 * Substitute values from an object into a string and optionally formats them using d3-format,
39953 * or fallback to associated labels.
39954 *
39955 * Examples:
39956 * Lib.hovertemplateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
39957 * Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
39958 * Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
39959 *
39960 * @param {string} input string containing %{...:...} template strings
39961 * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
39962 * @param {obj} d3 locale
39963 * @param {obj} data objects containing substitution values
39964 *
39965 * @return {string} templated string
39966 */
39967function templateFormatString(string, labels, d3locale) {
39968 var opts = this;
39969 var args = arguments;
39970 if(!labels) labels = {};
39971 // Not all that useful, but cache nestedProperty instantiation
39972 // just in case it speeds things up *slightly*:
39973 var getterCache = {};
39974
39975 return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, key, format) {
39976 var obj, value, i;
39977 for(i = 3; i < args.length; i++) {
39978 obj = args[i];
39979 if(!obj) continue;
39980 if(obj.hasOwnProperty(key)) {
39981 value = obj[key];
39982 break;
39983 }
39984
39985 if(!SIMPLE_PROPERTY_REGEX.test(key)) {
39986 value = getterCache[key] || lib.nestedProperty(obj, key).get();
39987 if(value) getterCache[key] = value;
39988 }
39989 if(value !== undefined) break;
39990 }
39991
39992 if(value === undefined && opts) {
39993 if(opts.count < opts.max) {
39994 lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!');
39995 value = match;
39996 }
39997
39998 if(opts.count === opts.max) {
39999 lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed');
40000 }
40001 opts.count++;
40002
40003 return match;
40004 }
40005
40006 if(format) {
40007 var fmt;
40008 if(format[0] === ':') {
40009 fmt = d3locale ? d3locale.numberFormat : d3.format;
40010 value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value);
40011 }
40012
40013 if(format[0] === '|') {
40014 fmt = d3locale ? d3locale.timeFormat.utc : d3.time.format.utc;
40015 var ms = lib.dateTime2ms(value);
40016 value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt);
40017 }
40018 } else {
40019 if(labels.hasOwnProperty(key + 'Label')) value = labels[key + 'Label'];
40020 }
40021 return value;
40022 });
40023}
40024
40025/*
40026 * alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc
40027 */
40028var char0 = 48;
40029var char9 = 57;
40030lib.subplotSort = function(a, b) {
40031 var l = Math.min(a.length, b.length) + 1;
40032 var numA = 0;
40033 var numB = 0;
40034 for(var i = 0; i < l; i++) {
40035 var charA = a.charCodeAt(i) || 0;
40036 var charB = b.charCodeAt(i) || 0;
40037 var isNumA = charA >= char0 && charA <= char9;
40038 var isNumB = charB >= char0 && charB <= char9;
40039
40040 if(isNumA) numA = 10 * numA + charA - char0;
40041 if(isNumB) numB = 10 * numB + charB - char0;
40042
40043 if(!isNumA || !isNumB) {
40044 if(numA !== numB) return numA - numB;
40045 if(charA !== charB) return charA - charB;
40046 }
40047 }
40048 return numB - numA;
40049};
40050
40051// repeatable pseudorandom generator
40052var randSeed = 2000000000;
40053
40054lib.seedPseudoRandom = function() {
40055 randSeed = 2000000000;
40056};
40057
40058lib.pseudoRandom = function() {
40059 var lastVal = randSeed;
40060 randSeed = (69069 * randSeed + 1) % 4294967296;
40061 // don't let consecutive vals be too close together
40062 // gets away from really trying to be random, in favor of better local uniformity
40063 if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
40064 return randSeed / 4294967296;
40065};
40066
40067
40068/** Fill hover 'pointData' container with 'correct' hover text value
40069 *
40070 * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
40071 * the text elements will be seen in the hover labels.
40072 *
40073 * - If trace hoverinfo contains a 'text' flag and hovertext is set,
40074 * hovertext takes precedence over text
40075 * i.e. the hoverinfo elements will be seen in the hover labels
40076 *
40077 * @param {object} calcPt
40078 * @param {object} trace
40079 * @param {object || array} contOut (mutated here)
40080 */
40081lib.fillText = function(calcPt, trace, contOut) {
40082 var fill = Array.isArray(contOut) ?
40083 function(v) { contOut.push(v); } :
40084 function(v) { contOut.text = v; };
40085
40086 var htx = lib.extractOption(calcPt, trace, 'htx', 'hovertext');
40087 if(lib.isValidTextValue(htx)) return fill(htx);
40088
40089 var tx = lib.extractOption(calcPt, trace, 'tx', 'text');
40090 if(lib.isValidTextValue(tx)) return fill(tx);
40091};
40092
40093// accept all truthy values and 0 (which gets cast to '0' in the hover labels)
40094lib.isValidTextValue = function(v) {
40095 return v || v === 0;
40096};
40097
40098/**
40099 * @param {number} ratio
40100 * @param {number} n (number of decimal places)
40101 */
40102lib.formatPercent = function(ratio, n) {
40103 n = n || 0;
40104 var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%';
40105 for(var i = 0; i < n; i++) {
40106 if(str.indexOf('.') !== -1) {
40107 str = str.replace('0%', '%');
40108 str = str.replace('.%', '%');
40109 }
40110 }
40111 return str;
40112};
40113
40114lib.isHidden = function(gd) {
40115 var display = window.getComputedStyle(gd).display;
40116 return !display || display === 'none';
40117};
40118
40119/** Return transform text for bar bar-like rectangles and pie-like slices
40120 * @param {object} transform
40121 * - targetX: desired position on the x-axis
40122 * - targetY: desired position on the y-axis
40123 * - textX: text middle position on the x-axis
40124 * - textY: text middle position on the y-axis
40125 * - anchorX: (optional) text anchor position on the x-axis (computed from textX), zero for middle anchor
40126 * - anchorY: (optional) text anchor position on the y-axis (computed from textY), zero for middle anchor
40127 * - scale: (optional) scale applied after translate
40128 * - rotate: (optional) rotation applied after scale
40129 * - noCenter: when defined no extra arguments needed in rotation
40130 */
40131lib.getTextTransform = function(transform) {
40132 var noCenter = transform.noCenter;
40133 var textX = transform.textX;
40134 var textY = transform.textY;
40135 var targetX = transform.targetX;
40136 var targetY = transform.targetY;
40137 var anchorX = transform.anchorX || 0;
40138 var anchorY = transform.anchorY || 0;
40139 var rotate = transform.rotate;
40140 var scale = transform.scale;
40141 if(!scale) scale = 0;
40142 else if(scale > 1) scale = 1;
40143
40144 return (
40145 'translate(' +
40146 (targetX - scale * (textX + anchorX)) + ',' +
40147 (targetY - scale * (textY + anchorY)) +
40148 ')' +
40149 (scale < 1 ?
40150 'scale(' + scale + ')' : ''
40151 ) +
40152 (rotate ?
40153 'rotate(' + rotate +
40154 (noCenter ? '' : ' ' + textX + ' ' + textY) +
40155 ')' : ''
40156 )
40157 );
40158};
40159
40160lib.ensureUniformFontSize = function(gd, baseFont) {
40161 var out = lib.extendFlat({}, baseFont);
40162 out.size = Math.max(
40163 baseFont.size,
40164 gd._fullLayout.uniformtext.minsize || 0
40165 );
40166 return out;
40167};
40168
40169},{"../constants/numerical":158,"./anchor_utils":163,"./angles":164,"./array":165,"./clean_number":166,"./clear_responsive":168,"./coerce":169,"./dates":170,"./dom":171,"./extend":173,"./filter_unique":174,"./filter_visible":175,"./geometry2d":176,"./identity":177,"./is_plain_object":179,"./keyed_container":180,"./localize":181,"./loggers":182,"./make_trace_groups":183,"./matrix":184,"./mod":185,"./nested_property":186,"./noop":187,"./notifier":188,"./push_unique":191,"./regex":193,"./relative_attr":194,"./relink_private":195,"./search":196,"./stats":198,"./throttle":200,"./to_log_range":201,"d3":16,"fast-isnumeric":18}],179:[function(_dereq_,module,exports){
40170/**
40171* Copyright 2012-2020, Plotly, Inc.
40172* All rights reserved.
40173*
40174* This source code is licensed under the MIT license found in the
40175* LICENSE file in the root directory of this source tree.
40176*/
40177
40178
40179'use strict';
40180
40181// more info: http://stackoverflow.com/questions/18531624/isplainobject-thing
40182module.exports = function isPlainObject(obj) {
40183 // We need to be a little less strict in the `imagetest` container because
40184 // of how async image requests are handled.
40185 //
40186 // N.B. isPlainObject(new Constructor()) will return true in `imagetest`
40187 if(window && window.process && window.process.versions) {
40188 return Object.prototype.toString.call(obj) === '[object Object]';
40189 }
40190
40191 return (
40192 Object.prototype.toString.call(obj) === '[object Object]' &&
40193 Object.getPrototypeOf(obj) === Object.prototype
40194 );
40195};
40196
40197},{}],180:[function(_dereq_,module,exports){
40198/**
40199* Copyright 2012-2020, Plotly, Inc.
40200* All rights reserved.
40201*
40202* This source code is licensed under the MIT license found in the
40203* LICENSE file in the root directory of this source tree.
40204*/
40205
40206'use strict';
40207
40208var nestedProperty = _dereq_('./nested_property');
40209
40210var SIMPLE_PROPERTY_REGEX = /^\w*$/;
40211
40212// bitmask for deciding what's updated. Sometimes the name needs to be updated,
40213// sometimes the value needs to be updated, and sometimes both do. This is just
40214// a simple way to track what's updated such that it's a simple OR operation to
40215// assimilate new updates.
40216//
40217// The only exception is the UNSET bit that tracks when we need to explicitly
40218// unset and remove the property. This concrn arises because of the special
40219// way in which nestedProperty handles null/undefined. When you specify `null`,
40220// it prunes any unused items in the tree. I ran into some issues with it getting
40221// null vs undefined confused, so UNSET is just a bit that forces the property
40222// update to send `null`, removing the property explicitly rather than setting
40223// it to undefined.
40224var NONE = 0;
40225var NAME = 1;
40226var VALUE = 2;
40227var BOTH = 3;
40228var UNSET = 4;
40229
40230module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
40231 keyName = keyName || 'name';
40232 valueName = valueName || 'value';
40233 var i, arr, baseProp;
40234 var changeTypes = {};
40235
40236 if(path && path.length) {
40237 baseProp = nestedProperty(baseObj, path);
40238 arr = baseProp.get();
40239 } else {
40240 arr = baseObj;
40241 }
40242
40243 path = path || '';
40244
40245 // Construct an index:
40246 var indexLookup = {};
40247 if(arr) {
40248 for(i = 0; i < arr.length; i++) {
40249 indexLookup[arr[i][keyName]] = i;
40250 }
40251 }
40252
40253 var isSimpleValueProp = SIMPLE_PROPERTY_REGEX.test(valueName);
40254
40255 var obj = {
40256 set: function(name, value) {
40257 var changeType = value === null ? UNSET : NONE;
40258
40259 // create the base array if necessary
40260 if(!arr) {
40261 if(!baseProp || changeType === UNSET) return;
40262
40263 arr = [];
40264 baseProp.set(arr);
40265 }
40266
40267 var idx = indexLookup[name];
40268 if(idx === undefined) {
40269 if(changeType === UNSET) return;
40270
40271 changeType = changeType | BOTH;
40272 idx = arr.length;
40273 indexLookup[name] = idx;
40274 } else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) {
40275 changeType = changeType | VALUE;
40276 }
40277
40278 var newValue = arr[idx] = arr[idx] || {};
40279 newValue[keyName] = name;
40280
40281 if(isSimpleValueProp) {
40282 newValue[valueName] = value;
40283 } else {
40284 nestedProperty(newValue, valueName).set(value);
40285 }
40286
40287 // If it's not an unset, force that bit to be unset. This is all related to the fact
40288 // that undefined and null are a bit specially implemented in nestedProperties.
40289 if(value !== null) {
40290 changeType = changeType & ~UNSET;
40291 }
40292
40293 changeTypes[idx] = changeTypes[idx] | changeType;
40294
40295 return obj;
40296 },
40297 get: function(name) {
40298 if(!arr) return;
40299
40300 var idx = indexLookup[name];
40301
40302 if(idx === undefined) {
40303 return undefined;
40304 } else if(isSimpleValueProp) {
40305 return arr[idx][valueName];
40306 } else {
40307 return nestedProperty(arr[idx], valueName).get();
40308 }
40309 },
40310 rename: function(name, newName) {
40311 var idx = indexLookup[name];
40312
40313 if(idx === undefined) return obj;
40314 changeTypes[idx] = changeTypes[idx] | NAME;
40315
40316 indexLookup[newName] = idx;
40317 delete indexLookup[name];
40318
40319 arr[idx][keyName] = newName;
40320
40321 return obj;
40322 },
40323 remove: function(name) {
40324 var idx = indexLookup[name];
40325
40326 if(idx === undefined) return obj;
40327
40328 var object = arr[idx];
40329 if(Object.keys(object).length > 2) {
40330 // This object contains more than just the key/value, so unset
40331 // the value without modifying the entry otherwise:
40332 changeTypes[idx] = changeTypes[idx] | VALUE;
40333 return obj.set(name, null);
40334 }
40335
40336 if(isSimpleValueProp) {
40337 for(i = idx; i < arr.length; i++) {
40338 changeTypes[i] = changeTypes[i] | BOTH;
40339 }
40340 for(i = idx; i < arr.length; i++) {
40341 indexLookup[arr[i][keyName]]--;
40342 }
40343 arr.splice(idx, 1);
40344 delete(indexLookup[name]);
40345 } else {
40346 // Perform this update *strictly* so we can check whether the result's
40347 // been pruned. If so, it's a removal. If not, it's a value unset only.
40348 nestedProperty(object, valueName).set(null);
40349
40350 // Now check if the top level nested property has any keys left. If so,
40351 // the object still has values so we only want to unset the key. If not,
40352 // the entire object can be removed since there's no other data.
40353 // var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []);
40354
40355 changeTypes[idx] = changeTypes[idx] | VALUE | UNSET;
40356 }
40357
40358 return obj;
40359 },
40360 constructUpdate: function() {
40361 var astr, idx;
40362 var update = {};
40363 var changed = Object.keys(changeTypes);
40364 for(var i = 0; i < changed.length; i++) {
40365 idx = changed[i];
40366 astr = path + '[' + idx + ']';
40367 if(arr[idx]) {
40368 if(changeTypes[idx] & NAME) {
40369 update[astr + '.' + keyName] = arr[idx][keyName];
40370 }
40371 if(changeTypes[idx] & VALUE) {
40372 if(isSimpleValueProp) {
40373 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName];
40374 } else {
40375 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get();
40376 }
40377 }
40378 } else {
40379 update[astr] = null;
40380 }
40381 }
40382
40383 return update;
40384 }
40385 };
40386
40387 return obj;
40388};
40389
40390},{"./nested_property":186}],181:[function(_dereq_,module,exports){
40391/**
40392* Copyright 2012-2020, Plotly, Inc.
40393* All rights reserved.
40394*
40395* This source code is licensed under the MIT license found in the
40396* LICENSE file in the root directory of this source tree.
40397*/
40398
40399
40400'use strict';
40401
40402var Registry = _dereq_('../registry');
40403
40404/**
40405 * localize: translate a string for the current locale
40406 *
40407 * @param {object} gd: the graphDiv for context
40408 * gd._context.locale determines the language (& optional region/country)
40409 * the dictionary for each locale may either be supplied in
40410 * gd._context.locales or globally via Plotly.register
40411 * @param {string} s: the string to translate
40412 */
40413module.exports = function localize(gd, s) {
40414 var locale = gd._context.locale;
40415
40416 /*
40417 * Priority of lookup:
40418 * contextDicts[locale],
40419 * registeredDicts[locale],
40420 * contextDicts[baseLocale], (if baseLocale is distinct)
40421 * registeredDicts[baseLocale]
40422 * Return the first translation we find.
40423 * This way if you have a regionalization you are allowed to specify
40424 * only what's different from the base locale, everything else will
40425 * fall back on the base.
40426 */
40427 for(var i = 0; i < 2; i++) {
40428 var locales = gd._context.locales;
40429 for(var j = 0; j < 2; j++) {
40430 var dict = (locales[locale] || {}).dictionary;
40431 if(dict) {
40432 var out = dict[s];
40433 if(out) return out;
40434 }
40435 locales = Registry.localeRegistry;
40436 }
40437
40438 var baseLocale = locale.split('-')[0];
40439 if(baseLocale === locale) break;
40440 locale = baseLocale;
40441 }
40442
40443 return s;
40444};
40445
40446},{"../registry":269}],182:[function(_dereq_,module,exports){
40447/**
40448* Copyright 2012-2020, Plotly, Inc.
40449* All rights reserved.
40450*
40451* This source code is licensed under the MIT license found in the
40452* LICENSE file in the root directory of this source tree.
40453*/
40454
40455'use strict';
40456
40457/* eslint-disable no-console */
40458
40459var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
40460
40461var notifier = _dereq_('./notifier');
40462
40463var loggers = module.exports = {};
40464
40465/**
40466 * ------------------------------------------
40467 * debugging tools
40468 * ------------------------------------------
40469 */
40470
40471loggers.log = function() {
40472 var i;
40473
40474 if(dfltConfig.logging > 1) {
40475 var messages = ['LOG:'];
40476 for(i = 0; i < arguments.length; i++) {
40477 messages.push(arguments[i]);
40478 }
40479 apply(console.trace || console.log, messages);
40480 }
40481
40482 if(dfltConfig.notifyOnLogging > 1) {
40483 var lines = [];
40484 for(i = 0; i < arguments.length; i++) {
40485 lines.push(arguments[i]);
40486 }
40487 notifier(lines.join('<br>'), 'long');
40488 }
40489};
40490
40491loggers.warn = function() {
40492 var i;
40493
40494 if(dfltConfig.logging > 0) {
40495 var messages = ['WARN:'];
40496 for(i = 0; i < arguments.length; i++) {
40497 messages.push(arguments[i]);
40498 }
40499 apply(console.trace || console.log, messages);
40500 }
40501
40502 if(dfltConfig.notifyOnLogging > 0) {
40503 var lines = [];
40504 for(i = 0; i < arguments.length; i++) {
40505 lines.push(arguments[i]);
40506 }
40507 notifier(lines.join('<br>'), 'stick');
40508 }
40509};
40510
40511loggers.error = function() {
40512 var i;
40513
40514 if(dfltConfig.logging > 0) {
40515 var messages = ['ERROR:'];
40516 for(i = 0; i < arguments.length; i++) {
40517 messages.push(arguments[i]);
40518 }
40519 apply(console.error, messages);
40520 }
40521
40522 if(dfltConfig.notifyOnLogging > 0) {
40523 var lines = [];
40524 for(i = 0; i < arguments.length; i++) {
40525 lines.push(arguments[i]);
40526 }
40527 notifier(lines.join('<br>'), 'stick');
40528 }
40529};
40530
40531/*
40532 * Robust apply, for IE9 where console.log doesn't support
40533 * apply like other functions do
40534 */
40535function apply(f, args) {
40536 if(f && f.apply) {
40537 try {
40538 // `this` should always be console, since here we're always
40539 // applying a method of the console object.
40540 f.apply(console, args);
40541 return;
40542 } catch(e) { /* in case apply failed, fall back on the code below */ }
40543 }
40544
40545 // no apply - just try calling the function on each arg independently
40546 for(var i = 0; i < args.length; i++) {
40547 try {
40548 f(args[i]);
40549 } catch(e) {
40550 // still fails - last resort simple console.log
40551 console.log(args[i]);
40552 }
40553 }
40554}
40555
40556},{"../plot_api/plot_config":210,"./notifier":188}],183:[function(_dereq_,module,exports){
40557/**
40558* Copyright 2012-2020, Plotly, Inc.
40559* All rights reserved.
40560*
40561* This source code is licensed under the MIT license found in the
40562* LICENSE file in the root directory of this source tree.
40563*/
40564
40565'use strict';
40566
40567var d3 = _dereq_('d3');
40568
40569/**
40570 * General helper to manage trace groups based on calcdata
40571 *
40572 * @param {d3.selection} traceLayer: a selection containing a single group
40573 * to draw these traces into
40574 * @param {array} cdModule: array of calcdata items for this
40575 * module and subplot combination. Assumes the calcdata item for each
40576 * trace is an array with the fullData trace attached to the first item.
40577 * @param {string} cls: the class attribute to give each trace group
40578 * so you can give multiple classes separated by spaces
40579 */
40580module.exports = function makeTraceGroups(traceLayer, cdModule, cls) {
40581 var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.'))
40582 .data(cdModule, function(cd) { return cd[0].trace.uid; });
40583
40584 traces.exit().remove();
40585
40586 traces.enter().append('g')
40587 .attr('class', cls);
40588
40589 traces.order();
40590
40591 // stash ref node to trace group in calcdata,
40592 // useful for (fast) styleOnSelect
40593 var k = traceLayer.classed('rangeplot') ? 'nodeRangePlot3' : 'node3';
40594 traces.each(function(cd) { cd[0][k] = d3.select(this); });
40595
40596 return traces;
40597};
40598
40599},{"d3":16}],184:[function(_dereq_,module,exports){
40600/**
40601* Copyright 2012-2020, Plotly, Inc.
40602* All rights reserved.
40603*
40604* This source code is licensed under the MIT license found in the
40605* LICENSE file in the root directory of this source tree.
40606*/
40607
40608
40609'use strict';
40610
40611
40612exports.init2dArray = function(rowLength, colLength) {
40613 var array = new Array(rowLength);
40614 for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
40615 return array;
40616};
40617
40618/**
40619 * transpose a (possibly ragged) 2d array z. inspired by
40620 * http://stackoverflow.com/questions/17428587/
40621 * transposing-a-2d-array-in-javascript
40622 */
40623exports.transposeRagged = function(z) {
40624 var maxlen = 0;
40625 var zlen = z.length;
40626 var i, j;
40627 // Maximum row length:
40628 for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
40629
40630 var t = new Array(maxlen);
40631 for(i = 0; i < maxlen; i++) {
40632 t[i] = new Array(zlen);
40633 for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
40634 }
40635
40636 return t;
40637};
40638
40639// our own dot function so that we don't need to include numeric
40640exports.dot = function(x, y) {
40641 if(!(x.length && y.length) || x.length !== y.length) return null;
40642
40643 var len = x.length;
40644 var out;
40645 var i;
40646
40647 if(x[0].length) {
40648 // mat-vec or mat-mat
40649 out = new Array(len);
40650 for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
40651 } else if(y[0].length) {
40652 // vec-mat
40653 var yTranspose = exports.transposeRagged(y);
40654 out = new Array(yTranspose.length);
40655 for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
40656 } else {
40657 // vec-vec
40658 out = 0;
40659 for(i = 0; i < len; i++) out += x[i] * y[i];
40660 }
40661
40662 return out;
40663};
40664
40665// translate by (x,y)
40666exports.translationMatrix = function(x, y) {
40667 return [[1, 0, x], [0, 1, y], [0, 0, 1]];
40668};
40669
40670// rotate by alpha around (0,0)
40671exports.rotationMatrix = function(alpha) {
40672 var a = alpha * Math.PI / 180;
40673 return [[Math.cos(a), -Math.sin(a), 0],
40674 [Math.sin(a), Math.cos(a), 0],
40675 [0, 0, 1]];
40676};
40677
40678// rotate by alpha around (x,y)
40679exports.rotationXYMatrix = function(a, x, y) {
40680 return exports.dot(
40681 exports.dot(exports.translationMatrix(x, y),
40682 exports.rotationMatrix(a)),
40683 exports.translationMatrix(-x, -y));
40684};
40685
40686// applies a 2D transformation matrix to either x and y params or an [x,y] array
40687exports.apply2DTransform = function(transform) {
40688 return function() {
40689 var args = arguments;
40690 if(args.length === 3) {
40691 args = args[0];
40692 }// from map
40693 var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
40694 return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
40695 };
40696};
40697
40698// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
40699exports.apply2DTransform2 = function(transform) {
40700 var at = exports.apply2DTransform(transform);
40701 return function(xys) {
40702 return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
40703 };
40704};
40705
40706},{}],185:[function(_dereq_,module,exports){
40707/**
40708* Copyright 2012-2020, Plotly, Inc.
40709* All rights reserved.
40710*
40711* This source code is licensed under the MIT license found in the
40712* LICENSE file in the root directory of this source tree.
40713*/
40714
40715'use strict';
40716
40717/**
40718 * sanitized modulus function that always returns in the range [0, d)
40719 * rather than (-d, 0] if v is negative
40720 */
40721function mod(v, d) {
40722 var out = v % d;
40723 return out < 0 ? out + d : out;
40724}
40725
40726/**
40727 * sanitized modulus function that always returns in the range [-d/2, d/2]
40728 * rather than (-d, 0] if v is negative
40729 */
40730function modHalf(v, d) {
40731 return Math.abs(v) > (d / 2) ?
40732 v - Math.round(v / d) * d :
40733 v;
40734}
40735
40736module.exports = {
40737 mod: mod,
40738 modHalf: modHalf
40739};
40740
40741},{}],186:[function(_dereq_,module,exports){
40742/**
40743* Copyright 2012-2020, Plotly, Inc.
40744* All rights reserved.
40745*
40746* This source code is licensed under the MIT license found in the
40747* LICENSE file in the root directory of this source tree.
40748*/
40749
40750
40751'use strict';
40752
40753var isNumeric = _dereq_('fast-isnumeric');
40754var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
40755
40756/**
40757 * convert a string s (such as 'xaxis.range[0]')
40758 * representing a property of nested object into set and get methods
40759 * also return the string and object so we don't have to keep track of them
40760 * allows [-1] for an array index, to set a property inside all elements
40761 * of an array
40762 * eg if obj = {arr: [{a: 1}, {a: 2}]}
40763 * you can do p = nestedProperty(obj, 'arr[-1].a')
40764 * but you cannot set the array itself this way, to do that
40765 * just set the whole array.
40766 * eg if obj = {arr: [1, 2, 3]}
40767 * you can't do nestedProperty(obj, 'arr[-1]').set(5)
40768 * but you can do nestedProperty(obj, 'arr').set([5, 5, 5])
40769 */
40770module.exports = function nestedProperty(container, propStr) {
40771 if(isNumeric(propStr)) propStr = String(propStr);
40772 else if(typeof propStr !== 'string' ||
40773 propStr.substr(propStr.length - 4) === '[-1]') {
40774 throw 'bad property string';
40775 }
40776
40777 var j = 0;
40778 var propParts = propStr.split('.');
40779 var indexed;
40780 var indices;
40781 var i;
40782
40783 // check for parts of the nesting hierarchy that are numbers (ie array elements)
40784 while(j < propParts.length) {
40785 // look for non-bracket chars, then any number of [##] blocks
40786 indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
40787 if(indexed) {
40788 if(indexed[1]) propParts[j] = indexed[1];
40789 // allow propStr to start with bracketed array indices
40790 else if(j === 0) propParts.splice(0, 1);
40791 else throw 'bad property string';
40792
40793 indices = indexed[2]
40794 .substr(1, indexed[2].length - 2)
40795 .split('][');
40796
40797 for(i = 0; i < indices.length; i++) {
40798 j++;
40799 propParts.splice(j, 0, Number(indices[i]));
40800 }
40801 }
40802 j++;
40803 }
40804
40805 if(typeof container !== 'object') {
40806 return badContainer(container, propStr, propParts);
40807 }
40808
40809 return {
40810 set: npSet(container, propParts, propStr),
40811 get: npGet(container, propParts),
40812 astr: propStr,
40813 parts: propParts,
40814 obj: container
40815 };
40816};
40817
40818function npGet(cont, parts) {
40819 return function() {
40820 var curCont = cont;
40821 var curPart;
40822 var allSame;
40823 var out;
40824 var i;
40825 var j;
40826
40827 for(i = 0; i < parts.length - 1; i++) {
40828 curPart = parts[i];
40829 if(curPart === -1) {
40830 allSame = true;
40831 out = [];
40832 for(j = 0; j < curCont.length; j++) {
40833 out[j] = npGet(curCont[j], parts.slice(i + 1))();
40834 if(out[j] !== out[0]) allSame = false;
40835 }
40836 return allSame ? out[0] : out;
40837 }
40838 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
40839 return undefined;
40840 }
40841 curCont = curCont[curPart];
40842 if(typeof curCont !== 'object' || curCont === null) {
40843 return undefined;
40844 }
40845 }
40846
40847 // only hit this if parts.length === 1
40848 if(typeof curCont !== 'object' || curCont === null) return undefined;
40849
40850 out = curCont[parts[i]];
40851 if(out === null) return undefined;
40852 return out;
40853 };
40854}
40855
40856/*
40857 * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an
40858 * *args* array.
40859 *
40860 * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset
40861 * a net noop; but this causes far more complication than it's worth, and still had
40862 * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410
40863 *
40864 * *args* arrays get passed directly to API methods and we should respect null if
40865 * the user put it there, but otherwise null is deleted as we use it as code
40866 * in restyle/relayout/update for "delete this value" whereas undefined means
40867 * "ignore this edit"
40868 */
40869var ARGS_PATTERN = /(^|\.)args\[/;
40870function isDeletable(val, propStr) {
40871 return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN));
40872}
40873
40874function npSet(cont, parts, propStr) {
40875 return function(val) {
40876 var curCont = cont;
40877 var propPart = '';
40878 var containerLevels = [[cont, propPart]];
40879 var toDelete = isDeletable(val, propStr);
40880 var curPart;
40881 var i;
40882
40883 for(i = 0; i < parts.length - 1; i++) {
40884 curPart = parts[i];
40885
40886 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
40887 throw 'array index but container is not an array';
40888 }
40889
40890 // handle special -1 array index
40891 if(curPart === -1) {
40892 toDelete = !setArrayAll(curCont, parts.slice(i + 1), val, propStr);
40893 if(toDelete) break;
40894 else return;
40895 }
40896
40897 if(!checkNewContainer(curCont, curPart, parts[i + 1], toDelete)) {
40898 break;
40899 }
40900
40901 curCont = curCont[curPart];
40902
40903 if(typeof curCont !== 'object' || curCont === null) {
40904 throw 'container is not an object';
40905 }
40906
40907 propPart = joinPropStr(propPart, curPart);
40908
40909 containerLevels.push([curCont, propPart]);
40910 }
40911
40912 if(toDelete) {
40913 if(i === parts.length - 1) {
40914 delete curCont[parts[i]];
40915
40916 // The one bit of pruning we still do: drop `undefined` from the end of arrays.
40917 // In case someone has already unset previous items, continue until we hit a
40918 // non-undefined value.
40919 if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) {
40920 while(curCont.length && curCont[curCont.length - 1] === undefined) {
40921 curCont.pop();
40922 }
40923 }
40924 }
40925 } else curCont[parts[i]] = val;
40926 };
40927}
40928
40929function joinPropStr(propStr, newPart) {
40930 var toAdd = newPart;
40931 if(isNumeric(newPart)) toAdd = '[' + newPart + ']';
40932 else if(propStr) toAdd = '.' + newPart;
40933
40934 return propStr + toAdd;
40935}
40936
40937// handle special -1 array index
40938function setArrayAll(containerArray, innerParts, val, propStr) {
40939 var arrayVal = isArrayOrTypedArray(val);
40940 var allSet = true;
40941 var thisVal = val;
40942 var thisPropStr = propStr.replace('-1', 0);
40943 var deleteThis = arrayVal ? false : isDeletable(val, thisPropStr);
40944 var firstPart = innerParts[0];
40945 var i;
40946
40947 for(i = 0; i < containerArray.length; i++) {
40948 thisPropStr = propStr.replace('-1', i);
40949 if(arrayVal) {
40950 thisVal = val[i % val.length];
40951 deleteThis = isDeletable(thisVal, thisPropStr);
40952 }
40953 if(deleteThis) allSet = false;
40954 if(!checkNewContainer(containerArray, i, firstPart, deleteThis)) {
40955 continue;
40956 }
40957 npSet(containerArray[i], innerParts, propStr.replace('-1', i))(thisVal);
40958 }
40959 return allSet;
40960}
40961
40962/**
40963 * make new sub-container as needed.
40964 * returns false if there's no container and none is needed
40965 * because we're only deleting an attribute
40966 */
40967function checkNewContainer(container, part, nextPart, toDelete) {
40968 if(container[part] === undefined) {
40969 if(toDelete) return false;
40970
40971 if(typeof nextPart === 'number') container[part] = [];
40972 else container[part] = {};
40973 }
40974 return true;
40975}
40976
40977function badContainer(container, propStr, propParts) {
40978 return {
40979 set: function() { throw 'bad container'; },
40980 get: function() {},
40981 astr: propStr,
40982 parts: propParts,
40983 obj: container
40984 };
40985}
40986
40987},{"./array":165,"fast-isnumeric":18}],187:[function(_dereq_,module,exports){
40988/**
40989* Copyright 2012-2020, Plotly, Inc.
40990* All rights reserved.
40991*
40992* This source code is licensed under the MIT license found in the
40993* LICENSE file in the root directory of this source tree.
40994*/
40995
40996'use strict';
40997
40998// Simple helper functions
40999// none of these need any external deps
41000
41001module.exports = function noop() {};
41002
41003},{}],188:[function(_dereq_,module,exports){
41004/**
41005* Copyright 2012-2020, Plotly, Inc.
41006* All rights reserved.
41007*
41008* This source code is licensed under the MIT license found in the
41009* LICENSE file in the root directory of this source tree.
41010*/
41011
41012
41013'use strict';
41014
41015var d3 = _dereq_('d3');
41016var isNumeric = _dereq_('fast-isnumeric');
41017
41018var NOTEDATA = [];
41019
41020/**
41021 * notifier
41022 * @param {String} text The person's user name
41023 * @param {Number} [delay=1000] The delay time in milliseconds
41024 * or 'long' which provides 2000 ms delay time.
41025 * @return {undefined} this function does not return a value
41026 */
41027module.exports = function(text, displayLength) {
41028 if(NOTEDATA.indexOf(text) !== -1) return;
41029
41030 NOTEDATA.push(text);
41031
41032 var ts = 1000;
41033 if(isNumeric(displayLength)) ts = displayLength;
41034 else if(displayLength === 'long') ts = 3000;
41035
41036 var notifierContainer = d3.select('body')
41037 .selectAll('.plotly-notifier')
41038 .data([0]);
41039 notifierContainer.enter()
41040 .append('div')
41041 .classed('plotly-notifier', true);
41042
41043 var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
41044
41045 function killNote(transition) {
41046 transition
41047 .duration(700)
41048 .style('opacity', 0)
41049 .each('end', function(thisText) {
41050 var thisIndex = NOTEDATA.indexOf(thisText);
41051 if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
41052 d3.select(this).remove();
41053 });
41054 }
41055
41056 notes.enter().append('div')
41057 .classed('notifier-note', true)
41058 .style('opacity', 0)
41059 .each(function(thisText) {
41060 var note = d3.select(this);
41061
41062 note.append('button')
41063 .classed('notifier-close', true)
41064 .html('&times;')
41065 .on('click', function() {
41066 note.transition().call(killNote);
41067 });
41068
41069 var p = note.append('p');
41070 var lines = thisText.split(/<br\s*\/?>/g);
41071 for(var i = 0; i < lines.length; i++) {
41072 if(i) p.append('br');
41073 p.append('span').text(lines[i]);
41074 }
41075
41076 if(displayLength === 'stick') {
41077 note.transition()
41078 .duration(350)
41079 .style('opacity', 1);
41080 } else {
41081 note.transition()
41082 .duration(700)
41083 .style('opacity', 1)
41084 .transition()
41085 .delay(ts)
41086 .call(killNote);
41087 }
41088 });
41089};
41090
41091},{"d3":16,"fast-isnumeric":18}],189:[function(_dereq_,module,exports){
41092/**
41093* Copyright 2012-2020, Plotly, Inc.
41094* All rights reserved.
41095*
41096* This source code is licensed under the MIT license found in the
41097* LICENSE file in the root directory of this source tree.
41098*/
41099
41100
41101'use strict';
41102
41103var setCursor = _dereq_('./setcursor');
41104
41105var STASHATTR = 'data-savedcursor';
41106var NO_CURSOR = '!!';
41107
41108/*
41109 * works with our CSS cursor classes (see css/_cursor.scss)
41110 * to override a previous cursor set on d3 single-element selections,
41111 * by moving the name of the original cursor to the data-savedcursor attr.
41112 * omit cursor to revert to the previously set value.
41113 */
41114module.exports = function overrideCursor(el3, csr) {
41115 var savedCursor = el3.attr(STASHATTR);
41116 if(csr) {
41117 if(!savedCursor) {
41118 var classes = (el3.attr('class') || '').split(' ');
41119 for(var i = 0; i < classes.length; i++) {
41120 var cls = classes[i];
41121 if(cls.indexOf('cursor-') === 0) {
41122 el3.attr(STASHATTR, cls.substr(7))
41123 .classed(cls, false);
41124 }
41125 }
41126 if(!el3.attr(STASHATTR)) {
41127 el3.attr(STASHATTR, NO_CURSOR);
41128 }
41129 }
41130 setCursor(el3, csr);
41131 } else if(savedCursor) {
41132 el3.attr(STASHATTR, null);
41133
41134 if(savedCursor === NO_CURSOR) setCursor(el3);
41135 else setCursor(el3, savedCursor);
41136 }
41137};
41138
41139},{"./setcursor":197}],190:[function(_dereq_,module,exports){
41140/**
41141* Copyright 2012-2020, Plotly, Inc.
41142* All rights reserved.
41143*
41144* This source code is licensed under the MIT license found in the
41145* LICENSE file in the root directory of this source tree.
41146*/
41147
41148
41149'use strict';
41150
41151var dot = _dereq_('./matrix').dot;
41152var BADNUM = _dereq_('../constants/numerical').BADNUM;
41153
41154var polygon = module.exports = {};
41155
41156/**
41157 * Turn an array of [x, y] pairs into a polygon object
41158 * that can test if points are inside it
41159 *
41160 * @param ptsIn Array of [x, y] pairs
41161 *
41162 * @returns polygon Object {xmin, xmax, ymin, ymax, pts, contains}
41163 * (x|y)(min|max) are the bounding rect of the polygon
41164 * pts is the original array, with the first pair repeated at the end
41165 * contains is a function: (pt, omitFirstEdge)
41166 * pt is the [x, y] pair to test
41167 * omitFirstEdge truthy means points exactly on the first edge don't
41168 * count. This is for use adding one polygon to another so we
41169 * don't double-count the edge where they meet.
41170 * returns boolean: is pt inside the polygon (including on its edges)
41171 */
41172polygon.tester = function tester(ptsIn) {
41173 var pts = ptsIn.slice();
41174 var xmin = pts[0][0];
41175 var xmax = xmin;
41176 var ymin = pts[0][1];
41177 var ymax = ymin;
41178 var i;
41179
41180 pts.push(pts[0]);
41181 for(i = 1; i < pts.length; i++) {
41182 xmin = Math.min(xmin, pts[i][0]);
41183 xmax = Math.max(xmax, pts[i][0]);
41184 ymin = Math.min(ymin, pts[i][1]);
41185 ymax = Math.max(ymax, pts[i][1]);
41186 }
41187
41188 // do we have a rectangle? Handle this here, so we can use the same
41189 // tester for the rectangular case without sacrificing speed
41190
41191 var isRect = false;
41192 var rectFirstEdgeTest;
41193
41194 if(pts.length === 5) {
41195 if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz
41196 if(pts[2][0] === pts[3][0] &&
41197 pts[0][1] === pts[3][1] &&
41198 pts[1][1] === pts[2][1]) {
41199 isRect = true;
41200 rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; };
41201 }
41202 } else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert
41203 if(pts[2][1] === pts[3][1] &&
41204 pts[0][0] === pts[3][0] &&
41205 pts[1][0] === pts[2][0]) {
41206 isRect = true;
41207 rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; };
41208 }
41209 }
41210 }
41211
41212 function rectContains(pt, omitFirstEdge) {
41213 var x = pt[0];
41214 var y = pt[1];
41215
41216 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
41217 // pt is outside the bounding box of polygon
41218 return false;
41219 }
41220 if(omitFirstEdge && rectFirstEdgeTest(pt)) return false;
41221
41222 return true;
41223 }
41224
41225 function contains(pt, omitFirstEdge) {
41226 var x = pt[0];
41227 var y = pt[1];
41228
41229 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
41230 // pt is outside the bounding box of polygon
41231 return false;
41232 }
41233
41234 var imax = pts.length;
41235 var x1 = pts[0][0];
41236 var y1 = pts[0][1];
41237 var crossings = 0;
41238 var i;
41239 var x0;
41240 var y0;
41241 var xmini;
41242 var ycross;
41243
41244 for(i = 1; i < imax; i++) {
41245 // find all crossings of a vertical line upward from pt with
41246 // polygon segments
41247 // crossings exactly at xmax don't count, unless the point is
41248 // exactly on the segment, then it counts as inside.
41249 x0 = x1;
41250 y0 = y1;
41251 x1 = pts[i][0];
41252 y1 = pts[i][1];
41253 xmini = Math.min(x0, x1);
41254
41255 if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) {
41256 // outside the bounding box of this segment, it's only a crossing
41257 // if it's below the box.
41258
41259 continue;
41260 } else if(y < Math.min(y0, y1)) {
41261 // don't count the left-most point of the segment as a crossing
41262 // because we don't want to double-count adjacent crossings
41263 // UNLESS the polygon turns past vertical at exactly this x
41264 // Note that this is repeated below, but we can't factor it out
41265 // because
41266 if(x !== xmini) crossings++;
41267 } else {
41268 // inside the bounding box, check the actual line intercept
41269
41270 // vertical segment - we know already that the point is exactly
41271 // on the segment, so mark the crossing as exactly at the point.
41272 if(x1 === x0) ycross = y;
41273 // any other angle
41274 else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
41275
41276 // exactly on the edge: counts as inside the polygon, unless it's the
41277 // first edge and we're omitting it.
41278 if(y === ycross) {
41279 if(i === 1 && omitFirstEdge) return false;
41280 return true;
41281 }
41282
41283 if(y <= ycross && x !== xmini) crossings++;
41284 }
41285 }
41286
41287 // if we've gotten this far, odd crossings means inside, even is outside
41288 return crossings % 2 === 1;
41289 }
41290
41291 // detect if poly is degenerate
41292 var degenerate = true;
41293 var lastPt = pts[0];
41294 for(i = 1; i < pts.length; i++) {
41295 if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) {
41296 degenerate = false;
41297 break;
41298 }
41299 }
41300
41301 return {
41302 xmin: xmin,
41303 xmax: xmax,
41304 ymin: ymin,
41305 ymax: ymax,
41306 pts: pts,
41307 contains: isRect ? rectContains : contains,
41308 isRect: isRect,
41309 degenerate: degenerate
41310 };
41311};
41312
41313/**
41314 * Test if a segment of a points array is bent or straight
41315 *
41316 * @param pts Array of [x, y] pairs
41317 * @param start the index of the proposed start of the straight section
41318 * @param end the index of the proposed end point
41319 * @param tolerance the max distance off the line connecting start and end
41320 * before the line counts as bent
41321 * @returns boolean: true means this segment is bent, false means straight
41322 */
41323polygon.isSegmentBent = function isSegmentBent(pts, start, end, tolerance) {
41324 var startPt = pts[start];
41325 var segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]];
41326 var segmentSquared = dot(segment, segment);
41327 var segmentLen = Math.sqrt(segmentSquared);
41328 var unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen];
41329 var i;
41330 var part;
41331 var partParallel;
41332
41333 for(i = start + 1; i < end; i++) {
41334 part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]];
41335 partParallel = dot(part, segment);
41336
41337 if(partParallel < 0 || partParallel > segmentSquared ||
41338 Math.abs(dot(part, unitPerp)) > tolerance) return true;
41339 }
41340 return false;
41341};
41342
41343/**
41344 * Make a filtering polygon, to minimize the number of segments
41345 *
41346 * @param pts Array of [x, y] pairs (must start with at least 1 pair)
41347 * @param tolerance the maximum deviation from straight allowed for
41348 * removing points to simplify the polygon
41349 *
41350 * @returns Object {addPt, raw, filtered}
41351 * addPt is a function(pt: [x, y] pair) to add a raw point and
41352 * continue filtering
41353 * raw is all the input points
41354 * filtered is the resulting filtered Array of [x, y] pairs
41355 */
41356polygon.filter = function filter(pts, tolerance) {
41357 var ptsFiltered = [pts[0]];
41358 var doneRawIndex = 0;
41359 var doneFilteredIndex = 0;
41360
41361 function addPt(pt) {
41362 pts.push(pt);
41363 var prevFilterLen = ptsFiltered.length;
41364 var iLast = doneRawIndex;
41365 ptsFiltered.splice(doneFilteredIndex + 1);
41366
41367 for(var i = iLast + 1; i < pts.length; i++) {
41368 if(i === pts.length - 1 || polygon.isSegmentBent(pts, iLast, i + 1, tolerance)) {
41369 ptsFiltered.push(pts[i]);
41370 if(ptsFiltered.length < prevFilterLen - 2) {
41371 doneRawIndex = i;
41372 doneFilteredIndex = ptsFiltered.length - 1;
41373 }
41374 iLast = i;
41375 }
41376 }
41377 }
41378
41379 if(pts.length > 1) {
41380 var lastPt = pts.pop();
41381 addPt(lastPt);
41382 }
41383
41384 return {
41385 addPt: addPt,
41386 raw: pts,
41387 filtered: ptsFiltered
41388 };
41389};
41390
41391},{"../constants/numerical":158,"./matrix":184}],191:[function(_dereq_,module,exports){
41392/**
41393* Copyright 2012-2020, Plotly, Inc.
41394* All rights reserved.
41395*
41396* This source code is licensed under the MIT license found in the
41397* LICENSE file in the root directory of this source tree.
41398*/
41399
41400'use strict';
41401
41402/**
41403 * Push array with unique items
41404 *
41405 * Ignores falsy items, except 0 so we can use it to construct arrays of indices.
41406 *
41407 * @param {array} array
41408 * array to be filled
41409 * @param {any} item
41410 * item to be or not to be inserted
41411 * @return {array}
41412 * ref to array (now possibly containing one more item)
41413 *
41414 */
41415module.exports = function pushUnique(array, item) {
41416 if(item instanceof RegExp) {
41417 var itemStr = item.toString();
41418 for(var i = 0; i < array.length; i++) {
41419 if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
41420 return array;
41421 }
41422 }
41423 array.push(item);
41424 } else if((item || item === 0) && array.indexOf(item) === -1) array.push(item);
41425
41426 return array;
41427};
41428
41429},{}],192:[function(_dereq_,module,exports){
41430/**
41431* Copyright 2012-2020, Plotly, Inc.
41432* All rights reserved.
41433*
41434* This source code is licensed under the MIT license found in the
41435* LICENSE file in the root directory of this source tree.
41436*/
41437
41438'use strict';
41439
41440var Lib = _dereq_('../lib');
41441var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
41442
41443/**
41444 * Copy arg array *without* removing `undefined` values from objects.
41445 *
41446 * @param gd
41447 * @param args
41448 * @returns {Array}
41449 */
41450function copyArgArray(gd, args) {
41451 var copy = [];
41452 var arg;
41453
41454 for(var i = 0; i < args.length; i++) {
41455 arg = args[i];
41456
41457 if(arg === gd) copy[i] = arg;
41458 else if(typeof arg === 'object') {
41459 copy[i] = Array.isArray(arg) ?
41460 Lib.extendDeep([], arg) :
41461 Lib.extendDeepAll({}, arg);
41462 } else copy[i] = arg;
41463 }
41464
41465 return copy;
41466}
41467
41468
41469// -----------------------------------------------------
41470// Undo/Redo queue for plots
41471// -----------------------------------------------------
41472
41473
41474var queue = {};
41475
41476// TODO: disable/enable undo and redo buttons appropriately
41477
41478/**
41479 * Add an item to the undoQueue for a graphDiv
41480 *
41481 * @param gd
41482 * @param undoFunc Function undo this operation
41483 * @param undoArgs Args to supply undoFunc with
41484 * @param redoFunc Function to redo this operation
41485 * @param redoArgs Args to supply redoFunc with
41486 */
41487queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) {
41488 var queueObj,
41489 queueIndex;
41490
41491 // make sure we have the queue and our position in it
41492 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
41493 queueIndex = gd.undoQueue.index;
41494
41495 // if we're already playing an undo or redo, or if this is an auto operation
41496 // (like pane resize... any others?) then we don't save this to the undo queue
41497 if(gd.autoplay) {
41498 if(!gd.undoQueue.inSequence) gd.autoplay = false;
41499 return;
41500 }
41501
41502 // if we're not in a sequence or are just starting, we need a new queue item
41503 if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) {
41504 queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}};
41505 gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj);
41506 gd.undoQueue.index += 1;
41507 } else {
41508 queueObj = gd.undoQueue.queue[queueIndex - 1];
41509 }
41510 gd.undoQueue.beginSequence = false;
41511
41512 // we unshift to handle calls for undo in a forward for loop later
41513 if(queueObj) {
41514 queueObj.undo.calls.unshift(undoFunc);
41515 queueObj.undo.args.unshift(undoArgs);
41516 queueObj.redo.calls.push(redoFunc);
41517 queueObj.redo.args.push(redoArgs);
41518 }
41519
41520 if(gd.undoQueue.queue.length > dfltConfig.queueLength) {
41521 gd.undoQueue.queue.shift();
41522 gd.undoQueue.index--;
41523 }
41524};
41525
41526/**
41527 * Begin a sequence of undoQueue changes
41528 *
41529 * @param gd
41530 */
41531queue.startSequence = function(gd) {
41532 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
41533 gd.undoQueue.sequence = true;
41534 gd.undoQueue.beginSequence = true;
41535};
41536
41537/**
41538 * Stop a sequence of undoQueue changes
41539 *
41540 * Call this *after* you're sure your undo chain has ended
41541 *
41542 * @param gd
41543 */
41544queue.stopSequence = function(gd) {
41545 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
41546 gd.undoQueue.sequence = false;
41547 gd.undoQueue.beginSequence = false;
41548};
41549
41550/**
41551 * Move one step back in the undo queue, and undo the object there.
41552 *
41553 * @param gd
41554 */
41555queue.undo = function undo(gd) {
41556 var queueObj, i;
41557
41558 if(gd.framework && gd.framework.isPolar) {
41559 gd.framework.undo();
41560 return;
41561 }
41562 if(gd.undoQueue === undefined ||
41563 isNaN(gd.undoQueue.index) ||
41564 gd.undoQueue.index <= 0) {
41565 return;
41566 }
41567
41568 // index is pointing to next *forward* queueObj, point to the one we're undoing
41569 gd.undoQueue.index--;
41570
41571 // get the queueObj for instructions on how to undo
41572 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
41573
41574 // this sequence keeps things from adding to the queue during undo/redo
41575 gd.undoQueue.inSequence = true;
41576 for(i = 0; i < queueObj.undo.calls.length; i++) {
41577 queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
41578 }
41579 gd.undoQueue.inSequence = false;
41580 gd.autoplay = false;
41581};
41582
41583/**
41584 * Redo the current object in the undo, then move forward in the queue.
41585 *
41586 * @param gd
41587 */
41588queue.redo = function redo(gd) {
41589 var queueObj, i;
41590
41591 if(gd.framework && gd.framework.isPolar) {
41592 gd.framework.redo();
41593 return;
41594 }
41595 if(gd.undoQueue === undefined ||
41596 isNaN(gd.undoQueue.index) ||
41597 gd.undoQueue.index >= gd.undoQueue.queue.length) {
41598 return;
41599 }
41600
41601 // get the queueObj for instructions on how to undo
41602 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
41603
41604 // this sequence keeps things from adding to the queue during undo/redo
41605 gd.undoQueue.inSequence = true;
41606 for(i = 0; i < queueObj.redo.calls.length; i++) {
41607 queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]);
41608 }
41609 gd.undoQueue.inSequence = false;
41610 gd.autoplay = false;
41611
41612 // index is pointing to the thing we just redid, move it
41613 gd.undoQueue.index++;
41614};
41615
41616/**
41617 * Called by undo/redo to make the actual changes.
41618 *
41619 * Not meant to be called publically, but included for mocking out in tests.
41620 *
41621 * @param gd
41622 * @param func
41623 * @param args
41624 */
41625queue.plotDo = function(gd, func, args) {
41626 gd.autoplay = true;
41627
41628 // this *won't* copy gd and it preserves `undefined` properties!
41629 args = copyArgArray(gd, args);
41630
41631 // call the supplied function
41632 func.apply(null, args);
41633};
41634
41635module.exports = queue;
41636
41637},{"../lib":178,"../plot_api/plot_config":210}],193:[function(_dereq_,module,exports){
41638/**
41639* Copyright 2012-2020, Plotly, Inc.
41640* All rights reserved.
41641*
41642* This source code is licensed under the MIT license found in the
41643* LICENSE file in the root directory of this source tree.
41644*/
41645
41646'use strict';
41647
41648/*
41649 * make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10...
41650 *
41651 * @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc.
41652 * 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc
41653 * @param {Optional(string)} tail: a fixed piece after the id
41654 * eg counterRegex('scene', '.annotations') for scene2.annotations etc.
41655 * @param {boolean} openEnded: if true, the string may continue past the match.
41656 * @param {boolean} matchBeginning: if false, the string may start before the match.
41657 */
41658exports.counter = function(head, tail, openEnded, matchBeginning) {
41659 var fullTail = (tail || '') + (openEnded ? '' : '$');
41660 var startWithPrefix = matchBeginning === false ? '' : '^';
41661 if(head === 'xy') {
41662 return new RegExp(startWithPrefix + 'x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail);
41663 }
41664 return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail);
41665};
41666
41667},{}],194:[function(_dereq_,module,exports){
41668/**
41669* Copyright 2012-2020, Plotly, Inc.
41670* All rights reserved.
41671*
41672* This source code is licensed under the MIT license found in the
41673* LICENSE file in the root directory of this source tree.
41674*/
41675
41676
41677'use strict';
41678
41679// ASCEND: chop off the last nesting level - either [<n>] or .<key> - to ascend
41680// the attribute tree. the remaining attrString is in match[1]
41681var ASCEND = /^(.*)(\.[^\.\[\]]+|\[\d\])$/;
41682
41683// SIMPLEATTR: is this an un-nested attribute? (no dots or brackets)
41684var SIMPLEATTR = /^[^\.\[\]]+$/;
41685
41686/*
41687 * calculate a relative attribute string, similar to a relative path
41688 *
41689 * @param {string} baseAttr:
41690 * an attribute string, such as 'annotations[3].x'. The "current location"
41691 * is the attribute string minus the last component ('annotations[3]')
41692 * @param {string} relativeAttr:
41693 * a route to the desired attribute string, using '^' to ascend
41694 *
41695 * @return {string} attrString:
41696 * for example:
41697 * relativeAttr('annotations[3].x', 'y') = 'annotations[3].y'
41698 * relativeAttr('annotations[3].x', '^[2].z') = 'annotations[2].z'
41699 * relativeAttr('annotations[3].x', '^^margin') = 'margin'
41700 * relativeAttr('annotations[3].x', '^^margin.r') = 'margin.r'
41701 */
41702module.exports = function(baseAttr, relativeAttr) {
41703 while(relativeAttr) {
41704 var match = baseAttr.match(ASCEND);
41705
41706 if(match) baseAttr = match[1];
41707 else if(baseAttr.match(SIMPLEATTR)) baseAttr = '';
41708 else throw new Error('bad relativeAttr call:' + [baseAttr, relativeAttr]);
41709
41710 if(relativeAttr.charAt(0) === '^') relativeAttr = relativeAttr.slice(1);
41711 else break;
41712 }
41713
41714 if(baseAttr && relativeAttr.charAt(0) !== '[') {
41715 return baseAttr + '.' + relativeAttr;
41716 }
41717 return baseAttr + relativeAttr;
41718};
41719
41720},{}],195:[function(_dereq_,module,exports){
41721/**
41722* Copyright 2012-2020, Plotly, Inc.
41723* All rights reserved.
41724*
41725* This source code is licensed under the MIT license found in the
41726* LICENSE file in the root directory of this source tree.
41727*/
41728
41729
41730'use strict';
41731
41732var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
41733var isPlainObject = _dereq_('./is_plain_object');
41734
41735/**
41736 * Relink private _keys and keys with a function value from one container
41737 * to the new container.
41738 * Relink means copying if object is pass-by-value and adding a reference
41739 * if object is pass-by-ref.
41740 * This prevents deepCopying massive structures like a webgl context.
41741 */
41742module.exports = function relinkPrivateKeys(toContainer, fromContainer) {
41743 for(var k in fromContainer) {
41744 var fromVal = fromContainer[k];
41745 var toVal = toContainer[k];
41746
41747 if(toVal === fromVal) {
41748 continue;
41749 }
41750 if(k.charAt(0) === '_' || typeof fromVal === 'function') {
41751 // if it already exists at this point, it's something
41752 // that we recreate each time around, so ignore it
41753 if(k in toContainer) continue;
41754
41755 toContainer[k] = fromVal;
41756 } else if(isArrayOrTypedArray(fromVal) && isArrayOrTypedArray(toVal) && isPlainObject(fromVal[0])) {
41757 // filter out data_array items that can contain user objects
41758 // most of the time the toVal === fromVal check will catch these early
41759 // but if the user makes new ones we also don't want to recurse in.
41760 if(k === 'customdata' || k === 'ids') continue;
41761
41762 // recurse into arrays containers
41763 var minLen = Math.min(fromVal.length, toVal.length);
41764 for(var j = 0; j < minLen; j++) {
41765 if((toVal[j] !== fromVal[j]) && isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) {
41766 relinkPrivateKeys(toVal[j], fromVal[j]);
41767 }
41768 }
41769 } else if(isPlainObject(fromVal) && isPlainObject(toVal)) {
41770 // recurse into objects, but only if they still exist
41771 relinkPrivateKeys(toVal, fromVal);
41772
41773 if(!Object.keys(toVal).length) delete toContainer[k];
41774 }
41775 }
41776};
41777
41778},{"./array":165,"./is_plain_object":179}],196:[function(_dereq_,module,exports){
41779/**
41780* Copyright 2012-2020, Plotly, Inc.
41781* All rights reserved.
41782*
41783* This source code is licensed under the MIT license found in the
41784* LICENSE file in the root directory of this source tree.
41785*/
41786
41787
41788'use strict';
41789
41790var isNumeric = _dereq_('fast-isnumeric');
41791var loggers = _dereq_('./loggers');
41792var identity = _dereq_('./identity');
41793
41794// don't trust floating point equality - fraction of bin size to call
41795// "on the line" and ensure that they go the right way specified by
41796// linelow
41797var roundingError = 1e-9;
41798
41799
41800/**
41801 * findBin - find the bin for val - note that it can return outside the
41802 * bin range any pos. or neg. integer for linear bins, or -1 or
41803 * bins.length-1 for explicit.
41804 * bins is either an object {start,size,end} or an array length #bins+1
41805 * bins can be either increasing or decreasing but must be monotonic
41806 * for linear bins, we can just calculate. For listed bins, run a binary
41807 * search linelow (truthy) says the bin boundary should be attributed to
41808 * the lower bin rather than the default upper bin
41809 */
41810exports.findBin = function(val, bins, linelow) {
41811 if(isNumeric(bins.start)) {
41812 return linelow ?
41813 Math.ceil((val - bins.start) / bins.size - roundingError) - 1 :
41814 Math.floor((val - bins.start) / bins.size + roundingError);
41815 } else {
41816 var n1 = 0;
41817 var n2 = bins.length;
41818 var c = 0;
41819 var binSize = (n2 > 1) ? (bins[n2 - 1] - bins[0]) / (n2 - 1) : 1;
41820 var n, test;
41821 if(binSize >= 0) {
41822 test = linelow ? lessThan : lessOrEqual;
41823 } else {
41824 test = linelow ? greaterOrEqual : greaterThan;
41825 }
41826 val += binSize * roundingError * (linelow ? -1 : 1) * (binSize >= 0 ? 1 : -1);
41827 // c is just to avoid infinite loops if there's an error
41828 while(n1 < n2 && c++ < 100) {
41829 n = Math.floor((n1 + n2) / 2);
41830 if(test(bins[n], val)) n1 = n + 1;
41831 else n2 = n;
41832 }
41833 if(c > 90) loggers.log('Long binary search...');
41834 return n1 - 1;
41835 }
41836};
41837
41838function lessThan(a, b) { return a < b; }
41839function lessOrEqual(a, b) { return a <= b; }
41840function greaterThan(a, b) { return a > b; }
41841function greaterOrEqual(a, b) { return a >= b; }
41842
41843exports.sorterAsc = function(a, b) { return a - b; };
41844exports.sorterDes = function(a, b) { return b - a; };
41845
41846/**
41847 * find distinct values in an array, lumping together ones that appear to
41848 * just be off by a rounding error
41849 * return the distinct values and the minimum difference between any two
41850 */
41851exports.distinctVals = function(valsIn) {
41852 var vals = valsIn.slice(); // otherwise we sort the original array...
41853 vals.sort(exports.sorterAsc);
41854
41855 var l = vals.length - 1;
41856 var minDiff = (vals[l] - vals[0]) || 1;
41857 var errDiff = minDiff / (l || 1) / 10000;
41858 var v2 = [vals[0]];
41859
41860 for(var i = 0; i < l; i++) {
41861 // make sure values aren't just off by a rounding error
41862 if(vals[i + 1] > vals[i] + errDiff) {
41863 minDiff = Math.min(minDiff, vals[i + 1] - vals[i]);
41864 v2.push(vals[i + 1]);
41865 }
41866 }
41867
41868 return {vals: v2, minDiff: minDiff};
41869};
41870
41871/**
41872 * return the smallest element from (sorted) array arrayIn that's bigger than val,
41873 * or (reverse) the largest element smaller than val
41874 * used to find the best tick given the minimum (non-rounded) tick
41875 * particularly useful for date/time where things are not powers of 10
41876 * binary search is probably overkill here...
41877 */
41878exports.roundUp = function(val, arrayIn, reverse) {
41879 var low = 0;
41880 var high = arrayIn.length - 1;
41881 var mid;
41882 var c = 0;
41883 var dlow = reverse ? 0 : 1;
41884 var dhigh = reverse ? 1 : 0;
41885 var rounded = reverse ? Math.ceil : Math.floor;
41886 // c is just to avoid infinite loops if there's an error
41887 while(low < high && c++ < 100) {
41888 mid = rounded((low + high) / 2);
41889 if(arrayIn[mid] <= val) low = mid + dlow;
41890 else high = mid - dhigh;
41891 }
41892 return arrayIn[low];
41893};
41894
41895/**
41896 * Tweak to Array.sort(sortFn) that improves performance for pre-sorted arrays
41897 *
41898 * Note that newer browsers (such as Chrome v70+) are starting to pick up
41899 * on pre-sorted arrays which may render the following optimization unnecessary
41900 * in the future.
41901 *
41902 * Motivation: sometimes we need to sort arrays but the input is likely to
41903 * already be sorted. Browsers don't seem to pick up on pre-sorted arrays,
41904 * and in fact Chrome is actually *slower* sorting pre-sorted arrays than purely
41905 * random arrays. FF is at least faster if the array is pre-sorted, but still
41906 * not as fast as it could be.
41907 * Here's how this plays out sorting a length-1e6 array:
41908 *
41909 * Calls to Sort FN | Chrome bare | FF bare | Chrome tweak | FF tweak
41910 * | v68.0 Mac | v61.0 Mac| |
41911 * ------------------+---------------+-----------+----------------+------------
41912 * ordered | 30.4e6 | 10.1e6 | 1e6 | 1e6
41913 * reversed | 29.4e6 | 9.9e6 | 1e6 + reverse | 1e6 + reverse
41914 * random | ~21e6 | ~18.7e6 | ~21e6 | ~18.7e6
41915 *
41916 * So this is a substantial win for pre-sorted (ordered or exactly reversed)
41917 * arrays. Including this wrapper on an unsorted array adds a penalty that will
41918 * in general be only a few calls to the sort function. The only case this
41919 * penalty will be significant is if the array is mostly sorted but there are
41920 * a few unsorted items near the end, but the penalty is still at most N calls
41921 * out of (for N=1e6) ~20N total calls
41922 *
41923 * @param {Array} array: the array, to be sorted in place
41924 * @param {function} sortFn: As in Array.sort, function(a, b) that puts
41925 * item a before item b if the return is negative, a after b if positive,
41926 * and no change if zero.
41927 * @return {Array}: the original array, sorted in place.
41928 */
41929exports.sort = function(array, sortFn) {
41930 var notOrdered = 0;
41931 var notReversed = 0;
41932 for(var i = 1; i < array.length; i++) {
41933 var pairOrder = sortFn(array[i], array[i - 1]);
41934 if(pairOrder < 0) notOrdered = 1;
41935 else if(pairOrder > 0) notReversed = 1;
41936 if(notOrdered && notReversed) return array.sort(sortFn);
41937 }
41938 return notReversed ? array : array.reverse();
41939};
41940
41941/**
41942 * find index in array 'arr' that minimizes 'fn'
41943 *
41944 * @param {array} arr : array where to search
41945 * @param {fn (optional)} fn : function to minimize,
41946 * if not given, fn is the identity function
41947 * @return {integer}
41948 */
41949exports.findIndexOfMin = function(arr, fn) {
41950 fn = fn || identity;
41951
41952 var min = Infinity;
41953 var ind;
41954
41955 for(var i = 0; i < arr.length; i++) {
41956 var v = fn(arr[i]);
41957 if(v < min) {
41958 min = v;
41959 ind = i;
41960 }
41961 }
41962 return ind;
41963};
41964
41965},{"./identity":177,"./loggers":182,"fast-isnumeric":18}],197:[function(_dereq_,module,exports){
41966/**
41967* Copyright 2012-2020, Plotly, Inc.
41968* All rights reserved.
41969*
41970* This source code is licensed under the MIT license found in the
41971* LICENSE file in the root directory of this source tree.
41972*/
41973
41974
41975'use strict';
41976
41977// works with our CSS cursor classes (see css/_cursor.scss)
41978// to apply cursors to d3 single-element selections.
41979// omit cursor to revert to the default.
41980module.exports = function setCursor(el3, csr) {
41981 (el3.attr('class') || '').split(' ').forEach(function(cls) {
41982 if(cls.indexOf('cursor-') === 0) el3.classed(cls, false);
41983 });
41984
41985 if(csr) el3.classed('cursor-' + csr, true);
41986};
41987
41988},{}],198:[function(_dereq_,module,exports){
41989/**
41990* Copyright 2012-2020, Plotly, Inc.
41991* All rights reserved.
41992*
41993* This source code is licensed under the MIT license found in the
41994* LICENSE file in the root directory of this source tree.
41995*/
41996
41997
41998'use strict';
41999
42000var isNumeric = _dereq_('fast-isnumeric');
42001var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
42002
42003/**
42004 * aggNums() returns the result of an aggregate function applied to an array of
42005 * values, where non-numerical values have been tossed out.
42006 *
42007 * @param {function} f - aggregation function (e.g., Math.min)
42008 * @param {Number} v - initial value (continuing from previous calls)
42009 * if there's no continuing value, use null for selector-type
42010 * functions (max,min), or 0 for summations
42011 * @param {Array} a - array to aggregate (may be nested, we will recurse,
42012 * but all elements must have the same dimension)
42013 * @param {Number} len - maximum length of a to aggregate
42014 * @return {Number} - result of f applied to a starting from v
42015 */
42016exports.aggNums = function(f, v, a, len) {
42017 var i,
42018 b;
42019 if(!len || len > a.length) len = a.length;
42020 if(!isNumeric(v)) v = false;
42021 if(isArrayOrTypedArray(a[0])) {
42022 b = new Array(len);
42023 for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
42024 a = b;
42025 }
42026
42027 for(i = 0; i < len; i++) {
42028 if(!isNumeric(v)) v = a[i];
42029 else if(isNumeric(a[i])) v = f(+v, +a[i]);
42030 }
42031 return v;
42032};
42033
42034/**
42035 * mean & std dev functions using aggNums, so it handles non-numerics nicely
42036 * even need to use aggNums instead of .length, to toss out non-numerics
42037 */
42038exports.len = function(data) {
42039 return exports.aggNums(function(a) { return a + 1; }, 0, data);
42040};
42041
42042exports.mean = function(data, len) {
42043 if(!len) len = exports.len(data);
42044 return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
42045};
42046
42047exports.midRange = function(numArr) {
42048 if(numArr === undefined || numArr.length === 0) return undefined;
42049 return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2;
42050};
42051
42052exports.variance = function(data, len, mean) {
42053 if(!len) len = exports.len(data);
42054 if(!isNumeric(mean)) mean = exports.mean(data, len);
42055
42056 return exports.aggNums(function(a, b) {
42057 return a + Math.pow(b - mean, 2);
42058 }, 0, data) / len;
42059};
42060
42061exports.stdev = function(data, len, mean) {
42062 return Math.sqrt(exports.variance(data, len, mean));
42063};
42064
42065/**
42066 * median of a finite set of numbers
42067 * reference page: https://en.wikipedia.org/wiki/Median#Finite_set_of_numbers
42068**/
42069exports.median = function(data) {
42070 var b = data.slice().sort();
42071 return exports.interp(b, 0.5);
42072};
42073
42074/**
42075 * interp() computes a percentile (quantile) for a given distribution.
42076 * We interpolate the distribution (to compute quantiles, we follow method #10 here:
42077 * http://www.amstat.org/publications/jse/v14n3/langford.html).
42078 * Typically the index or rank (n * arr.length) may be non-integer.
42079 * For reference: ends are clipped to the extreme values in the array;
42080 * For box plots: index you get is half a point too high (see
42081 * http://en.wikipedia.org/wiki/Percentile#Nearest_rank) but note that this definition
42082 * indexes from 1 rather than 0, so we subtract 1/2 (instead of add).
42083 *
42084 * @param {Array} arr - This array contains the values that make up the distribution.
42085 * @param {Number} n - Between 0 and 1, n = p/100 is such that we compute the p^th percentile.
42086 * For example, the 50th percentile (or median) corresponds to n = 0.5
42087 * @return {Number} - percentile
42088 */
42089exports.interp = function(arr, n) {
42090 if(!isNumeric(n)) throw 'n should be a finite number';
42091 n = n * arr.length - 0.5;
42092 if(n < 0) return arr[0];
42093 if(n > arr.length - 1) return arr[arr.length - 1];
42094 var frac = n % 1;
42095 return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
42096};
42097
42098},{"./array":165,"fast-isnumeric":18}],199:[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
42108'use strict';
42109
42110/* global MathJax:false */
42111
42112var d3 = _dereq_('d3');
42113
42114var Lib = _dereq_('../lib');
42115var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
42116var LINE_SPACING = _dereq_('../constants/alignment').LINE_SPACING;
42117
42118// text converter
42119
42120function getSize(_selection, _dimension) {
42121 return _selection.node().getBoundingClientRect()[_dimension];
42122}
42123
42124var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
42125
42126exports.convertToTspans = function(_context, gd, _callback) {
42127 var str = _context.text();
42128
42129 // Until we get tex integrated more fully (so it can be used along with non-tex)
42130 // allow some elements to prohibit it by attaching 'data-notex' to the original
42131 var tex = (!_context.attr('data-notex')) &&
42132 (typeof MathJax !== 'undefined') &&
42133 str.match(FIND_TEX);
42134
42135 var parent = d3.select(_context.node().parentNode);
42136 if(parent.empty()) return;
42137 var svgClass = (_context.attr('class')) ? _context.attr('class').split(' ')[0] : 'text';
42138 svgClass += '-math';
42139 parent.selectAll('svg.' + svgClass).remove();
42140 parent.selectAll('g.' + svgClass + '-group').remove();
42141 _context.style('display', null)
42142 .attr({
42143 // some callers use data-unformatted *from the <text> element* in 'cancel'
42144 // so we need it here even if we're going to turn it into math
42145 // these two (plus style and text-anchor attributes) form the key we're
42146 // going to use for Drawing.bBox
42147 'data-unformatted': str,
42148 'data-math': 'N'
42149 });
42150
42151 function showText() {
42152 if(!parent.empty()) {
42153 svgClass = _context.attr('class') + '-math';
42154 parent.select('svg.' + svgClass).remove();
42155 }
42156 _context.text('')
42157 .style('white-space', 'pre');
42158
42159 var hasLink = buildSVGText(_context.node(), str);
42160
42161 if(hasLink) {
42162 // at least in Chrome, pointer-events does not seem
42163 // to be honored in children of <text> elements
42164 // so if we have an anchor, we have to make the
42165 // whole element respond
42166 _context.style('pointer-events', 'all');
42167 }
42168
42169 exports.positionText(_context);
42170
42171 if(_callback) _callback.call(_context);
42172 }
42173
42174 if(tex) {
42175 ((gd && gd._promises) || []).push(new Promise(function(resolve) {
42176 _context.style('display', 'none');
42177 var fontSize = parseInt(_context.node().style.fontSize, 10);
42178 var config = {fontSize: fontSize};
42179
42180 texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
42181 parent.selectAll('svg.' + svgClass).remove();
42182 parent.selectAll('g.' + svgClass + '-group').remove();
42183
42184 var newSvg = _svgEl && _svgEl.select('svg');
42185 if(!newSvg || !newSvg.node()) {
42186 showText();
42187 resolve();
42188 return;
42189 }
42190
42191 var mathjaxGroup = parent.append('g')
42192 .classed(svgClass + '-group', true)
42193 .attr({
42194 'pointer-events': 'none',
42195 'data-unformatted': str,
42196 'data-math': 'Y'
42197 });
42198
42199 mathjaxGroup.node().appendChild(newSvg.node());
42200
42201 // stitch the glyph defs
42202 if(_glyphDefs && _glyphDefs.node()) {
42203 newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true),
42204 newSvg.node().firstChild);
42205 }
42206
42207 newSvg.attr({
42208 'class': svgClass,
42209 height: _svgBBox.height,
42210 preserveAspectRatio: 'xMinYMin meet'
42211 })
42212 .style({overflow: 'visible', 'pointer-events': 'none'});
42213
42214 var fill = _context.node().style.fill || 'black';
42215 var g = newSvg.select('g');
42216 g.attr({fill: fill, stroke: fill});
42217
42218 var newSvgW = getSize(g, 'width');
42219 var newSvgH = getSize(g, 'height');
42220 var newX = +_context.attr('x') - newSvgW *
42221 {start: 0, middle: 0.5, end: 1}[_context.attr('text-anchor') || 'start'];
42222 // font baseline is about 1/4 fontSize below centerline
42223 var textHeight = fontSize || getSize(_context, 'height');
42224 var dy = -textHeight / 4;
42225
42226 if(svgClass[0] === 'y') {
42227 mathjaxGroup.attr({
42228 transform: 'rotate(' + [-90, +_context.attr('x'), +_context.attr('y')] +
42229 ') translate(' + [-newSvgW / 2, dy - newSvgH / 2] + ')'
42230 });
42231 newSvg.attr({x: +_context.attr('x'), y: +_context.attr('y')});
42232 } else if(svgClass[0] === 'l') {
42233 newSvg.attr({x: _context.attr('x'), y: dy - (newSvgH / 2)});
42234 } else if(svgClass[0] === 'a' && svgClass.indexOf('atitle') !== 0) {
42235 newSvg.attr({x: 0, y: dy});
42236 } else {
42237 newSvg.attr({x: newX, y: (+_context.attr('y') + dy - newSvgH / 2)});
42238 }
42239
42240 if(_callback) _callback.call(_context, mathjaxGroup);
42241 resolve(mathjaxGroup);
42242 });
42243 }));
42244 } else showText();
42245
42246 return _context;
42247};
42248
42249
42250// MathJax
42251
42252var LT_MATCH = /(<|&lt;|&#60;)/g;
42253var GT_MATCH = /(>|&gt;|&#62;)/g;
42254
42255function cleanEscapesForTex(s) {
42256 return s.replace(LT_MATCH, '\\lt ')
42257 .replace(GT_MATCH, '\\gt ');
42258}
42259
42260function texToSVG(_texString, _config, _callback) {
42261 var originalRenderer,
42262 originalConfig,
42263 originalProcessSectionDelay,
42264 tmpDiv;
42265
42266 MathJax.Hub.Queue(
42267 function() {
42268 originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config);
42269
42270 originalProcessSectionDelay = MathJax.Hub.processSectionDelay;
42271 if(MathJax.Hub.processSectionDelay !== undefined) {
42272 // MathJax 2.5+
42273 MathJax.Hub.processSectionDelay = 0;
42274 }
42275
42276 return MathJax.Hub.Config({
42277 messageStyle: 'none',
42278 tex2jax: {
42279 inlineMath: [['$', '$'], ['\\(', '\\)']]
42280 },
42281 displayAlign: 'left',
42282 });
42283 },
42284 function() {
42285 // Get original renderer
42286 originalRenderer = MathJax.Hub.config.menuSettings.renderer;
42287 if(originalRenderer !== 'SVG') {
42288 return MathJax.Hub.setRenderer('SVG');
42289 }
42290 },
42291 function() {
42292 var randomID = 'math-output-' + Lib.randstr({}, 64);
42293 tmpDiv = d3.select('body').append('div')
42294 .attr({id: randomID})
42295 .style({visibility: 'hidden', position: 'absolute'})
42296 .style({'font-size': _config.fontSize + 'px'})
42297 .text(cleanEscapesForTex(_texString));
42298
42299 return MathJax.Hub.Typeset(tmpDiv.node());
42300 },
42301 function() {
42302 var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs');
42303
42304 if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) {
42305 Lib.log('There was an error in the tex syntax.', _texString);
42306 _callback();
42307 } else {
42308 var svgBBox = tmpDiv.select('svg').node().getBoundingClientRect();
42309 _callback(tmpDiv.select('.MathJax_SVG'), glyphDefs, svgBBox);
42310 }
42311
42312 tmpDiv.remove();
42313
42314 if(originalRenderer !== 'SVG') {
42315 return MathJax.Hub.setRenderer(originalRenderer);
42316 }
42317 },
42318 function() {
42319 if(originalProcessSectionDelay !== undefined) {
42320 MathJax.Hub.processSectionDelay = originalProcessSectionDelay;
42321 }
42322 return MathJax.Hub.Config(originalConfig);
42323 });
42324}
42325
42326var TAG_STYLES = {
42327 // would like to use baseline-shift for sub/sup but FF doesn't support it
42328 // so we need to use dy along with the uber hacky shift-back-to
42329 // baseline below
42330 sup: 'font-size:70%',
42331 sub: 'font-size:70%',
42332 b: 'font-weight:bold',
42333 i: 'font-style:italic',
42334 a: 'cursor:pointer',
42335 span: '',
42336 em: 'font-style:italic;font-weight:bold'
42337};
42338
42339// baseline shifts for sub and sup
42340var SHIFT_DY = {
42341 sub: '0.3em',
42342 sup: '-0.6em'
42343};
42344// reset baseline by adding a tspan (empty except for a zero-width space)
42345// with dy of -70% * SHIFT_DY (because font-size=70%)
42346var RESET_DY = {
42347 sub: '-0.21em',
42348 sup: '0.42em'
42349};
42350var ZERO_WIDTH_SPACE = '\u200b';
42351
42352/*
42353 * Whitelist of protocols in user-supplied urls. Mostly we want to avoid javascript
42354 * and related attack vectors. The empty items are there for IE, that in various
42355 * versions treats relative paths as having different flavors of no protocol, while
42356 * other browsers have these explicitly inherit the protocol of the page they're in.
42357 */
42358var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':'];
42359
42360var NEWLINES = exports.NEWLINES = /(\r\n?|\n)/g;
42361
42362var SPLIT_TAGS = /(<[^<>]*>)/;
42363
42364var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i;
42365
42366var BR_TAG = /<br(\s+.*)?>/i;
42367exports.BR_TAG_ALL = /<br(\s+.*)?>/gi;
42368
42369/*
42370 * style and href: pull them out of either single or double quotes. Also
42371 * - target: (_blank|_self|_parent|_top|framename)
42372 * note that you can't use target to get a popup but if you use popup,
42373 * a `framename` will be passed along as the name of the popup window.
42374 * per the spec, cannot contain whitespace.
42375 * for backward compatibility we default to '_blank'
42376 * - popup: a custom one for us to enable popup (new window) links. String
42377 * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550'
42378 * note that at least in Chrome, you need to give at least one property
42379 * in this string or the page will open in a new tab anyway. We follow this
42380 * convention and will not make a popup if this string is empty.
42381 * per the spec, cannot contain whitespace.
42382 *
42383 * Because we hack in other attributes with style (sub & sup), drop any trailing
42384 * semicolon in user-supplied styles so we can consistently append the tag-dependent style
42385 *
42386 * These are for tag attributes; Chrome anyway will convert entities in
42387 * attribute values, but not in attribute names
42388 * you can test this by for example:
42389 * > p = document.createElement('p')
42390 * > p.innerHTML = '<span styl&#x65;="font-color:r&#x65;d;">Hi</span>'
42391 * > p.innerHTML
42392 * <- '<span styl&#x65;="font-color:red;">Hi</span>'
42393 */
42394var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i;
42395var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i;
42396var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i;
42397var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i;
42398
42399// dedicated matcher for these quoted regexes, that can return their results
42400// in two different places
42401function getQuotedMatch(_str, re) {
42402 if(!_str) return null;
42403 var match = _str.match(re);
42404 var result = match && (match[3] || match[4]);
42405 return result && convertEntities(result);
42406}
42407
42408var COLORMATCH = /(^|;)\s*color:/;
42409
42410/**
42411 * Strip string of tags
42412 *
42413 * @param {string} _str : input string
42414 * @param {object} opts :
42415 * - len {number} max length of output string
42416 * - allowedTags {array} list of pseudo-html tags to NOT strip
42417 * @return {string}
42418 */
42419exports.plainText = function(_str, opts) {
42420 opts = opts || {};
42421
42422 var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity;
42423 var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br'];
42424
42425 var ellipsis = '...';
42426 var eLen = ellipsis.length;
42427
42428 var oldParts = _str.split(SPLIT_TAGS);
42429 var newParts = [];
42430 var prevTag = '';
42431 var l = 0;
42432
42433 for(var i = 0; i < oldParts.length; i++) {
42434 var p = oldParts[i];
42435 var match = p.match(ONE_TAG);
42436 var tagType = match && match[2].toLowerCase();
42437
42438 if(tagType) {
42439 // N.B. tags do not count towards string length
42440 if(allowedTags.indexOf(tagType) !== -1) {
42441 newParts.push(p);
42442 prevTag = tagType;
42443 }
42444 } else {
42445 var pLen = p.length;
42446
42447 if((l + pLen) < len) {
42448 newParts.push(p);
42449 l += pLen;
42450 } else if(l < len) {
42451 var pLen2 = len - l;
42452
42453 if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) {
42454 newParts.pop();
42455 }
42456
42457 if(len > eLen) {
42458 newParts.push(p.substr(0, pLen2 - eLen) + ellipsis);
42459 } else {
42460 newParts.push(p.substr(0, pLen2));
42461 }
42462 break;
42463 }
42464
42465 prevTag = '';
42466 }
42467 }
42468
42469 return newParts.join('');
42470};
42471
42472/*
42473 * N.B. HTML entities are listed without the leading '&' and trailing ';'
42474 * https://www.freeformatter.com/html-entities.html
42475 *
42476 * FWIW if we wanted to support the full set, it has 2261 entries:
42477 * https://www.w3.org/TR/html5/entities.json
42478 * though I notice that some of these are duplicates and/or are missing ";"
42479 * eg: "&amp;", "&amp", "&AMP;", and "&AMP" all map to "&"
42480 * We no longer need to include numeric entities here, these are now handled
42481 * by String.fromCodePoint/fromCharCode
42482 *
42483 * Anyway the only ones that are really important to allow are the HTML special
42484 * chars <, >, and &, because these ones can trigger special processing if not
42485 * replaced by the corresponding entity.
42486 */
42487var entityToUnicode = {
42488 mu: 'μ',
42489 amp: '&',
42490 lt: '<',
42491 gt: '>',
42492 nbsp: ' ',
42493 times: '×',
42494 plusmn: '±',
42495 deg: '°'
42496};
42497
42498// NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the
42499// ones we support use only lowercase. If we ever change that, update the regex.
42500var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g;
42501function convertEntities(_str) {
42502 return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) {
42503 var outChar;
42504 if(innerMatch.charAt(0) === '#') {
42505 // cannot use String.fromCodePoint in IE
42506 outChar = fromCodePoint(
42507 innerMatch.charAt(1) === 'x' ?
42508 parseInt(innerMatch.substr(2), 16) :
42509 parseInt(innerMatch.substr(1), 10)
42510 );
42511 } else outChar = entityToUnicode[innerMatch];
42512
42513 // as in regular HTML, if we didn't decode the entity just
42514 // leave the raw text in place.
42515 return outChar || fullMatch;
42516 });
42517}
42518exports.convertEntities = convertEntities;
42519
42520function fromCodePoint(code) {
42521 // Don't allow overflow. In Chrome this turns into � but I feel like it's
42522 // more useful to just not convert it at all.
42523 if(code > 0x10FFFF) return;
42524 var stringFromCodePoint = String.fromCodePoint;
42525 if(stringFromCodePoint) return stringFromCodePoint(code);
42526
42527 // IE doesn't have String.fromCodePoint
42528 // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
42529 var stringFromCharCode = String.fromCharCode;
42530 if(code <= 0xFFFF) return stringFromCharCode(code);
42531 return stringFromCharCode(
42532 (code >> 10) + 0xD7C0,
42533 (code % 0x400) + 0xDC00
42534 );
42535}
42536
42537/*
42538 * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these
42539 * to containerNode
42540 *
42541 * @param {svg text element} containerNode: the <text> node to insert this text into
42542 * @param {string} str: the pseudo-html string to convert to svg
42543 *
42544 * @returns {bool}: does the result contain any links? We need to handle the text element
42545 * somewhat differently if it does, so just keep track of this when it happens.
42546 */
42547function buildSVGText(containerNode, str) {
42548 /*
42549 * Normalize behavior between IE and others wrt newlines and whitespace:pre
42550 * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
42551 * Chrome and FF display \n, \r, or \r\n as a space in this mode.
42552 * I feel like at some point we turned these into <br> but currently we don't so
42553 * I'm just going to cement what we do now in Chrome and FF
42554 */
42555 str = str.replace(NEWLINES, ' ');
42556
42557 var hasLink = false;
42558
42559 // as we're building the text, keep track of what elements we're nested inside
42560 // nodeStack will be an array of {node, type, style, href, target, popup}
42561 // where only type: 'a' gets the last 3 and node is only added when it's created
42562 var nodeStack = [];
42563 var currentNode;
42564 var currentLine = -1;
42565
42566 function newLine() {
42567 currentLine++;
42568
42569 var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
42570 d3.select(lineNode).attr({
42571 class: 'line',
42572 dy: (currentLine * LINE_SPACING) + 'em'
42573 });
42574 containerNode.appendChild(lineNode);
42575
42576 currentNode = lineNode;
42577
42578 var oldNodeStack = nodeStack;
42579 nodeStack = [{node: lineNode}];
42580
42581 if(oldNodeStack.length > 1) {
42582 for(var i = 1; i < oldNodeStack.length; i++) {
42583 enterNode(oldNodeStack[i]);
42584 }
42585 }
42586 }
42587
42588 function enterNode(nodeSpec) {
42589 var type = nodeSpec.type;
42590 var nodeAttrs = {};
42591 var nodeType;
42592
42593 if(type === 'a') {
42594 nodeType = 'a';
42595 var target = nodeSpec.target;
42596 var href = nodeSpec.href;
42597 var popup = nodeSpec.popup;
42598 if(href) {
42599 nodeAttrs = {
42600 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace',
42601 target: target,
42602 'xlink:xlink:href': href
42603 };
42604 if(popup) {
42605 // security: href and target are not inserted as code but
42606 // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/
42607 nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' +
42608 popup + '");return false;';
42609 }
42610 }
42611 } else nodeType = 'tspan';
42612
42613 if(nodeSpec.style) nodeAttrs.style = nodeSpec.style;
42614
42615 var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType);
42616
42617 if(type === 'sup' || type === 'sub') {
42618 addTextNode(currentNode, ZERO_WIDTH_SPACE);
42619 currentNode.appendChild(newNode);
42620
42621 var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
42622 addTextNode(resetter, ZERO_WIDTH_SPACE);
42623 d3.select(resetter).attr('dy', RESET_DY[type]);
42624 nodeAttrs.dy = SHIFT_DY[type];
42625
42626 currentNode.appendChild(newNode);
42627 currentNode.appendChild(resetter);
42628 } else {
42629 currentNode.appendChild(newNode);
42630 }
42631
42632 d3.select(newNode).attr(nodeAttrs);
42633
42634 currentNode = nodeSpec.node = newNode;
42635 nodeStack.push(nodeSpec);
42636 }
42637
42638 function addTextNode(node, text) {
42639 node.appendChild(document.createTextNode(text));
42640 }
42641
42642 function exitNode(type) {
42643 // A bare closing tag can't close the root node. If we encounter this it
42644 // means there's an extra closing tag that can just be ignored:
42645 if(nodeStack.length === 1) {
42646 Lib.log('Ignoring unexpected end tag </' + type + '>.', str);
42647 return;
42648 }
42649
42650 var innerNode = nodeStack.pop();
42651
42652 if(type !== innerNode.type) {
42653 Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' +
42654 type + '>. Pretending it did match.', str);
42655 }
42656 currentNode = nodeStack[nodeStack.length - 1].node;
42657 }
42658
42659 var hasLines = BR_TAG.test(str);
42660
42661 if(hasLines) newLine();
42662 else {
42663 currentNode = containerNode;
42664 nodeStack = [{node: containerNode}];
42665 }
42666
42667 var parts = str.split(SPLIT_TAGS);
42668 for(var i = 0; i < parts.length; i++) {
42669 var parti = parts[i];
42670 var match = parti.match(ONE_TAG);
42671 var tagType = match && match[2].toLowerCase();
42672 var tagStyle = TAG_STYLES[tagType];
42673
42674 if(tagType === 'br') {
42675 newLine();
42676 } else if(tagStyle === undefined) {
42677 addTextNode(currentNode, convertEntities(parti));
42678 } else {
42679 // tag - open or close
42680 if(match[1]) {
42681 exitNode(tagType);
42682 } else {
42683 var extra = match[4];
42684
42685 var nodeSpec = {type: tagType};
42686
42687 // now add style, from both the tag name and any extra css
42688 // Most of the svg css that users will care about is just like html,
42689 // but font color is different (uses fill). Let our users ignore this.
42690 var css = getQuotedMatch(extra, STYLEMATCH);
42691 if(css) {
42692 css = css.replace(COLORMATCH, '$1 fill:');
42693 if(tagStyle) css += ';' + tagStyle;
42694 } else if(tagStyle) css = tagStyle;
42695
42696 if(css) nodeSpec.style = css;
42697
42698 if(tagType === 'a') {
42699 hasLink = true;
42700
42701 var href = getQuotedMatch(extra, HREFMATCH);
42702
42703 if(href) {
42704 // check safe protocols
42705 var dummyAnchor = document.createElement('a');
42706 dummyAnchor.href = href;
42707 if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
42708 // Decode href to allow both already encoded and not encoded
42709 // URIs. Without decoding prior encoding, an already encoded
42710 // URI would be encoded twice producing a semantically different URI.
42711 nodeSpec.href = encodeURI(decodeURI(href));
42712 nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank';
42713 nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH);
42714 }
42715 }
42716 }
42717
42718 enterNode(nodeSpec);
42719 }
42720 }
42721 }
42722
42723 return hasLink;
42724}
42725
42726/*
42727 * sanitizeHTML: port of buildSVGText aimed at providing a clean subset of HTML
42728 * @param {string} str: the html string to clean
42729 * @returns {string}: a cleaned and normalized version of the input,
42730 * supporting only a small subset of html
42731 */
42732exports.sanitizeHTML = function sanitizeHTML(str) {
42733 str = str.replace(NEWLINES, ' ');
42734
42735 var rootNode = document.createElement('p');
42736 var currentNode = rootNode;
42737 var nodeStack = [];
42738
42739 var parts = str.split(SPLIT_TAGS);
42740 for(var i = 0; i < parts.length; i++) {
42741 var parti = parts[i];
42742 var match = parti.match(ONE_TAG);
42743 var tagType = match && match[2].toLowerCase();
42744
42745 if(tagType in TAG_STYLES) {
42746 if(match[1]) {
42747 if(nodeStack.length) {
42748 currentNode = nodeStack.pop();
42749 }
42750 } else {
42751 var extra = match[4];
42752
42753 var css = getQuotedMatch(extra, STYLEMATCH);
42754 var nodeAttrs = css ? {style: css} : {};
42755
42756 if(tagType === 'a') {
42757 var href = getQuotedMatch(extra, HREFMATCH);
42758
42759 if(href) {
42760 var dummyAnchor = document.createElement('a');
42761 dummyAnchor.href = href;
42762 if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
42763 nodeAttrs.href = encodeURI(decodeURI(href));
42764 var target = getQuotedMatch(extra, TARGETMATCH);
42765 if(target) {
42766 nodeAttrs.target = target;
42767 }
42768 }
42769 }
42770 }
42771
42772 var newNode = document.createElement(tagType);
42773 currentNode.appendChild(newNode);
42774 d3.select(newNode).attr(nodeAttrs);
42775
42776 currentNode = newNode;
42777 nodeStack.push(newNode);
42778 }
42779 } else {
42780 currentNode.appendChild(
42781 document.createTextNode(convertEntities(parti))
42782 );
42783 }
42784 }
42785 var key = 'innerHTML'; // i.e. to avoid pass test-syntax
42786 return rootNode[key];
42787};
42788
42789exports.lineCount = function lineCount(s) {
42790 return s.selectAll('tspan.line').size() || 1;
42791};
42792
42793exports.positionText = function positionText(s, x, y) {
42794 return s.each(function() {
42795 var text = d3.select(this);
42796
42797 function setOrGet(attr, val) {
42798 if(val === undefined) {
42799 val = text.attr(attr);
42800 if(val === null) {
42801 text.attr(attr, 0);
42802 val = 0;
42803 }
42804 } else text.attr(attr, val);
42805 return val;
42806 }
42807
42808 var thisX = setOrGet('x', x);
42809 var thisY = setOrGet('y', y);
42810
42811 if(this.nodeName === 'text') {
42812 text.selectAll('tspan.line').attr({x: thisX, y: thisY});
42813 }
42814 });
42815};
42816
42817function alignHTMLWith(_base, container, options) {
42818 var alignH = options.horizontalAlign;
42819 var alignV = options.verticalAlign || 'top';
42820 var bRect = _base.node().getBoundingClientRect();
42821 var cRect = container.node().getBoundingClientRect();
42822 var thisRect;
42823 var getTop;
42824 var getLeft;
42825
42826 if(alignV === 'bottom') {
42827 getTop = function() { return bRect.bottom - thisRect.height; };
42828 } else if(alignV === 'middle') {
42829 getTop = function() { return bRect.top + (bRect.height - thisRect.height) / 2; };
42830 } else { // default: top
42831 getTop = function() { return bRect.top; };
42832 }
42833
42834 if(alignH === 'right') {
42835 getLeft = function() { return bRect.right - thisRect.width; };
42836 } else if(alignH === 'center') {
42837 getLeft = function() { return bRect.left + (bRect.width - thisRect.width) / 2; };
42838 } else { // default: left
42839 getLeft = function() { return bRect.left; };
42840 }
42841
42842 return function() {
42843 thisRect = this.node().getBoundingClientRect();
42844 this.style({
42845 top: (getTop() - cRect.top) + 'px',
42846 left: (getLeft() - cRect.left) + 'px',
42847 'z-index': 1000
42848 });
42849 return this;
42850 };
42851}
42852
42853/*
42854 * Editable title
42855 * @param {d3.selection} context: the element being edited. Normally text,
42856 * but if it isn't, you should provide the styling options
42857 * @param {object} options:
42858 * @param {div} options.gd: graphDiv
42859 * @param {d3.selection} options.delegate: item to bind events to if not this
42860 * @param {boolean} options.immediate: start editing now (true) or on click (false, default)
42861 * @param {string} options.fill: font color if not as shown
42862 * @param {string} options.background: background color if not as shown
42863 * @param {string} options.text: initial text, if not as shown
42864 * @param {string} options.horizontalAlign: alignment of the edit box wrt. the bound element
42865 * @param {string} options.verticalAlign: alignment of the edit box wrt. the bound element
42866 */
42867
42868exports.makeEditable = function(context, options) {
42869 var gd = options.gd;
42870 var _delegate = options.delegate;
42871 var dispatch = d3.dispatch('edit', 'input', 'cancel');
42872 var handlerElement = _delegate || context;
42873
42874 context.style({'pointer-events': _delegate ? 'none' : 'all'});
42875
42876 if(context.size() !== 1) throw new Error('boo');
42877
42878 function handleClick() {
42879 appendEditable();
42880 context.style({opacity: 0});
42881 // also hide any mathjax svg
42882 var svgClass = handlerElement.attr('class');
42883 var mathjaxClass;
42884 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
42885 else mathjaxClass = '[class*=-math-group]';
42886 if(mathjaxClass) {
42887 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
42888 }
42889 }
42890
42891 function selectElementContents(_el) {
42892 var el = _el.node();
42893 var range = document.createRange();
42894 range.selectNodeContents(el);
42895 var sel = window.getSelection();
42896 sel.removeAllRanges();
42897 sel.addRange(range);
42898 el.focus();
42899 }
42900
42901 function appendEditable() {
42902 var plotDiv = d3.select(gd);
42903 var container = plotDiv.select('.svg-container');
42904 var div = container.append('div');
42905 var cStyle = context.node().style;
42906 var fontSize = parseFloat(cStyle.fontSize || 12);
42907
42908 var initialText = options.text;
42909 if(initialText === undefined) initialText = context.attr('data-unformatted');
42910
42911 div.classed('plugin-editable editable', true)
42912 .style({
42913 position: 'absolute',
42914 'font-family': cStyle.fontFamily || 'Arial',
42915 'font-size': fontSize,
42916 color: options.fill || cStyle.fill || 'black',
42917 opacity: 1,
42918 'background-color': options.background || 'transparent',
42919 outline: '#ffffff33 1px solid',
42920 margin: [-fontSize / 8 + 1, 0, 0, -1].join('px ') + 'px',
42921 padding: '0',
42922 'box-sizing': 'border-box'
42923 })
42924 .attr({contenteditable: true})
42925 .text(initialText)
42926 .call(alignHTMLWith(context, container, options))
42927 .on('blur', function() {
42928 gd._editing = false;
42929 context.text(this.textContent)
42930 .style({opacity: 1});
42931 var svgClass = d3.select(this).attr('class');
42932 var mathjaxClass;
42933 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
42934 else mathjaxClass = '[class*=-math-group]';
42935 if(mathjaxClass) {
42936 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
42937 }
42938 var text = this.textContent;
42939 d3.select(this).transition().duration(0).remove();
42940 d3.select(document).on('mouseup', null);
42941 dispatch.edit.call(context, text);
42942 })
42943 .on('focus', function() {
42944 var editDiv = this;
42945 gd._editing = true;
42946 d3.select(document).on('mouseup', function() {
42947 if(d3.event.target === editDiv) return false;
42948 if(document.activeElement === div.node()) div.node().blur();
42949 });
42950 })
42951 .on('keyup', function() {
42952 if(d3.event.which === 27) {
42953 gd._editing = false;
42954 context.style({opacity: 1});
42955 d3.select(this)
42956 .style({opacity: 0})
42957 .on('blur', function() { return false; })
42958 .transition().remove();
42959 dispatch.cancel.call(context, this.textContent);
42960 } else {
42961 dispatch.input.call(context, this.textContent);
42962 d3.select(this).call(alignHTMLWith(context, container, options));
42963 }
42964 })
42965 .on('keydown', function() {
42966 if(d3.event.which === 13) this.blur();
42967 })
42968 .call(selectElementContents);
42969 }
42970
42971 if(options.immediate) handleClick();
42972 else handlerElement.on('click', handleClick);
42973
42974 return d3.rebind(context, dispatch, 'on');
42975};
42976
42977},{"../constants/alignment":154,"../constants/xmlns_namespaces":159,"../lib":178,"d3":16}],200:[function(_dereq_,module,exports){
42978/**
42979* Copyright 2012-2020, Plotly, Inc.
42980* All rights reserved.
42981*
42982* This source code is licensed under the MIT license found in the
42983* LICENSE file in the root directory of this source tree.
42984*/
42985
42986'use strict';
42987
42988var timerCache = {};
42989
42990/**
42991 * Throttle a callback. `callback` executes synchronously only if
42992 * more than `minInterval` milliseconds have already elapsed since the latest
42993 * call (if any). Otherwise we wait until `minInterval` is over and execute the
42994 * last callback received while waiting.
42995 * So the first and last events in a train are always executed (eventually)
42996 * but some of the events in the middle can be dropped.
42997 *
42998 * @param {string} id: an identifier to mark events to throttle together
42999 * @param {number} minInterval: minimum time, in milliseconds, between
43000 * invocations of `callback`
43001 * @param {function} callback: the function to throttle. `callback` itself
43002 * should be a purely synchronous function.
43003 */
43004exports.throttle = function throttle(id, minInterval, callback) {
43005 var cache = timerCache[id];
43006 var now = Date.now();
43007
43008 if(!cache) {
43009 /*
43010 * Throw out old items before making a new one, to prevent the cache
43011 * getting overgrown, for example from old plots that have been replaced.
43012 * 1 minute age is arbitrary.
43013 */
43014 for(var idi in timerCache) {
43015 if(timerCache[idi].ts < now - 60000) {
43016 delete timerCache[idi];
43017 }
43018 }
43019 cache = timerCache[id] = {ts: 0, timer: null};
43020 }
43021
43022 _clearTimeout(cache);
43023
43024 function exec() {
43025 callback();
43026 cache.ts = Date.now();
43027 if(cache.onDone) {
43028 cache.onDone();
43029 cache.onDone = null;
43030 }
43031 }
43032
43033 if(now > cache.ts + minInterval) {
43034 exec();
43035 return;
43036 }
43037
43038 cache.timer = setTimeout(function() {
43039 exec();
43040 cache.timer = null;
43041 }, minInterval);
43042};
43043
43044exports.done = function(id) {
43045 var cache = timerCache[id];
43046 if(!cache || !cache.timer) return Promise.resolve();
43047
43048 return new Promise(function(resolve) {
43049 var previousOnDone = cache.onDone;
43050 cache.onDone = function onDone() {
43051 if(previousOnDone) previousOnDone();
43052 resolve();
43053 cache.onDone = null;
43054 };
43055 });
43056};
43057
43058/**
43059 * Clear the throttle cache for one or all timers
43060 * @param {optional string} id:
43061 * if provided, clear just this timer
43062 * if omitted, clear all timers (mainly useful for testing)
43063 */
43064exports.clear = function(id) {
43065 if(id) {
43066 _clearTimeout(timerCache[id]);
43067 delete timerCache[id];
43068 } else {
43069 for(var idi in timerCache) exports.clear(idi);
43070 }
43071};
43072
43073function _clearTimeout(cache) {
43074 if(cache && cache.timer !== null) {
43075 clearTimeout(cache.timer);
43076 cache.timer = null;
43077 }
43078}
43079
43080},{}],201:[function(_dereq_,module,exports){
43081/**
43082* Copyright 2012-2020, Plotly, Inc.
43083* All rights reserved.
43084*
43085* This source code is licensed under the MIT license found in the
43086* LICENSE file in the root directory of this source tree.
43087*/
43088
43089'use strict';
43090
43091var isNumeric = _dereq_('fast-isnumeric');
43092
43093/**
43094 * convert a linear value into a logged value, folding negative numbers into
43095 * the given range
43096 */
43097module.exports = function toLogRange(val, range) {
43098 if(val > 0) return Math.log(val) / Math.LN10;
43099
43100 // move a negative value reference to a log axis - just put the
43101 // result at the lowest range value on the plot (or if the range also went negative,
43102 // one millionth of the top of the range)
43103 var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10;
43104 if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6;
43105 return newVal;
43106};
43107
43108},{"fast-isnumeric":18}],202:[function(_dereq_,module,exports){
43109/**
43110* Copyright 2012-2020, Plotly, Inc.
43111* All rights reserved.
43112*
43113* This source code is licensed under the MIT license found in the
43114* LICENSE file in the root directory of this source tree.
43115*/
43116
43117'use strict';
43118
43119module.exports = {
43120 moduleType: 'locale',
43121 name: 'en-US',
43122 dictionary: {
43123 'Click to enter Colorscale title': 'Click to enter Colorscale title'
43124 },
43125 format: {
43126 date: '%m/%d/%Y'
43127 }
43128};
43129
43130},{}],203:[function(_dereq_,module,exports){
43131/**
43132* Copyright 2012-2020, Plotly, Inc.
43133* All rights reserved.
43134*
43135* This source code is licensed under the MIT license found in the
43136* LICENSE file in the root directory of this source tree.
43137*/
43138
43139'use strict';
43140
43141module.exports = {
43142 moduleType: 'locale',
43143 name: 'en',
43144 dictionary: {
43145 'Click to enter Colorscale title': 'Click to enter Colourscale title'
43146 },
43147 format: {
43148 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
43149 shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
43150 months: [
43151 'January', 'February', 'March', 'April', 'May', 'June',
43152 'July', 'August', 'September', 'October', 'November', 'December'
43153 ],
43154 shortMonths: [
43155 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
43156 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
43157 ],
43158 periods: ['AM', 'PM'],
43159 dateTime: '%a %b %e %X %Y',
43160 date: '%d/%m/%Y',
43161 time: '%H:%M:%S',
43162 decimal: '.',
43163 thousands: ',',
43164 grouping: [3],
43165 currency: ['$', ''],
43166 year: '%Y',
43167 month: '%b %Y',
43168 dayMonth: '%b %-d',
43169 dayMonthYear: '%b %-d, %Y'
43170 }
43171};
43172
43173},{}],204:[function(_dereq_,module,exports){
43174/**
43175* Copyright 2012-2020, Plotly, Inc.
43176* All rights reserved.
43177*
43178* This source code is licensed under the MIT license found in the
43179* LICENSE file in the root directory of this source tree.
43180*/
43181
43182
43183'use strict';
43184
43185var Registry = _dereq_('../registry');
43186
43187/*
43188 * containerArrayMatch: does this attribute string point into a
43189 * layout container array?
43190 *
43191 * @param {String} astr: an attribute string, like *annotations[2].text*
43192 *
43193 * @returns {Object | false} Returns false if `astr` doesn't match a container
43194 * array. If it does, returns:
43195 * {array: {String}, index: {Number}, property: {String}}
43196 * ie the attribute string for the array, the index within the array (or ''
43197 * if the whole array) and the property within that (or '' if the whole array
43198 * or the whole object)
43199 */
43200module.exports = function containerArrayMatch(astr) {
43201 var rootContainers = Registry.layoutArrayContainers;
43202 var regexpContainers = Registry.layoutArrayRegexes;
43203 var rootPart = astr.split('[')[0];
43204 var arrayStr;
43205 var match;
43206
43207 // look for regexp matches first, because they may be nested inside root matches
43208 // eg updatemenus[i].buttons is nested inside updatemenus
43209 for(var i = 0; i < regexpContainers.length; i++) {
43210 match = astr.match(regexpContainers[i]);
43211 if(match && match.index === 0) {
43212 arrayStr = match[0];
43213 break;
43214 }
43215 }
43216
43217 // now look for root matches
43218 if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)];
43219
43220 if(!arrayStr) return false;
43221
43222 var tail = astr.substr(arrayStr.length);
43223 if(!tail) return {array: arrayStr, index: '', property: ''};
43224
43225 match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/);
43226 if(!match) return false;
43227
43228 return {array: arrayStr, index: Number(match[1]), property: match[3] || ''};
43229};
43230
43231},{"../registry":269}],205:[function(_dereq_,module,exports){
43232/**
43233* Copyright 2012-2020, Plotly, Inc.
43234* All rights reserved.
43235*
43236* This source code is licensed under the MIT license found in the
43237* LICENSE file in the root directory of this source tree.
43238*/
43239
43240'use strict';
43241
43242var Lib = _dereq_('../lib');
43243var extendFlat = Lib.extendFlat;
43244var isPlainObject = Lib.isPlainObject;
43245
43246var traceOpts = {
43247 valType: 'flaglist',
43248 extras: ['none'],
43249 flags: ['calc', 'clearAxisTypes', 'plot', 'style', 'markerSize', 'colorbars'],
43250
43251};
43252
43253var layoutOpts = {
43254 valType: 'flaglist',
43255 extras: ['none'],
43256 flags: [
43257 'calc', 'plot', 'legend', 'ticks', 'axrange',
43258 'layoutstyle', 'modebar', 'camera', 'arraydraw', 'colorbars'
43259 ],
43260
43261};
43262
43263// flags for inside restyle/relayout include a few extras
43264// that shouldn't be used in attributes, to deal with certain
43265// combinations and conditionals efficiently
43266var traceEditTypeFlags = traceOpts.flags.slice()
43267 .concat(['fullReplot']);
43268
43269var layoutEditTypeFlags = layoutOpts.flags.slice()
43270 .concat('layoutReplot');
43271
43272module.exports = {
43273 traces: traceOpts,
43274 layout: layoutOpts,
43275 /*
43276 * default (all false) edit flags for restyle (traces)
43277 * creates a new object each call, so the caller can mutate freely
43278 */
43279 traceFlags: function() { return falseObj(traceEditTypeFlags); },
43280
43281 /*
43282 * default (all false) edit flags for relayout
43283 * creates a new object each call, so the caller can mutate freely
43284 */
43285 layoutFlags: function() { return falseObj(layoutEditTypeFlags); },
43286
43287 /*
43288 * update `flags` with the `editType` values found in `attr`
43289 */
43290 update: function(flags, attr) {
43291 var editType = attr.editType;
43292 if(editType && editType !== 'none') {
43293 var editTypeParts = editType.split('+');
43294 for(var i = 0; i < editTypeParts.length; i++) {
43295 flags[editTypeParts[i]] = true;
43296 }
43297 }
43298 },
43299
43300 overrideAll: overrideAll
43301};
43302
43303function falseObj(keys) {
43304 var out = {};
43305 for(var i = 0; i < keys.length; i++) out[keys[i]] = false;
43306 return out;
43307}
43308
43309/**
43310 * For attributes that are largely copied from elsewhere into a plot type that doesn't
43311 * support partial redraws - overrides the editType field of all attributes in the object
43312 *
43313 * @param {object} attrs: the attributes to override. Will not be mutated.
43314 * @param {string} editTypeOverride: the new editType to use
43315 * @param {'nested'|'from-root'} overrideContainers:
43316 * - 'nested' will override editType for nested containers but not the root.
43317 * - 'from-root' will also override editType of the root container.
43318 * Containers below the absolute top level (trace or layout root) DO need an
43319 * editType even if they are not `valObject`s themselves (eg `scatter.marker`)
43320 * to handle the case where you edit the whole container.
43321 *
43322 * @return {object} a new attributes object with `editType` modified as directed
43323 */
43324function overrideAll(attrs, editTypeOverride, overrideContainers) {
43325 var out = extendFlat({}, attrs);
43326 for(var key in out) {
43327 var attr = out[key];
43328 if(isPlainObject(attr)) {
43329 out[key] = overrideOne(attr, editTypeOverride, overrideContainers, key);
43330 }
43331 }
43332 if(overrideContainers === 'from-root') out.editType = editTypeOverride;
43333
43334 return out;
43335}
43336
43337function overrideOne(attr, editTypeOverride, overrideContainers, key) {
43338 if(attr.valType) {
43339 var out = extendFlat({}, attr);
43340 out.editType = editTypeOverride;
43341
43342 if(Array.isArray(attr.items)) {
43343 out.items = new Array(attr.items.length);
43344 for(var i = 0; i < attr.items.length; i++) {
43345 out.items[i] = overrideOne(attr.items[i], editTypeOverride, 'from-root');
43346 }
43347 }
43348 return out;
43349 } else {
43350 // don't provide an editType for the _deprecated container
43351 return overrideAll(attr, editTypeOverride,
43352 (key.charAt(0) === '_') ? 'nested' : 'from-root');
43353 }
43354}
43355
43356},{"../lib":178}],206:[function(_dereq_,module,exports){
43357/**
43358* Copyright 2012-2020, Plotly, Inc.
43359* All rights reserved.
43360*
43361* This source code is licensed under the MIT license found in the
43362* LICENSE file in the root directory of this source tree.
43363*/
43364
43365'use strict';
43366
43367var isNumeric = _dereq_('fast-isnumeric');
43368var m4FromQuat = _dereq_('gl-mat4/fromQuat');
43369
43370var Registry = _dereq_('../registry');
43371var Lib = _dereq_('../lib');
43372var Plots = _dereq_('../plots/plots');
43373var AxisIds = _dereq_('../plots/cartesian/axis_ids');
43374var Color = _dereq_('../components/color');
43375
43376var cleanId = AxisIds.cleanId;
43377var getFromTrace = AxisIds.getFromTrace;
43378var traceIs = Registry.traceIs;
43379
43380// clear the promise queue if one of them got rejected
43381exports.clearPromiseQueue = function(gd) {
43382 if(Array.isArray(gd._promises) && gd._promises.length > 0) {
43383 Lib.log('Clearing previous rejected promises from queue.');
43384 }
43385
43386 gd._promises = [];
43387};
43388
43389// make a few changes to the layout right away
43390// before it gets used for anything
43391// backward compatibility and cleanup of nonstandard options
43392exports.cleanLayout = function(layout) {
43393 var i, j;
43394
43395 if(!layout) layout = {};
43396
43397 // cannot have (x|y)axis1, numbering goes axis, axis2, axis3...
43398 if(layout.xaxis1) {
43399 if(!layout.xaxis) layout.xaxis = layout.xaxis1;
43400 delete layout.xaxis1;
43401 }
43402 if(layout.yaxis1) {
43403 if(!layout.yaxis) layout.yaxis = layout.yaxis1;
43404 delete layout.yaxis1;
43405 }
43406 if(layout.scene1) {
43407 if(!layout.scene) layout.scene = layout.scene1;
43408 delete layout.scene1;
43409 }
43410
43411 var axisAttrRegex = (Plots.subplotsRegistry.cartesian || {}).attrRegex;
43412 var polarAttrRegex = (Plots.subplotsRegistry.polar || {}).attrRegex;
43413 var ternaryAttrRegex = (Plots.subplotsRegistry.ternary || {}).attrRegex;
43414 var sceneAttrRegex = (Plots.subplotsRegistry.gl3d || {}).attrRegex;
43415
43416 var keys = Object.keys(layout);
43417 for(i = 0; i < keys.length; i++) {
43418 var key = keys[i];
43419
43420 if(axisAttrRegex && axisAttrRegex.test(key)) {
43421 // modifications to cartesian axes
43422
43423 var ax = layout[key];
43424 if(ax.anchor && ax.anchor !== 'free') {
43425 ax.anchor = cleanId(ax.anchor);
43426 }
43427 if(ax.overlaying) ax.overlaying = cleanId(ax.overlaying);
43428
43429 // old method of axis type - isdate and islog (before category existed)
43430 if(!ax.type) {
43431 if(ax.isdate) ax.type = 'date';
43432 else if(ax.islog) ax.type = 'log';
43433 else if(ax.isdate === false && ax.islog === false) ax.type = 'linear';
43434 }
43435 if(ax.autorange === 'withzero' || ax.autorange === 'tozero') {
43436 ax.autorange = true;
43437 ax.rangemode = 'tozero';
43438 }
43439 delete ax.islog;
43440 delete ax.isdate;
43441 delete ax.categories; // replaced by _categories
43442
43443 // prune empty domain arrays made before the new nestedProperty
43444 if(emptyContainer(ax, 'domain')) delete ax.domain;
43445
43446 // autotick -> tickmode
43447 if(ax.autotick !== undefined) {
43448 if(ax.tickmode === undefined) {
43449 ax.tickmode = ax.autotick ? 'auto' : 'linear';
43450 }
43451 delete ax.autotick;
43452 }
43453
43454 cleanTitle(ax);
43455 } else if(polarAttrRegex && polarAttrRegex.test(key)) {
43456 // modifications for polar
43457
43458 var polar = layout[key];
43459 cleanTitle(polar.radialaxis);
43460 } else if(ternaryAttrRegex && ternaryAttrRegex.test(key)) {
43461 // modifications for ternary
43462
43463 var ternary = layout[key];
43464 cleanTitle(ternary.aaxis);
43465 cleanTitle(ternary.baxis);
43466 cleanTitle(ternary.caxis);
43467 } else if(sceneAttrRegex && sceneAttrRegex.test(key)) {
43468 // modifications for 3D scenes
43469
43470 var scene = layout[key];
43471
43472 // clean old Camera coords
43473 var cameraposition = scene.cameraposition;
43474
43475 if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
43476 var rotation = cameraposition[0];
43477 var center = cameraposition[1];
43478 var radius = cameraposition[2];
43479 var mat = m4FromQuat([], rotation);
43480 var eye = [];
43481
43482 for(j = 0; j < 3; ++j) {
43483 eye[j] = center[j] + radius * mat[2 + 4 * j];
43484 }
43485
43486 scene.camera = {
43487 eye: {x: eye[0], y: eye[1], z: eye[2]},
43488 center: {x: center[0], y: center[1], z: center[2]},
43489 up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case
43490 };
43491
43492 delete scene.cameraposition;
43493 }
43494
43495 // clean axis titles
43496 cleanTitle(scene.xaxis);
43497 cleanTitle(scene.yaxis);
43498 cleanTitle(scene.zaxis);
43499 }
43500 }
43501
43502 var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
43503 for(i = 0; i < annotationsLen; i++) {
43504 var ann = layout.annotations[i];
43505
43506 if(!Lib.isPlainObject(ann)) continue;
43507
43508 if(ann.ref) {
43509 if(ann.ref === 'paper') {
43510 ann.xref = 'paper';
43511 ann.yref = 'paper';
43512 } else if(ann.ref === 'data') {
43513 ann.xref = 'x';
43514 ann.yref = 'y';
43515 }
43516 delete ann.ref;
43517 }
43518
43519 cleanAxRef(ann, 'xref');
43520 cleanAxRef(ann, 'yref');
43521 }
43522
43523 var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0;
43524 for(i = 0; i < shapesLen; i++) {
43525 var shape = layout.shapes[i];
43526
43527 if(!Lib.isPlainObject(shape)) continue;
43528
43529 cleanAxRef(shape, 'xref');
43530 cleanAxRef(shape, 'yref');
43531 }
43532
43533 var legend = layout.legend;
43534 if(legend) {
43535 // check for old-style legend positioning (x or y is +/- 100)
43536 if(legend.x > 3) {
43537 legend.x = 1.02;
43538 legend.xanchor = 'left';
43539 } else if(legend.x < -2) {
43540 legend.x = -0.02;
43541 legend.xanchor = 'right';
43542 }
43543
43544 if(legend.y > 3) {
43545 legend.y = 1.02;
43546 legend.yanchor = 'bottom';
43547 } else if(legend.y < -2) {
43548 legend.y = -0.02;
43549 legend.yanchor = 'top';
43550 }
43551 }
43552
43553 // clean plot title
43554 cleanTitle(layout);
43555
43556 /*
43557 * Moved from rotate -> orbit for dragmode
43558 */
43559 if(layout.dragmode === 'rotate') layout.dragmode = 'orbit';
43560
43561 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
43562 // supported, but new tinycolor does not because they're not valid css
43563 Color.clean(layout);
43564
43565 // clean the layout container in layout.template
43566 if(layout.template && layout.template.layout) {
43567 exports.cleanLayout(layout.template.layout);
43568 }
43569
43570 return layout;
43571};
43572
43573function cleanAxRef(container, attr) {
43574 var valIn = container[attr];
43575 var axLetter = attr.charAt(0);
43576 if(valIn && valIn !== 'paper') {
43577 container[attr] = cleanId(valIn, axLetter);
43578 }
43579}
43580
43581/**
43582 * Cleans up old title attribute structure (flat) in favor of the new one (nested).
43583 *
43584 * @param {Object} titleContainer - an object potentially including deprecated title attributes
43585 */
43586function cleanTitle(titleContainer) {
43587 if(titleContainer) {
43588 // title -> title.text
43589 // (although title used to be a string attribute,
43590 // numbers are accepted as well)
43591 if(typeof titleContainer.title === 'string' || typeof titleContainer.title === 'number') {
43592 titleContainer.title = {
43593 text: titleContainer.title
43594 };
43595 }
43596
43597 rewireAttr('titlefont', 'font');
43598 rewireAttr('titleposition', 'position');
43599 rewireAttr('titleside', 'side');
43600 rewireAttr('titleoffset', 'offset');
43601 }
43602
43603 function rewireAttr(oldAttrName, newAttrName) {
43604 var oldAttrSet = titleContainer[oldAttrName];
43605 var newAttrSet = titleContainer.title && titleContainer.title[newAttrName];
43606
43607 if(oldAttrSet && !newAttrSet) {
43608 // Ensure title object exists
43609 if(!titleContainer.title) {
43610 titleContainer.title = {};
43611 }
43612
43613 titleContainer.title[newAttrName] = titleContainer[oldAttrName];
43614 delete titleContainer[oldAttrName];
43615 }
43616 }
43617}
43618
43619/*
43620 * cleanData: Make a few changes to the data for backward compatibility
43621 * before it gets used for anything. Modifies the data traces users provide.
43622 *
43623 * Important: if you're going to add something here that modifies a data array,
43624 * update it in place so the new array === the old one.
43625 */
43626exports.cleanData = function(data) {
43627 for(var tracei = 0; tracei < data.length; tracei++) {
43628 var trace = data[tracei];
43629 var i;
43630
43631 // use xbins to bin data in x, and ybins to bin data in y
43632 if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) {
43633 trace.ybins = trace.xbins;
43634 delete trace.xbins;
43635 }
43636
43637 // error_y.opacity is obsolete - merge into color
43638 if(trace.error_y && 'opacity' in trace.error_y) {
43639 var dc = Color.defaults;
43640 var yeColor = trace.error_y.color || (traceIs(trace, 'bar') ?
43641 Color.defaultLine :
43642 dc[tracei % dc.length]);
43643 trace.error_y.color = Color.addOpacity(
43644 Color.rgb(yeColor),
43645 Color.opacity(yeColor) * trace.error_y.opacity);
43646 delete trace.error_y.opacity;
43647 }
43648
43649 // convert bardir to orientation, and put the data into
43650 // the axes it's eventually going to be used with
43651 if('bardir' in trace) {
43652 if(trace.bardir === 'h' && (traceIs(trace, 'bar') ||
43653 trace.type.substr(0, 9) === 'histogram')) {
43654 trace.orientation = 'h';
43655 exports.swapXYData(trace);
43656 }
43657 delete trace.bardir;
43658 }
43659
43660 // now we have only one 1D histogram type, and whether
43661 // it uses x or y data depends on trace.orientation
43662 if(trace.type === 'histogramy') exports.swapXYData(trace);
43663 if(trace.type === 'histogramx' || trace.type === 'histogramy') {
43664 trace.type = 'histogram';
43665 }
43666
43667 // scl->scale, reversescl->reversescale
43668 if('scl' in trace && !('colorscale' in trace)) {
43669 trace.colorscale = trace.scl;
43670 delete trace.scl;
43671 }
43672 if('reversescl' in trace && !('reversescale' in trace)) {
43673 trace.reversescale = trace.reversescl;
43674 delete trace.reversescl;
43675 }
43676
43677 // axis ids x1 -> x, y1-> y
43678 if(trace.xaxis) trace.xaxis = cleanId(trace.xaxis, 'x');
43679 if(trace.yaxis) trace.yaxis = cleanId(trace.yaxis, 'y');
43680
43681 // scene ids scene1 -> scene
43682 if(traceIs(trace, 'gl3d') && trace.scene) {
43683 trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
43684 }
43685
43686 if(!traceIs(trace, 'pie-like') && !traceIs(trace, 'bar-like')) {
43687 if(Array.isArray(trace.textposition)) {
43688 for(i = 0; i < trace.textposition.length; i++) {
43689 trace.textposition[i] = cleanTextPosition(trace.textposition[i]);
43690 }
43691 } else if(trace.textposition) {
43692 trace.textposition = cleanTextPosition(trace.textposition);
43693 }
43694 }
43695
43696 // fix typo in colorscale definition
43697 var _module = Registry.getModule(trace);
43698 if(_module && _module.colorbar) {
43699 var containerName = _module.colorbar.container;
43700 var container = containerName ? trace[containerName] : trace;
43701 if(container && container.colorscale) {
43702 if(container.colorscale === 'YIGnBu') container.colorscale = 'YlGnBu';
43703 if(container.colorscale === 'YIOrRd') container.colorscale = 'YlOrRd';
43704 }
43705 }
43706
43707 // fix typo in surface 'highlight*' definitions
43708 if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) {
43709 var dims = ['x', 'y', 'z'];
43710
43711 for(i = 0; i < dims.length; i++) {
43712 var opts = trace.contours[dims[i]];
43713
43714 if(!Lib.isPlainObject(opts)) continue;
43715
43716 if(opts.highlightColor) {
43717 opts.highlightcolor = opts.highlightColor;
43718 delete opts.highlightColor;
43719 }
43720
43721 if(opts.highlightWidth) {
43722 opts.highlightwidth = opts.highlightWidth;
43723 delete opts.highlightWidth;
43724 }
43725 }
43726 }
43727
43728 // fixes from converting finance from transforms to real trace types
43729 if(trace.type === 'candlestick' || trace.type === 'ohlc') {
43730 var increasingShowlegend = (trace.increasing || {}).showlegend !== false;
43731 var decreasingShowlegend = (trace.decreasing || {}).showlegend !== false;
43732 var increasingName = cleanFinanceDir(trace.increasing);
43733 var decreasingName = cleanFinanceDir(trace.decreasing);
43734
43735 // now figure out something smart to do with the separate direction
43736 // names we removed
43737 if((increasingName !== false) && (decreasingName !== false)) {
43738 // both sub-names existed: base name previously had no effect
43739 // so ignore it and try to find a shared part of the sub-names
43740
43741 var newName = commonPrefix(
43742 increasingName, decreasingName,
43743 increasingShowlegend, decreasingShowlegend
43744 );
43745 // if no common part, leave whatever name was (or wasn't) there
43746 if(newName) trace.name = newName;
43747 } else if((increasingName || decreasingName) && !trace.name) {
43748 // one sub-name existed but not the base name - just use the sub-name
43749 trace.name = increasingName || decreasingName;
43750 }
43751 }
43752
43753 // transforms backward compatibility fixes
43754 if(Array.isArray(trace.transforms)) {
43755 var transforms = trace.transforms;
43756
43757 for(i = 0; i < transforms.length; i++) {
43758 var transform = transforms[i];
43759
43760 if(!Lib.isPlainObject(transform)) continue;
43761
43762 switch(transform.type) {
43763 case 'filter':
43764 if(transform.filtersrc) {
43765 transform.target = transform.filtersrc;
43766 delete transform.filtersrc;
43767 }
43768
43769 if(transform.calendar) {
43770 if(!transform.valuecalendar) {
43771 transform.valuecalendar = transform.calendar;
43772 }
43773 delete transform.calendar;
43774 }
43775 break;
43776
43777 case 'groupby':
43778 // Name has changed from `style` to `styles`, so use `style` but prefer `styles`:
43779 transform.styles = transform.styles || transform.style;
43780
43781 if(transform.styles && !Array.isArray(transform.styles)) {
43782 var prevStyles = transform.styles;
43783 var styleKeys = Object.keys(prevStyles);
43784
43785 transform.styles = [];
43786 for(var j = 0; j < styleKeys.length; j++) {
43787 transform.styles.push({
43788 target: styleKeys[j],
43789 value: prevStyles[styleKeys[j]]
43790 });
43791 }
43792 }
43793 break;
43794 }
43795 }
43796 }
43797
43798 // prune empty containers made before the new nestedProperty
43799 if(emptyContainer(trace, 'line')) delete trace.line;
43800 if('marker' in trace) {
43801 if(emptyContainer(trace.marker, 'line')) delete trace.marker.line;
43802 if(emptyContainer(trace, 'marker')) delete trace.marker;
43803 }
43804
43805 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
43806 // supported, but new tinycolor does not because they're not valid css
43807 Color.clean(trace);
43808
43809 // remove obsolete autobin(x|y) attributes, but only if true
43810 // if false, this needs to happen in Histogram.calc because it
43811 // can be a one-time autobin so we need to know the results before
43812 // we can push them back into the trace.
43813 if(trace.autobinx) {
43814 delete trace.autobinx;
43815 delete trace.xbins;
43816 }
43817 if(trace.autobiny) {
43818 delete trace.autobiny;
43819 delete trace.ybins;
43820 }
43821
43822 cleanTitle(trace);
43823 if(trace.colorbar) cleanTitle(trace.colorbar);
43824 if(trace.marker && trace.marker.colorbar) cleanTitle(trace.marker.colorbar);
43825 if(trace.line && trace.line.colorbar) cleanTitle(trace.line.colorbar);
43826 if(trace.aaxis) cleanTitle(trace.aaxis);
43827 if(trace.baxis) cleanTitle(trace.baxis);
43828 }
43829};
43830
43831function cleanFinanceDir(dirContainer) {
43832 if(!Lib.isPlainObject(dirContainer)) return false;
43833
43834 var dirName = dirContainer.name;
43835
43836 delete dirContainer.name;
43837 delete dirContainer.showlegend;
43838
43839 return (typeof dirName === 'string' || typeof dirName === 'number') && String(dirName);
43840}
43841
43842function commonPrefix(name1, name2, show1, show2) {
43843 // if only one is shown in the legend, use that
43844 if(show1 && !show2) return name1;
43845 if(show2 && !show1) return name2;
43846
43847 // if both or neither are in the legend, check if one is blank (or whitespace)
43848 // and use the other one
43849 // note that hover labels can still use the name even if the legend doesn't
43850 if(!name1.trim()) return name2;
43851 if(!name2.trim()) return name1;
43852
43853 var minLen = Math.min(name1.length, name2.length);
43854 var i;
43855 for(i = 0; i < minLen; i++) {
43856 if(name1.charAt(i) !== name2.charAt(i)) break;
43857 }
43858
43859 var out = name1.substr(0, i);
43860 return out.trim();
43861}
43862
43863// textposition - support partial attributes (ie just 'top')
43864// and incorrect use of middle / center etc.
43865function cleanTextPosition(textposition) {
43866 var posY = 'middle';
43867 var posX = 'center';
43868
43869 if(typeof textposition === 'string') {
43870 if(textposition.indexOf('top') !== -1) posY = 'top';
43871 else if(textposition.indexOf('bottom') !== -1) posY = 'bottom';
43872
43873 if(textposition.indexOf('left') !== -1) posX = 'left';
43874 else if(textposition.indexOf('right') !== -1) posX = 'right';
43875 }
43876
43877 return posY + ' ' + posX;
43878}
43879
43880function emptyContainer(outer, innerStr) {
43881 return (innerStr in outer) &&
43882 (typeof outer[innerStr] === 'object') &&
43883 (Object.keys(outer[innerStr]).length === 0);
43884}
43885
43886
43887// swap all the data and data attributes associated with x and y
43888exports.swapXYData = function(trace) {
43889 var i;
43890 Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']);
43891 if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
43892 if(trace.transpose) delete trace.transpose;
43893 else trace.transpose = true;
43894 }
43895 if(trace.error_x && trace.error_y) {
43896 var errorY = trace.error_y;
43897 var copyYstyle = ('copy_ystyle' in errorY) ?
43898 errorY.copy_ystyle :
43899 !(errorY.color || errorY.thickness || errorY.width);
43900 Lib.swapAttrs(trace, ['error_?.copy_ystyle']);
43901 if(copyYstyle) {
43902 Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
43903 }
43904 }
43905 if(typeof trace.hoverinfo === 'string') {
43906 var hoverInfoParts = trace.hoverinfo.split('+');
43907 for(i = 0; i < hoverInfoParts.length; i++) {
43908 if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';
43909 else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x';
43910 }
43911 trace.hoverinfo = hoverInfoParts.join('+');
43912 }
43913};
43914
43915// coerce traceIndices input to array of trace indices
43916exports.coerceTraceIndices = function(gd, traceIndices) {
43917 if(isNumeric(traceIndices)) {
43918 return [traceIndices];
43919 } else if(!Array.isArray(traceIndices) || !traceIndices.length) {
43920 return gd.data.map(function(_, i) { return i; });
43921 } else if(Array.isArray(traceIndices)) {
43922 var traceIndicesOut = [];
43923 for(var i = 0; i < traceIndices.length; i++) {
43924 if(Lib.isIndex(traceIndices[i], gd.data.length)) {
43925 traceIndicesOut.push(traceIndices[i]);
43926 } else {
43927 Lib.warn('trace index (', traceIndices[i], ') is not a number or is out of bounds');
43928 }
43929 }
43930 return traceIndicesOut;
43931 }
43932
43933 return traceIndices;
43934};
43935
43936/**
43937 * Manages logic around array container item creation / deletion / update
43938 * that nested property alone can't handle.
43939 *
43940 * @param {Object} np
43941 * nested property of update attribute string about trace or layout object
43942 * @param {*} newVal
43943 * update value passed to restyle / relayout / update
43944 * @param {Object} undoit
43945 * undo hash (N.B. undoit may be mutated here).
43946 *
43947 */
43948exports.manageArrayContainers = function(np, newVal, undoit) {
43949 var obj = np.obj;
43950 var parts = np.parts;
43951 var pLength = parts.length;
43952 var pLast = parts[pLength - 1];
43953
43954 var pLastIsNumber = isNumeric(pLast);
43955
43956 if(pLastIsNumber && newVal === null) {
43957 // delete item
43958
43959 // Clear item in array container when new value is null
43960 var contPath = parts.slice(0, pLength - 1).join('.');
43961 var cont = Lib.nestedProperty(obj, contPath).get();
43962 cont.splice(pLast, 1);
43963
43964 // Note that nested property clears null / undefined at end of
43965 // array container, but not within them.
43966 } else if(pLastIsNumber && np.get() === undefined) {
43967 // create item
43968
43969 // When adding a new item, make sure undo command will remove it
43970 if(np.get() === undefined) undoit[np.astr] = null;
43971
43972 np.set(newVal);
43973 } else {
43974 // update item
43975
43976 // If the last part of attribute string isn't a number,
43977 // np.set is all we need.
43978 np.set(newVal);
43979 }
43980};
43981
43982/*
43983 * Match the part to strip off to turn an attribute into its parent
43984 * really it should be either '.some_characters' or '[number]'
43985 * but we're a little more permissive here and match either
43986 * '.not_brackets_or_dot' or '[not_brackets_or_dot]'
43987 */
43988var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/;
43989
43990function getParent(attr) {
43991 var tail = attr.search(ATTR_TAIL_RE);
43992 if(tail > 0) return attr.substr(0, tail);
43993}
43994
43995/*
43996 * hasParent: does an attribute object contain a parent of the given attribute?
43997 * for example, given 'images[2].x' do we also have 'images' or 'images[2]'?
43998 *
43999 * @param {Object} aobj
44000 * update object, whose keys are attribute strings and values are their new settings
44001 * @param {string} attr
44002 * the attribute string to test against
44003 * @returns {Boolean}
44004 * is a parent of attr present in aobj?
44005 */
44006exports.hasParent = function(aobj, attr) {
44007 var attrParent = getParent(attr);
44008 while(attrParent) {
44009 if(attrParent in aobj) return true;
44010 attrParent = getParent(attrParent);
44011 }
44012 return false;
44013};
44014
44015/**
44016 * Empty out types for all axes containing these traces so we auto-set them again
44017 *
44018 * @param {object} gd
44019 * @param {[integer]} traces: trace indices to search for axes to clear the types of
44020 * @param {object} layoutUpdate: any update being done concurrently to the layout,
44021 * which may supercede clearing the axis types
44022 */
44023var axLetters = ['x', 'y', 'z'];
44024exports.clearAxisTypes = function(gd, traces, layoutUpdate) {
44025 for(var i = 0; i < traces.length; i++) {
44026 var trace = gd._fullData[i];
44027 for(var j = 0; j < 3; j++) {
44028 var ax = getFromTrace(gd, trace, axLetters[j]);
44029
44030 // do not clear log type - that's never an auto result so must have been intentional
44031 if(ax && ax.type !== 'log') {
44032 var axAttr = ax._name;
44033 var sceneName = ax._id.substr(1);
44034 if(sceneName.substr(0, 5) === 'scene') {
44035 if(layoutUpdate[sceneName] !== undefined) continue;
44036 axAttr = sceneName + '.' + axAttr;
44037 }
44038 var typeAttr = axAttr + '.type';
44039
44040 if(layoutUpdate[axAttr] === undefined && layoutUpdate[typeAttr] === undefined) {
44041 Lib.nestedProperty(gd.layout, typeAttr).set(null);
44042 }
44043 }
44044 }
44045 }
44046};
44047
44048},{"../components/color":52,"../lib":178,"../plots/cartesian/axis_ids":225,"../plots/plots":256,"../registry":269,"fast-isnumeric":18,"gl-mat4/fromQuat":19}],207:[function(_dereq_,module,exports){
44049/**
44050* Copyright 2012-2020, Plotly, Inc.
44051* All rights reserved.
44052*
44053* This source code is licensed under the MIT license found in the
44054* LICENSE file in the root directory of this source tree.
44055*/
44056
44057'use strict';
44058
44059var main = _dereq_('./plot_api');
44060
44061exports.plot = main.plot;
44062exports.newPlot = main.newPlot;
44063exports.restyle = main.restyle;
44064exports.relayout = main.relayout;
44065exports.redraw = main.redraw;
44066exports.update = main.update;
44067exports._guiRestyle = main._guiRestyle;
44068exports._guiRelayout = main._guiRelayout;
44069exports._guiUpdate = main._guiUpdate;
44070exports._storeDirectGUIEdit = main._storeDirectGUIEdit;
44071exports.react = main.react;
44072exports.extendTraces = main.extendTraces;
44073exports.prependTraces = main.prependTraces;
44074exports.addTraces = main.addTraces;
44075exports.deleteTraces = main.deleteTraces;
44076exports.moveTraces = main.moveTraces;
44077exports.purge = main.purge;
44078exports.addFrames = main.addFrames;
44079exports.deleteFrames = main.deleteFrames;
44080exports.animate = main.animate;
44081exports.setPlotConfig = main.setPlotConfig;
44082
44083exports.toImage = _dereq_('./to_image');
44084exports.validate = _dereq_('./validate');
44085exports.downloadImage = _dereq_('../snapshot/download');
44086
44087var templateApi = _dereq_('./template_api');
44088exports.makeTemplate = templateApi.makeTemplate;
44089exports.validateTemplate = templateApi.validateTemplate;
44090
44091},{"../snapshot/download":271,"./plot_api":209,"./template_api":214,"./to_image":215,"./validate":216}],208:[function(_dereq_,module,exports){
44092/**
44093* Copyright 2012-2020, Plotly, Inc.
44094* All rights reserved.
44095*
44096* This source code is licensed under the MIT license found in the
44097* LICENSE file in the root directory of this source tree.
44098*/
44099
44100
44101'use strict';
44102
44103var isPlainObject = _dereq_('../lib/is_plain_object');
44104var noop = _dereq_('../lib/noop');
44105var Loggers = _dereq_('../lib/loggers');
44106var sorterAsc = _dereq_('../lib/search').sorterAsc;
44107var Registry = _dereq_('../registry');
44108
44109
44110exports.containerArrayMatch = _dereq_('./container_array_match');
44111
44112var isAddVal = exports.isAddVal = function isAddVal(val) {
44113 return val === 'add' || isPlainObject(val);
44114};
44115
44116var isRemoveVal = exports.isRemoveVal = function isRemoveVal(val) {
44117 return val === null || val === 'remove';
44118};
44119
44120/*
44121 * applyContainerArrayChanges: for managing arrays of layout components in relayout
44122 * handles them all with a consistent interface.
44123 *
44124 * Here are the supported actions -> relayout calls -> edits we get here
44125 * (as prepared in _relayout):
44126 *
44127 * add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}}
44128 * add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}}
44129 * delete an obj -> {'annotations[2]': 'remove'} -> {2: {'': 'remove'}}
44130 * -> {'annotations[2]': null} -> {2: {'': null}}
44131 * delete the whole array -> {'annotations': 'remove'} -> {'': {'': 'remove'}}
44132 * -> {'annotations': null} -> {'': {'': null}}
44133 * edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}}
44134 *
44135 * You can combine many edits to different objects. Objects are added and edited
44136 * in ascending order, then removed in descending order.
44137 * For example, starting with [a, b, c], if you want to:
44138 * - replace b with d:
44139 * {'annotations[1]': d, 'annotations[2]': null} (b is item 2 after adding d)
44140 * - add a new item d between a and b, and edit b:
44141 * {'annotations[1]': d, 'annotations[2].x': newX} (b is item 2 after adding d)
44142 * - delete b and edit c:
44143 * {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed)
44144 *
44145 * You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i`
44146 * You CANNOT combine replacing/deleting the whole array with anything else (for the same array).
44147 *
44148 * @param {HTMLDivElement} gd
44149 * the DOM element of the graph container div
44150 * @param {Lib.nestedProperty} componentType: the array we are editing
44151 * @param {Object} edits
44152 * the changes to make; keys are indices to edit, values are themselves objects:
44153 * {attr: newValue} of changes to make to that index (with add/remove behavior
44154 * in special values of the empty attr)
44155 * @param {Object} flags
44156 * the flags for which actions we're going to perform to display these (and
44157 * any other) changes. If we're already `recalc`ing, we don't need to redraw
44158 * individual items
44159 * @param {function} _nestedProperty
44160 * a (possibly modified for gui edits) nestedProperty constructor
44161 * The modified version takes a 3rd argument, for a prefix to the attribute
44162 * string necessary for storing GUI edits
44163 *
44164 * @returns {bool} `true` if it managed to complete drawing of the changes
44165 * `false` would mean the parent should replot.
44166 */
44167exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, edits, flags, _nestedProperty) {
44168 var componentType = np.astr;
44169 var supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults');
44170 var draw = Registry.getComponentMethod(componentType, 'draw');
44171 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
44172 var replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) || (draw === noop);
44173 var layout = gd.layout;
44174 var fullLayout = gd._fullLayout;
44175
44176 if(edits['']) {
44177 if(Object.keys(edits).length > 1) {
44178 Loggers.warn('Full array edits are incompatible with other edits',
44179 componentType);
44180 }
44181
44182 var fullVal = edits[''][''];
44183
44184 if(isRemoveVal(fullVal)) np.set(null);
44185 else if(Array.isArray(fullVal)) np.set(fullVal);
44186 else {
44187 Loggers.warn('Unrecognized full array edit value', componentType, fullVal);
44188 return true;
44189 }
44190
44191 if(replotLater) return false;
44192
44193 supplyComponentDefaults(layout, fullLayout);
44194 draw(gd);
44195 return true;
44196 }
44197
44198 var componentNums = Object.keys(edits).map(Number).sort(sorterAsc);
44199 var componentArrayIn = np.get();
44200 var componentArray = componentArrayIn || [];
44201 // componentArrayFull is used just to keep splices in line between
44202 // full and input arrays, so private keys can be copied over after
44203 // redoing supplyDefaults
44204 // TODO: this assumes componentArray is in gd.layout - which will not be
44205 // true after we extend this to restyle
44206 var componentArrayFull = _nestedProperty(fullLayout, componentType).get();
44207
44208 var deletes = [];
44209 var firstIndexChange = -1;
44210 var maxIndex = componentArray.length;
44211 var i;
44212 var j;
44213 var componentNum;
44214 var objEdits;
44215 var objKeys;
44216 var objVal;
44217 var adding, prefix;
44218
44219 // first make the add and edit changes
44220 for(i = 0; i < componentNums.length; i++) {
44221 componentNum = componentNums[i];
44222 objEdits = edits[componentNum];
44223 objKeys = Object.keys(objEdits);
44224 objVal = objEdits[''],
44225 adding = isAddVal(objVal);
44226
44227 if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) {
44228 Loggers.warn('index out of range', componentType, componentNum);
44229 continue;
44230 }
44231
44232 if(objVal !== undefined) {
44233 if(objKeys.length > 1) {
44234 Loggers.warn(
44235 'Insertion & removal are incompatible with edits to the same index.',
44236 componentType, componentNum);
44237 }
44238
44239 if(isRemoveVal(objVal)) {
44240 deletes.push(componentNum);
44241 } else if(adding) {
44242 if(objVal === 'add') objVal = {};
44243 componentArray.splice(componentNum, 0, objVal);
44244 if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {});
44245 } else {
44246 Loggers.warn('Unrecognized full object edit value',
44247 componentType, componentNum, objVal);
44248 }
44249
44250 if(firstIndexChange === -1) firstIndexChange = componentNum;
44251 } else {
44252 for(j = 0; j < objKeys.length; j++) {
44253 prefix = componentType + '[' + componentNum + '].';
44254 _nestedProperty(componentArray[componentNum], objKeys[j], prefix)
44255 .set(objEdits[objKeys[j]]);
44256 }
44257 }
44258 }
44259
44260 // now do deletes
44261 for(i = deletes.length - 1; i >= 0; i--) {
44262 componentArray.splice(deletes[i], 1);
44263 // TODO: this drops private keys that had been stored in componentArrayFull
44264 // does this have any ill effects?
44265 if(componentArrayFull) componentArrayFull.splice(deletes[i], 1);
44266 }
44267
44268 if(!componentArray.length) np.set(null);
44269 else if(!componentArrayIn) np.set(componentArray);
44270
44271 if(replotLater) return false;
44272
44273 supplyComponentDefaults(layout, fullLayout);
44274
44275 // finally draw all the components we need to
44276 // if we added or removed any, redraw all after it
44277 if(drawOne !== noop) {
44278 var indicesToDraw;
44279 if(firstIndexChange === -1) {
44280 // there's no re-indexing to do, so only redraw components that changed
44281 indicesToDraw = componentNums;
44282 } else {
44283 // in case the component array was shortened, we still need do call
44284 // drawOne on the latter items so they get properly removed
44285 maxIndex = Math.max(componentArray.length, maxIndex);
44286 indicesToDraw = [];
44287 for(i = 0; i < componentNums.length; i++) {
44288 componentNum = componentNums[i];
44289 if(componentNum >= firstIndexChange) break;
44290 indicesToDraw.push(componentNum);
44291 }
44292 for(i = firstIndexChange; i < maxIndex; i++) {
44293 indicesToDraw.push(i);
44294 }
44295 }
44296 for(i = 0; i < indicesToDraw.length; i++) {
44297 drawOne(gd, indicesToDraw[i]);
44298 }
44299 } else draw(gd);
44300
44301 return true;
44302};
44303
44304},{"../lib/is_plain_object":179,"../lib/loggers":182,"../lib/noop":187,"../lib/search":196,"../registry":269,"./container_array_match":204}],209:[function(_dereq_,module,exports){
44305/**
44306* Copyright 2012-2020, Plotly, Inc.
44307* All rights reserved.
44308*
44309* This source code is licensed under the MIT license found in the
44310* LICENSE file in the root directory of this source tree.
44311*/
44312
44313'use strict';
44314
44315var d3 = _dereq_('d3');
44316var isNumeric = _dereq_('fast-isnumeric');
44317var hasHover = _dereq_('has-hover');
44318
44319var Lib = _dereq_('../lib');
44320var nestedProperty = Lib.nestedProperty;
44321
44322var Events = _dereq_('../lib/events');
44323var Queue = _dereq_('../lib/queue');
44324
44325var Registry = _dereq_('../registry');
44326var PlotSchema = _dereq_('./plot_schema');
44327var Plots = _dereq_('../plots/plots');
44328var Polar = _dereq_('../plots/polar/legacy');
44329
44330var Axes = _dereq_('../plots/cartesian/axes');
44331var Drawing = _dereq_('../components/drawing');
44332var Color = _dereq_('../components/color');
44333var initInteractions = _dereq_('../plots/cartesian/graph_interact').initInteractions;
44334var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
44335var svgTextUtils = _dereq_('../lib/svg_text_utils');
44336var clearSelect = _dereq_('../plots/cartesian/select').clearSelect;
44337
44338var dfltConfig = _dereq_('./plot_config').dfltConfig;
44339var manageArrays = _dereq_('./manage_arrays');
44340var helpers = _dereq_('./helpers');
44341var subroutines = _dereq_('./subroutines');
44342var editTypes = _dereq_('./edit_types');
44343
44344var AX_NAME_PATTERN = _dereq_('../plots/cartesian/constants').AX_NAME_PATTERN;
44345
44346var numericNameWarningCount = 0;
44347var numericNameWarningCountLimit = 5;
44348
44349/**
44350 * Main plot-creation function
44351 *
44352 * @param {string id or DOM element} gd
44353 * the id or DOM element of the graph container div
44354 * @param {array of objects} data
44355 * array of traces, containing the data and display information for each trace
44356 * @param {object} layout
44357 * object describing the overall display of the plot,
44358 * all the stuff that doesn't pertain to any individual trace
44359 * @param {object} config
44360 * configuration options (see ./plot_config.js for more info)
44361 *
44362 * OR
44363 *
44364 * @param {string id or DOM element} gd
44365 * the id or DOM element of the graph container div
44366 * @param {object} figure
44367 * object containing `data`, `layout`, `config`, and `frames` members
44368 *
44369 */
44370function plot(gd, data, layout, config) {
44371 var frames;
44372
44373 gd = Lib.getGraphDiv(gd);
44374
44375 // Events.init is idempotent and bails early if gd has already been init'd
44376 Events.init(gd);
44377
44378 if(Lib.isPlainObject(data)) {
44379 var obj = data;
44380 data = obj.data;
44381 layout = obj.layout;
44382 config = obj.config;
44383 frames = obj.frames;
44384 }
44385
44386 var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]);
44387 if(okToPlot === false) return Promise.reject();
44388
44389 // if there's no data or layout, and this isn't yet a plotly plot
44390 // container, log a warning to help plotly.js users debug
44391 if(!data && !layout && !Lib.isPlotDiv(gd)) {
44392 Lib.warn('Calling Plotly.plot as if redrawing ' +
44393 'but this container doesn\'t yet have a plot.', gd);
44394 }
44395
44396 function addFrames() {
44397 if(frames) {
44398 return exports.addFrames(gd, frames);
44399 }
44400 }
44401
44402 // transfer configuration options to gd until we move over to
44403 // a more OO like model
44404 setPlotContext(gd, config);
44405
44406 if(!layout) layout = {};
44407
44408 // hook class for plots main container (in case of plotly.js
44409 // this won't be #embedded-graph or .js-tab-contents)
44410 d3.select(gd).classed('js-plotly-plot', true);
44411
44412 // off-screen getBoundingClientRect testing space,
44413 // in #js-plotly-tester (and stored as Drawing.tester)
44414 // so we can share cached text across tabs
44415 Drawing.makeTester();
44416
44417 // collect promises for any async actions during plotting
44418 // any part of the plotting code can push to gd._promises, then
44419 // before we move to the next step, we check that they're all
44420 // complete, and empty out the promise list again.
44421 if(!Array.isArray(gd._promises)) gd._promises = [];
44422
44423 var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
44424
44425 // if there is already data on the graph, append the new data
44426 // if you only want to redraw, pass a non-array for data
44427 if(Array.isArray(data)) {
44428 helpers.cleanData(data);
44429
44430 if(graphWasEmpty) gd.data = data;
44431 else gd.data.push.apply(gd.data, data);
44432
44433 // for routines outside graph_obj that want a clean tab
44434 // (rather than appending to an existing one) gd.empty
44435 // is used to determine whether to make a new tab
44436 gd.empty = false;
44437 }
44438
44439 if(!gd.layout || graphWasEmpty) {
44440 gd.layout = helpers.cleanLayout(layout);
44441 }
44442
44443 Plots.supplyDefaults(gd);
44444
44445 var fullLayout = gd._fullLayout;
44446 var hasCartesian = fullLayout._has('cartesian');
44447
44448 // Legacy polar plots
44449 if(!fullLayout._has('polar') && data && data[0] && data[0].r) {
44450 Lib.log('Legacy polar charts are deprecated!');
44451 return plotLegacyPolar(gd, data, layout);
44452 }
44453
44454 // so we don't try to re-call Plotly.plot from inside
44455 // legend and colorbar, if margins changed
44456 fullLayout._replotting = true;
44457
44458 // make or remake the framework if we need to
44459 if(graphWasEmpty) makePlotFramework(gd);
44460
44461 // polar need a different framework
44462 if(gd.framework !== makePlotFramework) {
44463 gd.framework = makePlotFramework;
44464 makePlotFramework(gd);
44465 }
44466
44467 // clear gradient defs on each .plot call, because we know we'll loop through all traces
44468 Drawing.initGradients(gd);
44469
44470 // save initial show spikes once per graph
44471 if(graphWasEmpty) Axes.saveShowSpikeInitial(gd);
44472
44473 // prepare the data and find the autorange
44474
44475 // generate calcdata, if we need to
44476 // to force redoing calcdata, just delete it before calling Plotly.plot
44477 var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
44478 if(recalc) Plots.doCalcdata(gd);
44479
44480 // in case it has changed, attach fullData traces to calcdata
44481 for(var i = 0; i < gd.calcdata.length; i++) {
44482 gd.calcdata[i][0].trace = gd._fullData[i];
44483 }
44484
44485 // make the figure responsive
44486 if(gd._context.responsive) {
44487 if(!gd._responsiveChartHandler) {
44488 // Keep a reference to the resize handler to purge it down the road
44489 gd._responsiveChartHandler = function() { if(!Lib.isHidden(gd)) Plots.resize(gd); };
44490
44491 // Listen to window resize
44492 window.addEventListener('resize', gd._responsiveChartHandler);
44493 }
44494 } else {
44495 Lib.clearResponsive(gd);
44496 }
44497
44498 /*
44499 * start async-friendly code - now we're actually drawing things
44500 */
44501
44502 var oldMargins = Lib.extendFlat({}, fullLayout._size);
44503
44504 // draw framework first so that margin-pushing
44505 // components can position themselves correctly
44506 var drawFrameworkCalls = 0;
44507 function drawFramework() {
44508 var basePlotModules = fullLayout._basePlotModules;
44509
44510 for(var i = 0; i < basePlotModules.length; i++) {
44511 if(basePlotModules[i].drawFramework) {
44512 basePlotModules[i].drawFramework(gd);
44513 }
44514 }
44515
44516 if(!fullLayout._glcanvas && fullLayout._has('gl')) {
44517 fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
44518 key: 'contextLayer',
44519 context: true,
44520 pick: false
44521 }, {
44522 key: 'focusLayer',
44523 context: false,
44524 pick: false
44525 }, {
44526 key: 'pickLayer',
44527 context: false,
44528 pick: true
44529 }], function(d) { return d.key; });
44530
44531 fullLayout._glcanvas.enter().append('canvas')
44532 .attr('class', function(d) {
44533 return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
44534 })
44535 .style({
44536 position: 'absolute',
44537 top: 0,
44538 left: 0,
44539 overflow: 'visible',
44540 'pointer-events': 'none'
44541 });
44542 }
44543
44544 if(fullLayout._glcanvas) {
44545 fullLayout._glcanvas
44546 .attr('width', fullLayout.width)
44547 .attr('height', fullLayout.height);
44548
44549 var regl = fullLayout._glcanvas.data()[0].regl;
44550 if(regl) {
44551 // Unfortunately, this can happen when relayouting to large
44552 // width/height on some browsers.
44553 if(Math.floor(fullLayout.width) !== regl._gl.drawingBufferWidth ||
44554 Math.floor(fullLayout.height) !== regl._gl.drawingBufferHeight
44555 ) {
44556 var msg = 'WebGL context buffer and canvas dimensions do not match due to browser/WebGL bug.';
44557 if(drawFrameworkCalls) {
44558 Lib.error(msg);
44559 } else {
44560 Lib.log(msg + ' Clearing graph and plotting again.');
44561 Plots.cleanPlot([], {}, gd._fullData, fullLayout);
44562 Plots.supplyDefaults(gd);
44563 fullLayout = gd._fullLayout;
44564 Plots.doCalcdata(gd);
44565 drawFrameworkCalls++;
44566 return drawFramework();
44567 }
44568 }
44569 }
44570 }
44571
44572 if(fullLayout.modebar.orientation === 'h') {
44573 fullLayout._modebardiv
44574 .style('height', null)
44575 .style('width', '100%');
44576 } else {
44577 fullLayout._modebardiv
44578 .style('width', null)
44579 .style('height', fullLayout.height + 'px');
44580 }
44581
44582 return Plots.previousPromises(gd);
44583 }
44584
44585 // draw anything that can affect margins.
44586 function marginPushers() {
44587 // First reset the list of things that are allowed to change the margins
44588 // So any deleted traces or components will be wiped out of the
44589 // automargin calculation.
44590 // This means *every* margin pusher must be listed here, even if it
44591 // doesn't actually try to push the margins until later.
44592 Plots.clearAutoMarginIds(gd);
44593
44594 subroutines.drawMarginPushers(gd);
44595 Axes.allowAutoMargin(gd);
44596
44597 // TODO can this be moved elsewhere?
44598 if(fullLayout._has('pie')) {
44599 var fullData = gd._fullData;
44600 for(var i = 0; i < fullData.length; i++) {
44601 var trace = fullData[i];
44602 if(trace.type === 'pie' && trace.automargin) {
44603 Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin');
44604 }
44605 }
44606 }
44607
44608 Plots.doAutoMargin(gd);
44609 return Plots.previousPromises(gd);
44610 }
44611
44612 // in case the margins changed, draw margin pushers again
44613 function marginPushersAgain() {
44614 if(!Plots.didMarginChange(oldMargins, fullLayout._size)) return;
44615
44616 return Lib.syncOrAsync([
44617 marginPushers,
44618 subroutines.layoutStyles
44619 ], gd);
44620 }
44621
44622 function positionAndAutorange() {
44623 if(!recalc) {
44624 doAutoRangeAndConstraints();
44625 return;
44626 }
44627
44628 // TODO: autosize extra for text markers and images
44629 // see https://github.com/plotly/plotly.js/issues/1111
44630 return Lib.syncOrAsync([
44631 Registry.getComponentMethod('shapes', 'calcAutorange'),
44632 Registry.getComponentMethod('annotations', 'calcAutorange'),
44633 doAutoRangeAndConstraints
44634 ], gd);
44635 }
44636
44637 function doAutoRangeAndConstraints() {
44638 if(gd._transitioning) return;
44639
44640 subroutines.doAutoRangeAndConstraints(gd);
44641
44642 // store initial ranges *after* enforcing constraints, otherwise
44643 // we will never look like we're at the initial ranges
44644 if(graphWasEmpty) Axes.saveRangeInitial(gd);
44645
44646 // this one is different from shapes/annotations calcAutorange
44647 // the others incorporate those components into ax._extremes,
44648 // this one actually sets the ranges in rangesliders.
44649 Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd);
44650 }
44651
44652 // draw ticks, titles, and calculate axis scaling (._b, ._m)
44653 function drawAxes() {
44654 return Axes.draw(gd, graphWasEmpty ? '' : 'redraw');
44655 }
44656
44657 var seq = [
44658 Plots.previousPromises,
44659 addFrames,
44660 drawFramework,
44661 marginPushers,
44662 marginPushersAgain
44663 ];
44664
44665 if(hasCartesian) seq.push(positionAndAutorange);
44666
44667 seq.push(subroutines.layoutStyles);
44668 if(hasCartesian) seq.push(drawAxes);
44669
44670 seq.push(
44671 subroutines.drawData,
44672 subroutines.finalDraw,
44673 initInteractions,
44674 Plots.addLinks,
44675 Plots.rehover,
44676 Plots.redrag,
44677 // TODO: doAutoMargin is only needed here for axis automargin, which
44678 // happens outside of marginPushers where all the other automargins are
44679 // calculated. Would be much better to separate margin calculations from
44680 // component drawing - see https://github.com/plotly/plotly.js/issues/2704
44681 Plots.doAutoMargin,
44682 Plots.previousPromises
44683 );
44684
44685 // even if everything we did was synchronous, return a promise
44686 // so that the caller doesn't care which route we took
44687 var plotDone = Lib.syncOrAsync(seq, gd);
44688 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
44689
44690 return plotDone.then(function() {
44691 emitAfterPlot(gd);
44692 return gd;
44693 });
44694}
44695
44696function emitAfterPlot(gd) {
44697 var fullLayout = gd._fullLayout;
44698
44699 if(fullLayout._redrawFromAutoMarginCount) {
44700 fullLayout._redrawFromAutoMarginCount--;
44701 } else {
44702 gd.emit('plotly_afterplot');
44703 }
44704}
44705
44706function setPlotConfig(obj) {
44707 return Lib.extendFlat(dfltConfig, obj);
44708}
44709
44710function setBackground(gd, bgColor) {
44711 try {
44712 gd._fullLayout._paper.style('background', bgColor);
44713 } catch(e) {
44714 Lib.error(e);
44715 }
44716}
44717
44718function opaqueSetBackground(gd, bgColor) {
44719 var blend = Color.combine(bgColor, 'white');
44720 setBackground(gd, blend);
44721}
44722
44723function setPlotContext(gd, config) {
44724 if(!gd._context) {
44725 gd._context = Lib.extendDeep({}, dfltConfig);
44726
44727 // stash <base> href, used to make robust clipPath URLs
44728 var base = d3.select('base');
44729 gd._context._baseUrl = base.size() && base.attr('href') ?
44730 window.location.href.split('#')[0] :
44731 '';
44732 }
44733
44734 var context = gd._context;
44735
44736 var i, keys, key;
44737
44738 if(config) {
44739 keys = Object.keys(config);
44740 for(i = 0; i < keys.length; i++) {
44741 key = keys[i];
44742 if(key === 'editable' || key === 'edits') continue;
44743 if(key in context) {
44744 if(key === 'setBackground' && config[key] === 'opaque') {
44745 context[key] = opaqueSetBackground;
44746 } else {
44747 context[key] = config[key];
44748 }
44749 }
44750 }
44751
44752 // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility
44753 if(config.plot3dPixelRatio && !context.plotGlPixelRatio) {
44754 context.plotGlPixelRatio = context.plot3dPixelRatio;
44755 }
44756
44757 // now deal with editable and edits - first editable overrides
44758 // everything, then edits refines
44759 var editable = config.editable;
44760 if(editable !== undefined) {
44761 // we're not going to *use* context.editable, we're only going to
44762 // use context.edits... but keep it for the record
44763 context.editable = editable;
44764
44765 keys = Object.keys(context.edits);
44766 for(i = 0; i < keys.length; i++) {
44767 context.edits[keys[i]] = editable;
44768 }
44769 }
44770 if(config.edits) {
44771 keys = Object.keys(config.edits);
44772 for(i = 0; i < keys.length; i++) {
44773 key = keys[i];
44774 if(key in context.edits) {
44775 context.edits[key] = config.edits[key];
44776 }
44777 }
44778 }
44779
44780 // not part of the user-facing config options
44781 context._exportedPlot = config._exportedPlot;
44782 }
44783
44784 // staticPlot forces a bunch of others:
44785 if(context.staticPlot) {
44786 context.editable = false;
44787 context.edits = {};
44788 context.autosizable = false;
44789 context.scrollZoom = false;
44790 context.doubleClick = false;
44791 context.showTips = false;
44792 context.showLink = false;
44793 context.displayModeBar = false;
44794 }
44795
44796 // make sure hover-only devices have mode bar visible
44797 if(context.displayModeBar === 'hover' && !hasHover) {
44798 context.displayModeBar = true;
44799 }
44800
44801 // default and fallback for setBackground
44802 if(context.setBackground === 'transparent' || typeof context.setBackground !== 'function') {
44803 context.setBackground = setBackground;
44804 }
44805
44806 // Check if gd has a specified widht/height to begin with
44807 context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
44808 context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;
44809
44810 // fill context._scrollZoom helper to help manage scrollZoom flaglist
44811 var szIn = context.scrollZoom;
44812 var szOut = context._scrollZoom = {};
44813 if(szIn === true) {
44814 szOut.cartesian = 1;
44815 szOut.gl3d = 1;
44816 szOut.geo = 1;
44817 szOut.mapbox = 1;
44818 } else if(typeof szIn === 'string') {
44819 var parts = szIn.split('+');
44820 for(i = 0; i < parts.length; i++) {
44821 szOut[parts[i]] = 1;
44822 }
44823 } else if(szIn !== false) {
44824 szOut.gl3d = 1;
44825 szOut.geo = 1;
44826 szOut.mapbox = 1;
44827 }
44828}
44829
44830function plotLegacyPolar(gd, data, layout) {
44831 // build or reuse the container skeleton
44832 var plotContainer = d3.select(gd).selectAll('.plot-container')
44833 .data([0]);
44834 plotContainer.enter()
44835 .insert('div', ':first-child')
44836 .classed('plot-container plotly', true);
44837 var paperDiv = plotContainer.selectAll('.svg-container')
44838 .data([0]);
44839 paperDiv.enter().append('div')
44840 .classed('svg-container', true)
44841 .style('position', 'relative');
44842
44843 // empty it everytime for now
44844 paperDiv.html('');
44845
44846 // fulfill gd requirements
44847 if(data) gd.data = data;
44848 if(layout) gd.layout = layout;
44849 Polar.manager.fillLayout(gd);
44850
44851 // resize canvas
44852 paperDiv.style({
44853 width: gd._fullLayout.width + 'px',
44854 height: gd._fullLayout.height + 'px'
44855 });
44856
44857 // instantiate framework
44858 gd.framework = Polar.manager.framework(gd);
44859
44860 // plot
44861 gd.framework({data: gd.data, layout: gd.layout}, paperDiv.node());
44862
44863 // set undo point
44864 gd.framework.setUndoPoint();
44865
44866 // get the resulting svg for extending it
44867 var polarPlotSVG = gd.framework.svg();
44868
44869 // editable title
44870 var opacity = 1;
44871 var txt = gd._fullLayout.title ? gd._fullLayout.title.text : '';
44872 if(txt === '' || !txt) opacity = 0;
44873
44874 var titleLayout = function() {
44875 this.call(svgTextUtils.convertToTspans, gd);
44876 // TODO: html/mathjax
44877 // TODO: center title
44878 };
44879
44880 var title = polarPlotSVG.select('.title-group text')
44881 .call(titleLayout);
44882
44883 if(gd._context.edits.titleText) {
44884 var placeholderText = Lib._(gd, 'Click to enter Plot title');
44885 if(!txt || txt === placeholderText) {
44886 opacity = 0.2;
44887 // placeholder is not going through convertToTspans
44888 // so needs explicit data-unformatted
44889 title.attr({'data-unformatted': placeholderText})
44890 .text(placeholderText)
44891 .style({opacity: opacity})
44892 .on('mouseover.opacity', function() {
44893 d3.select(this).transition().duration(100)
44894 .style('opacity', 1);
44895 })
44896 .on('mouseout.opacity', function() {
44897 d3.select(this).transition().duration(1000)
44898 .style('opacity', 0);
44899 });
44900 }
44901
44902 var setContenteditable = function() {
44903 this.call(svgTextUtils.makeEditable, {gd: gd})
44904 .on('edit', function(text) {
44905 gd.framework({layout: {title: {text: text}}});
44906 this.text(text)
44907 .call(titleLayout);
44908 this.call(setContenteditable);
44909 })
44910 .on('cancel', function() {
44911 var txt = this.attr('data-unformatted');
44912 this.text(txt).call(titleLayout);
44913 });
44914 };
44915 title.call(setContenteditable);
44916 }
44917
44918 gd._context.setBackground(gd, gd._fullLayout.paper_bgcolor);
44919 Plots.addLinks(gd);
44920
44921 return Promise.resolve();
44922}
44923
44924// convenience function to force a full redraw, mostly for use by plotly.js
44925function redraw(gd) {
44926 gd = Lib.getGraphDiv(gd);
44927
44928 if(!Lib.isPlotDiv(gd)) {
44929 throw new Error('This element is not a Plotly plot: ' + gd);
44930 }
44931
44932 helpers.cleanData(gd.data);
44933 helpers.cleanLayout(gd.layout);
44934
44935 gd.calcdata = undefined;
44936 return exports.plot(gd).then(function() {
44937 gd.emit('plotly_redraw');
44938 return gd;
44939 });
44940}
44941
44942/**
44943 * Convenience function to make idempotent plot option obvious to users.
44944 *
44945 * @param gd
44946 * @param {Object[]} data
44947 * @param {Object} layout
44948 * @param {Object} config
44949 */
44950function newPlot(gd, data, layout, config) {
44951 gd = Lib.getGraphDiv(gd);
44952
44953 // remove gl contexts
44954 Plots.cleanPlot([], {}, gd._fullData || [], gd._fullLayout || {});
44955
44956 Plots.purge(gd);
44957 return exports.plot(gd, data, layout, config);
44958}
44959
44960/**
44961 * Wrap negative indicies to their positive counterparts.
44962 *
44963 * @param {Number[]} indices An array of indices
44964 * @param {Number} maxIndex The maximum index allowable (arr.length - 1)
44965 */
44966function positivifyIndices(indices, maxIndex) {
44967 var parentLength = maxIndex + 1;
44968 var positiveIndices = [];
44969 var i;
44970 var index;
44971
44972 for(i = 0; i < indices.length; i++) {
44973 index = indices[i];
44974 if(index < 0) {
44975 positiveIndices.push(parentLength + index);
44976 } else {
44977 positiveIndices.push(index);
44978 }
44979 }
44980 return positiveIndices;
44981}
44982
44983/**
44984 * Ensures that an index array for manipulating gd.data is valid.
44985 *
44986 * Intended for use with addTraces, deleteTraces, and moveTraces.
44987 *
44988 * @param gd
44989 * @param indices
44990 * @param arrayName
44991 */
44992function assertIndexArray(gd, indices, arrayName) {
44993 var i,
44994 index;
44995
44996 for(i = 0; i < indices.length; i++) {
44997 index = indices[i];
44998
44999 // validate that indices are indeed integers
45000 if(index !== parseInt(index, 10)) {
45001 throw new Error('all values in ' + arrayName + ' must be integers');
45002 }
45003
45004 // check that all indices are in bounds for given gd.data array length
45005 if(index >= gd.data.length || index < -gd.data.length) {
45006 throw new Error(arrayName + ' must be valid indices for gd.data.');
45007 }
45008
45009 // check that indices aren't repeated
45010 if(indices.indexOf(index, i + 1) > -1 ||
45011 index >= 0 && indices.indexOf(-gd.data.length + index) > -1 ||
45012 index < 0 && indices.indexOf(gd.data.length + index) > -1) {
45013 throw new Error('each index in ' + arrayName + ' must be unique.');
45014 }
45015 }
45016}
45017
45018/**
45019 * Private function used by Plotly.moveTraces to check input args
45020 *
45021 * @param gd
45022 * @param currentIndices
45023 * @param newIndices
45024 */
45025function checkMoveTracesArgs(gd, currentIndices, newIndices) {
45026 // check that gd has attribute 'data' and 'data' is array
45027 if(!Array.isArray(gd.data)) {
45028 throw new Error('gd.data must be an array.');
45029 }
45030
45031 // validate currentIndices array
45032 if(typeof currentIndices === 'undefined') {
45033 throw new Error('currentIndices is a required argument.');
45034 } else if(!Array.isArray(currentIndices)) {
45035 currentIndices = [currentIndices];
45036 }
45037 assertIndexArray(gd, currentIndices, 'currentIndices');
45038
45039 // validate newIndices array if it exists
45040 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
45041 newIndices = [newIndices];
45042 }
45043 if(typeof newIndices !== 'undefined') {
45044 assertIndexArray(gd, newIndices, 'newIndices');
45045 }
45046
45047 // check currentIndices and newIndices are the same length if newIdices exists
45048 if(typeof newIndices !== 'undefined' && currentIndices.length !== newIndices.length) {
45049 throw new Error('current and new indices must be of equal length.');
45050 }
45051}
45052/**
45053 * A private function to reduce the type checking clutter in addTraces.
45054 *
45055 * @param gd
45056 * @param traces
45057 * @param newIndices
45058 */
45059function checkAddTracesArgs(gd, traces, newIndices) {
45060 var i, value;
45061
45062 // check that gd has attribute 'data' and 'data' is array
45063 if(!Array.isArray(gd.data)) {
45064 throw new Error('gd.data must be an array.');
45065 }
45066
45067 // make sure traces exists
45068 if(typeof traces === 'undefined') {
45069 throw new Error('traces must be defined.');
45070 }
45071
45072 // make sure traces is an array
45073 if(!Array.isArray(traces)) {
45074 traces = [traces];
45075 }
45076
45077 // make sure each value in traces is an object
45078 for(i = 0; i < traces.length; i++) {
45079 value = traces[i];
45080 if(typeof value !== 'object' || (Array.isArray(value) || value === null)) {
45081 throw new Error('all values in traces array must be non-array objects');
45082 }
45083 }
45084
45085 // make sure we have an index for each trace
45086 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
45087 newIndices = [newIndices];
45088 }
45089 if(typeof newIndices !== 'undefined' && newIndices.length !== traces.length) {
45090 throw new Error(
45091 'if indices is specified, traces.length must equal indices.length'
45092 );
45093 }
45094}
45095
45096/**
45097 * A private function to reduce the type checking clutter in spliceTraces.
45098 * Get all update Properties from gd.data. Validate inputs and outputs.
45099 * Used by prependTrace and extendTraces
45100 *
45101 * @param gd
45102 * @param update
45103 * @param indices
45104 * @param maxPoints
45105 */
45106function assertExtendTracesArgs(gd, update, indices, maxPoints) {
45107 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
45108
45109 if(!Array.isArray(gd.data)) {
45110 throw new Error('gd.data must be an array');
45111 }
45112 if(!Lib.isPlainObject(update)) {
45113 throw new Error('update must be a key:value object');
45114 }
45115
45116 if(typeof indices === 'undefined') {
45117 throw new Error('indices must be an integer or array of integers');
45118 }
45119
45120 assertIndexArray(gd, indices, 'indices');
45121
45122 for(var key in update) {
45123 /*
45124 * Verify that the attribute to be updated contains as many trace updates
45125 * as indices. Failure must result in throw and no-op
45126 */
45127 if(!Array.isArray(update[key]) || update[key].length !== indices.length) {
45128 throw new Error('attribute ' + key + ' must be an array of length equal to indices array length');
45129 }
45130
45131 /*
45132 * if maxPoints is an object it must match keys and array lengths of 'update' 1:1
45133 */
45134 if(maxPointsIsObject &&
45135 (!(key in maxPoints) || !Array.isArray(maxPoints[key]) ||
45136 maxPoints[key].length !== update[key].length)) {
45137 throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' +
45138 'corrispondence with the keys and number of traces in the update object');
45139 }
45140 }
45141}
45142
45143/**
45144 * A private function to reduce the type checking clutter in spliceTraces.
45145 *
45146 * @param {Object|HTMLDivElement} gd
45147 * @param {Object} update
45148 * @param {Number[]} indices
45149 * @param {Number||Object} maxPoints
45150 * @return {Object[]}
45151 */
45152function getExtendProperties(gd, update, indices, maxPoints) {
45153 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
45154 var updateProps = [];
45155 var trace, target, prop, insert, maxp;
45156
45157 // allow scalar index to represent a single trace position
45158 if(!Array.isArray(indices)) indices = [indices];
45159
45160 // negative indices are wrapped around to their positive value. Equivalent to python indexing.
45161 indices = positivifyIndices(indices, gd.data.length - 1);
45162
45163 // loop through all update keys and traces and harvest validated data.
45164 for(var key in update) {
45165 for(var j = 0; j < indices.length; j++) {
45166 /*
45167 * Choose the trace indexed by the indices map argument and get the prop setter-getter
45168 * instance that references the key and value for this particular trace.
45169 */
45170 trace = gd.data[indices[j]];
45171 prop = nestedProperty(trace, key);
45172
45173 /*
45174 * Target is the existing gd.data.trace.dataArray value like "x" or "marker.size"
45175 * Target must exist as an Array to allow the extend operation to be performed.
45176 */
45177 target = prop.get();
45178 insert = update[key][j];
45179
45180 if(!Lib.isArrayOrTypedArray(insert)) {
45181 throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
45182 }
45183 if(!Lib.isArrayOrTypedArray(target)) {
45184 throw new Error('cannot extend missing or non-array attribute: ' + key);
45185 }
45186 if(target.constructor !== insert.constructor) {
45187 throw new Error('cannot extend array with an array of a different type: ' + key);
45188 }
45189
45190 /*
45191 * maxPoints may be an object map or a scalar. If object select the key:value, else
45192 * Use the scalar maxPoints for all key and trace combinations.
45193 */
45194 maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints;
45195
45196 // could have chosen null here, -1 just tells us to not take a window
45197 if(!isNumeric(maxp)) maxp = -1;
45198
45199 /*
45200 * Wrap the nestedProperty in an object containing required data
45201 * for lengthening and windowing this particular trace - key combination.
45202 * Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function.
45203 */
45204 updateProps.push({
45205 prop: prop,
45206 target: target,
45207 insert: insert,
45208 maxp: Math.floor(maxp)
45209 });
45210 }
45211 }
45212
45213 // all target and insertion data now validated
45214 return updateProps;
45215}
45216
45217/**
45218 * A private function to key Extend and Prepend traces DRY
45219 *
45220 * @param {Object|HTMLDivElement} gd
45221 * @param {Object} update
45222 * @param {Number[]} indices
45223 * @param {Number||Object} maxPoints
45224 * @param {Function} updateArray
45225 * @return {Object}
45226 */
45227function spliceTraces(gd, update, indices, maxPoints, updateArray) {
45228 assertExtendTracesArgs(gd, update, indices, maxPoints);
45229
45230 var updateProps = getExtendProperties(gd, update, indices, maxPoints);
45231 var undoUpdate = {};
45232 var undoPoints = {};
45233
45234 for(var i = 0; i < updateProps.length; i++) {
45235 var prop = updateProps[i].prop;
45236 var maxp = updateProps[i].maxp;
45237
45238 // return new array and remainder
45239 var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp);
45240 prop.set(out[0]);
45241
45242 // build the inverse update object for the undo operation
45243 if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
45244 undoUpdate[prop.astr].push(out[1]);
45245
45246 // build the matching maxPoints undo object containing original trace lengths
45247 if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
45248 undoPoints[prop.astr].push(updateProps[i].target.length);
45249 }
45250
45251 return {update: undoUpdate, maxPoints: undoPoints};
45252}
45253
45254function concatTypedArray(arr0, arr1) {
45255 var arr2 = new arr0.constructor(arr0.length + arr1.length);
45256 arr2.set(arr0);
45257 arr2.set(arr1, arr0.length);
45258 return arr2;
45259}
45260
45261/**
45262 * extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
45263 *
45264 * Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend
45265 * inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints
45266 * from the head, whereas Extend truncates the head of the array, counting backward maxPoints
45267 * from the tail.
45268 *
45269 * If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no
45270 * truncation / windowing will be performed. If its zero, well the whole trace is truncated.
45271 *
45272 * @param {Object|HTMLDivElement} gd The graph div
45273 * @param {Object} update The key:array map of target attributes to extend
45274 * @param {Number|Number[]} indices The locations of traces to be extended
45275 * @param {Number|Object} [maxPoints] Number of points for trace window after lengthening.
45276 *
45277 */
45278function extendTraces(gd, update, indices, maxPoints) {
45279 gd = Lib.getGraphDiv(gd);
45280
45281 function updateArray(target, insert, maxp) {
45282 var newArray, remainder;
45283
45284 if(Lib.isTypedArray(target)) {
45285 if(maxp < 0) {
45286 var none = new target.constructor(0);
45287 var both = concatTypedArray(target, insert);
45288
45289 if(maxp < 0) {
45290 newArray = both;
45291 remainder = none;
45292 } else {
45293 newArray = none;
45294 remainder = both;
45295 }
45296 } else {
45297 newArray = new target.constructor(maxp);
45298 remainder = new target.constructor(target.length + insert.length - maxp);
45299
45300 if(maxp === insert.length) {
45301 newArray.set(insert);
45302 remainder.set(target);
45303 } else if(maxp < insert.length) {
45304 var numberOfItemsFromInsert = insert.length - maxp;
45305
45306 newArray.set(insert.subarray(numberOfItemsFromInsert));
45307 remainder.set(target);
45308 remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length);
45309 } else {
45310 var numberOfItemsFromTarget = maxp - insert.length;
45311 var targetBegin = target.length - numberOfItemsFromTarget;
45312
45313 newArray.set(target.subarray(targetBegin));
45314 newArray.set(insert, numberOfItemsFromTarget);
45315 remainder.set(target.subarray(0, targetBegin));
45316 }
45317 }
45318 } else {
45319 newArray = target.concat(insert);
45320 remainder = (maxp >= 0 && maxp < newArray.length) ?
45321 newArray.splice(0, newArray.length - maxp) :
45322 [];
45323 }
45324
45325 return [newArray, remainder];
45326 }
45327
45328 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
45329 var promise = exports.redraw(gd);
45330 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
45331 Queue.add(gd, exports.prependTraces, undoArgs, extendTraces, arguments);
45332
45333 return promise;
45334}
45335
45336function prependTraces(gd, update, indices, maxPoints) {
45337 gd = Lib.getGraphDiv(gd);
45338
45339 function updateArray(target, insert, maxp) {
45340 var newArray, remainder;
45341
45342 if(Lib.isTypedArray(target)) {
45343 if(maxp <= 0) {
45344 var none = new target.constructor(0);
45345 var both = concatTypedArray(insert, target);
45346
45347 if(maxp < 0) {
45348 newArray = both;
45349 remainder = none;
45350 } else {
45351 newArray = none;
45352 remainder = both;
45353 }
45354 } else {
45355 newArray = new target.constructor(maxp);
45356 remainder = new target.constructor(target.length + insert.length - maxp);
45357
45358 if(maxp === insert.length) {
45359 newArray.set(insert);
45360 remainder.set(target);
45361 } else if(maxp < insert.length) {
45362 var numberOfItemsFromInsert = insert.length - maxp;
45363
45364 newArray.set(insert.subarray(0, numberOfItemsFromInsert));
45365 remainder.set(insert.subarray(numberOfItemsFromInsert));
45366 remainder.set(target, numberOfItemsFromInsert);
45367 } else {
45368 var numberOfItemsFromTarget = maxp - insert.length;
45369
45370 newArray.set(insert);
45371 newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length);
45372 remainder.set(target.subarray(numberOfItemsFromTarget));
45373 }
45374 }
45375 } else {
45376 newArray = insert.concat(target);
45377 remainder = (maxp >= 0 && maxp < newArray.length) ?
45378 newArray.splice(maxp, newArray.length) :
45379 [];
45380 }
45381
45382 return [newArray, remainder];
45383 }
45384
45385 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
45386 var promise = exports.redraw(gd);
45387 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
45388 Queue.add(gd, exports.extendTraces, undoArgs, prependTraces, arguments);
45389
45390 return promise;
45391}
45392
45393/**
45394 * Add data traces to an existing graph div.
45395 *
45396 * @param {Object|HTMLDivElement} gd The graph div
45397 * @param {Object[]} gd.data The array of traces we're adding to
45398 * @param {Object[]|Object} traces The object or array of objects to add
45399 * @param {Number[]|Number} [newIndices=[gd.data.length]] Locations to add traces
45400 *
45401 */
45402function addTraces(gd, traces, newIndices) {
45403 gd = Lib.getGraphDiv(gd);
45404
45405 var currentIndices = [];
45406 var undoFunc = exports.deleteTraces;
45407 var redoFunc = addTraces;
45408 var undoArgs = [gd, currentIndices];
45409 var redoArgs = [gd, traces]; // no newIndices here
45410 var i;
45411 var promise;
45412
45413 // all validation is done elsewhere to remove clutter here
45414 checkAddTracesArgs(gd, traces, newIndices);
45415
45416 // make sure traces is an array
45417 if(!Array.isArray(traces)) {
45418 traces = [traces];
45419 }
45420
45421 // make sure traces do not repeat existing ones
45422 traces = traces.map(function(trace) {
45423 return Lib.extendFlat({}, trace);
45424 });
45425
45426 helpers.cleanData(traces);
45427
45428 // add the traces to gd.data (no redrawing yet!)
45429 for(i = 0; i < traces.length; i++) {
45430 gd.data.push(traces[i]);
45431 }
45432
45433 // to continue, we need to call moveTraces which requires currentIndices
45434 for(i = 0; i < traces.length; i++) {
45435 currentIndices.push(-traces.length + i);
45436 }
45437
45438 // if the user didn't define newIndices, they just want the traces appended
45439 // i.e., we can simply redraw and be done
45440 if(typeof newIndices === 'undefined') {
45441 promise = exports.redraw(gd);
45442 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
45443 return promise;
45444 }
45445
45446 // make sure indices is property defined
45447 if(!Array.isArray(newIndices)) {
45448 newIndices = [newIndices];
45449 }
45450
45451 try {
45452 // this is redundant, but necessary to not catch later possible errors!
45453 checkMoveTracesArgs(gd, currentIndices, newIndices);
45454 } catch(error) {
45455 // something went wrong, reset gd to be safe and rethrow error
45456 gd.data.splice(gd.data.length - traces.length, traces.length);
45457 throw error;
45458 }
45459
45460 // if we're here, the user has defined specific places to place the new traces
45461 // this requires some extra work that moveTraces will do
45462 Queue.startSequence(gd);
45463 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
45464 promise = exports.moveTraces(gd, currentIndices, newIndices);
45465 Queue.stopSequence(gd);
45466 return promise;
45467}
45468
45469/**
45470 * Delete traces at `indices` from gd.data array.
45471 *
45472 * @param {Object|HTMLDivElement} gd The graph div
45473 * @param {Object[]} gd.data The array of traces we're removing from
45474 * @param {Number|Number[]} indices The indices
45475 */
45476function deleteTraces(gd, indices) {
45477 gd = Lib.getGraphDiv(gd);
45478
45479 var traces = [];
45480 var undoFunc = exports.addTraces;
45481 var redoFunc = deleteTraces;
45482 var undoArgs = [gd, traces, indices];
45483 var redoArgs = [gd, indices];
45484 var i;
45485 var deletedTrace;
45486
45487 // make sure indices are defined
45488 if(typeof indices === 'undefined') {
45489 throw new Error('indices must be an integer or array of integers.');
45490 } else if(!Array.isArray(indices)) {
45491 indices = [indices];
45492 }
45493 assertIndexArray(gd, indices, 'indices');
45494
45495 // convert negative indices to positive indices
45496 indices = positivifyIndices(indices, gd.data.length - 1);
45497
45498 // we want descending here so that splicing later doesn't affect indexing
45499 indices.sort(Lib.sorterDes);
45500 for(i = 0; i < indices.length; i += 1) {
45501 deletedTrace = gd.data.splice(indices[i], 1)[0];
45502 traces.push(deletedTrace);
45503 }
45504
45505 var promise = exports.redraw(gd);
45506 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
45507
45508 return promise;
45509}
45510
45511/**
45512 * Move traces at currentIndices array to locations in newIndices array.
45513 *
45514 * If newIndices is omitted, currentIndices will be moved to the end. E.g.,
45515 * these are equivalent:
45516 *
45517 * Plotly.moveTraces(gd, [1, 2, 3], [-3, -2, -1])
45518 * Plotly.moveTraces(gd, [1, 2, 3])
45519 *
45520 * @param {Object|HTMLDivElement} gd The graph div
45521 * @param {Object[]} gd.data The array of traces we're removing from
45522 * @param {Number|Number[]} currentIndices The locations of traces to be moved
45523 * @param {Number|Number[]} [newIndices] The locations to move traces to
45524 *
45525 * Example calls:
45526 *
45527 * // move trace i to location x
45528 * Plotly.moveTraces(gd, i, x)
45529 *
45530 * // move trace i to end of array
45531 * Plotly.moveTraces(gd, i)
45532 *
45533 * // move traces i, j, k to end of array (i != j != k)
45534 * Plotly.moveTraces(gd, [i, j, k])
45535 *
45536 * // move traces [i, j, k] to [x, y, z] (i != j != k) (x != y != z)
45537 * Plotly.moveTraces(gd, [i, j, k], [x, y, z])
45538 *
45539 * // reorder all traces (assume there are 5--a, b, c, d, e)
45540 * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end'
45541 */
45542function moveTraces(gd, currentIndices, newIndices) {
45543 gd = Lib.getGraphDiv(gd);
45544
45545 var newData = [];
45546 var movingTraceMap = [];
45547 var undoFunc = moveTraces;
45548 var redoFunc = moveTraces;
45549 var undoArgs = [gd, newIndices, currentIndices];
45550 var redoArgs = [gd, currentIndices, newIndices];
45551 var i;
45552
45553 // to reduce complexity here, check args elsewhere
45554 // this throws errors where appropriate
45555 checkMoveTracesArgs(gd, currentIndices, newIndices);
45556
45557 // make sure currentIndices is an array
45558 currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices];
45559
45560 // if undefined, define newIndices to point to the end of gd.data array
45561 if(typeof newIndices === 'undefined') {
45562 newIndices = [];
45563 for(i = 0; i < currentIndices.length; i++) {
45564 newIndices.push(-currentIndices.length + i);
45565 }
45566 }
45567
45568 // make sure newIndices is an array if it's user-defined
45569 newIndices = Array.isArray(newIndices) ? newIndices : [newIndices];
45570
45571 // convert negative indices to positive indices (they're the same length)
45572 currentIndices = positivifyIndices(currentIndices, gd.data.length - 1);
45573 newIndices = positivifyIndices(newIndices, gd.data.length - 1);
45574
45575 // at this point, we've coerced the index arrays into predictable forms
45576
45577 // get the traces that aren't being moved around
45578 for(i = 0; i < gd.data.length; i++) {
45579 // if index isn't in currentIndices, include it in ignored!
45580 if(currentIndices.indexOf(i) === -1) {
45581 newData.push(gd.data[i]);
45582 }
45583 }
45584
45585 // get a mapping of indices to moving traces
45586 for(i = 0; i < currentIndices.length; i++) {
45587 movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]});
45588 }
45589
45590 // reorder this mapping by newIndex, ascending
45591 movingTraceMap.sort(function(a, b) {
45592 return a.newIndex - b.newIndex;
45593 });
45594
45595 // now, add the moving traces back in, in order!
45596 for(i = 0; i < movingTraceMap.length; i += 1) {
45597 newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace);
45598 }
45599
45600 gd.data = newData;
45601
45602 var promise = exports.redraw(gd);
45603 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
45604
45605 return promise;
45606}
45607
45608/**
45609 * restyle: update trace attributes of an existing plot
45610 *
45611 * Can be called two ways.
45612 *
45613 * Signature 1:
45614 * @param {String | HTMLDivElement} gd
45615 * the id or DOM element of the graph container div
45616 * @param {String} astr
45617 * attribute string (like `'marker.symbol'`) to update
45618 * @param {*} val
45619 * value to give this attribute
45620 * @param {Number[] | Number} [traces]
45621 * integer or array of integers for the traces to alter (all if omitted)
45622 *
45623 * Signature 2:
45624 * @param {String | HTMLDivElement} gd
45625 * (as in signature 1)
45626 * @param {Object} aobj
45627 * attribute object `{astr1: val1, astr2: val2 ...}`
45628 * allows setting multiple attributes simultaneously
45629 * @param {Number[] | Number} [traces]
45630 * (as in signature 1)
45631 *
45632 * `val` (or `val1`, `val2` ... in the object form) can be an array,
45633 * to apply different values to each trace.
45634 *
45635 * If the array is too short, it will wrap around (useful for
45636 * style files that want to specify cyclical default values).
45637 */
45638function restyle(gd, astr, val, _traces) {
45639 gd = Lib.getGraphDiv(gd);
45640 helpers.clearPromiseQueue(gd);
45641
45642 var aobj = {};
45643 if(typeof astr === 'string') aobj[astr] = val;
45644 else if(Lib.isPlainObject(astr)) {
45645 // the 3-arg form
45646 aobj = Lib.extendFlat({}, astr);
45647 if(_traces === undefined) _traces = val;
45648 } else {
45649 Lib.warn('Restyle fail.', astr, val, _traces);
45650 return Promise.reject();
45651 }
45652
45653 if(Object.keys(aobj).length) gd.changed = true;
45654
45655 var traces = helpers.coerceTraceIndices(gd, _traces);
45656
45657 var specs = _restyle(gd, aobj, traces);
45658 var flags = specs.flags;
45659
45660 // clear calcdata and/or axis types if required so they get regenerated
45661 if(flags.calc) gd.calcdata = undefined;
45662 if(flags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, {});
45663
45664 // fill in redraw sequence
45665 var seq = [];
45666
45667 if(flags.fullReplot) {
45668 seq.push(exports.plot);
45669 } else {
45670 seq.push(Plots.previousPromises);
45671
45672 // maybe only call Plots.supplyDataDefaults in the splom case,
45673 // to skip over long and slow axes defaults
45674 Plots.supplyDefaults(gd);
45675
45676 if(flags.markerSize) {
45677 Plots.doCalcdata(gd);
45678 addAxRangeSequence(seq);
45679
45680 // TODO
45681 // if all axes have autorange:false, then
45682 // proceed to subroutines.doTraceStyle(),
45683 // otherwise we must go through addAxRangeSequence,
45684 // which in general must redraws 'all' axes
45685 }
45686
45687 if(flags.style) seq.push(subroutines.doTraceStyle);
45688 if(flags.colorbars) seq.push(subroutines.doColorBars);
45689
45690 seq.push(emitAfterPlot);
45691 }
45692
45693 seq.push(Plots.rehover, Plots.redrag);
45694
45695 Queue.add(gd,
45696 restyle, [gd, specs.undoit, specs.traces],
45697 restyle, [gd, specs.redoit, specs.traces]
45698 );
45699
45700 var plotDone = Lib.syncOrAsync(seq, gd);
45701 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
45702
45703 return plotDone.then(function() {
45704 gd.emit('plotly_restyle', specs.eventData);
45705 return gd;
45706 });
45707}
45708
45709// for undo: undefined initial vals must be turned into nulls
45710// so that we unset rather than ignore them
45711function undefinedToNull(val) {
45712 if(val === undefined) return null;
45713 return val;
45714}
45715
45716/**
45717 * Factory function to wrap nestedProperty with GUI edits if necessary
45718 * with GUI edits we add an optional prefix to the nestedProperty constructor
45719 * to prepend to the attribute string in the preGUI store.
45720 */
45721function makeNP(preGUI, guiEditFlag) {
45722 if(!guiEditFlag) return nestedProperty;
45723
45724 return function(container, attr, prefix) {
45725 var np = nestedProperty(container, attr);
45726 var npSet = np.set;
45727 np.set = function(val) {
45728 var fullAttr = (prefix || '') + attr;
45729 storeCurrent(fullAttr, np.get(), val, preGUI);
45730 npSet(val);
45731 };
45732 return np;
45733 };
45734}
45735
45736function storeCurrent(attr, val, newVal, preGUI) {
45737 if(Array.isArray(val) || Array.isArray(newVal)) {
45738 var arrayVal = Array.isArray(val) ? val : [];
45739 var arrayNew = Array.isArray(newVal) ? newVal : [];
45740 var maxLen = Math.max(arrayVal.length, arrayNew.length);
45741 for(var i = 0; i < maxLen; i++) {
45742 storeCurrent(attr + '[' + i + ']', arrayVal[i], arrayNew[i], preGUI);
45743 }
45744 } else if(Lib.isPlainObject(val) || Lib.isPlainObject(newVal)) {
45745 var objVal = Lib.isPlainObject(val) ? val : {};
45746 var objNew = Lib.isPlainObject(newVal) ? newVal : {};
45747 var objBoth = Lib.extendFlat({}, objVal, objNew);
45748 for(var key in objBoth) {
45749 storeCurrent(attr + '.' + key, objVal[key], objNew[key], preGUI);
45750 }
45751 } else if(preGUI[attr] === undefined) {
45752 preGUI[attr] = undefinedToNull(val);
45753 }
45754}
45755
45756/**
45757 * storeDirectGUIEdit: for routines that skip restyle/relayout and mock it
45758 * by emitting a plotly_restyle or plotly_relayout event, this routine
45759 * keeps track of the initial state in _preGUI for use by uirevision
45760 * Does *not* apply these changes to data/layout - that's the responsibility
45761 * of the calling routine.
45762 *
45763 * @param {object} container: the input attributes container (eg `layout` or a `trace`)
45764 * @param {object} preGUI: where original values should be stored, either
45765 * `layout._preGUI` or `layout._tracePreGUI[uid]`
45766 * @param {object} edits: the {attr: val} object as normally passed to `relayout` etc
45767 */
45768function _storeDirectGUIEdit(container, preGUI, edits) {
45769 for(var attr in edits) {
45770 var np = nestedProperty(container, attr);
45771 storeCurrent(attr, np.get(), edits[attr], preGUI);
45772 }
45773}
45774
45775function _restyle(gd, aobj, traces) {
45776 var fullLayout = gd._fullLayout;
45777 var fullData = gd._fullData;
45778 var data = gd.data;
45779 var guiEditFlag = fullLayout._guiEditing;
45780 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
45781 var eventData = Lib.extendDeepAll({}, aobj);
45782 var i;
45783
45784 cleanDeprecatedAttributeKeys(aobj);
45785
45786 // initialize flags
45787 var flags = editTypes.traceFlags();
45788
45789 // copies of the change (and previous values of anything affected)
45790 // for the undo / redo queue
45791 var redoit = {};
45792 var undoit = {};
45793 var axlist;
45794
45795 // make a new empty vals array for undoit
45796 function a0() { return traces.map(function() { return undefined; }); }
45797
45798 // for autoranging multiple axes
45799 function addToAxlist(axid) {
45800 var axName = Axes.id2name(axid);
45801 if(axlist.indexOf(axName) === -1) axlist.push(axName);
45802 }
45803
45804 function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
45805
45806 function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
45807
45808 function getFullTrace(traceIndex) {
45809 // usually fullData maps 1:1 onto data, but with groupby transforms
45810 // the fullData index can be greater. Take the *first* matching trace.
45811 for(var j = traceIndex; j < fullData.length; j++) {
45812 if(fullData[j]._input === data[traceIndex]) return fullData[j];
45813 }
45814 // should never get here - and if we *do* it should cause an error
45815 // later on undefined fullTrace is passed to nestedProperty.
45816 }
45817
45818 // for attrs that interact (like scales & autoscales), save the
45819 // old vals before making the change
45820 // val=undefined will not set a value, just record what the value was.
45821 // val=null will delete the attribute
45822 // attr can be an array to set several at once (all to the same val)
45823 function doextra(attr, val, i) {
45824 if(Array.isArray(attr)) {
45825 attr.forEach(function(a) { doextra(a, val, i); });
45826 return;
45827 }
45828 // quit if explicitly setting this elsewhere
45829 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
45830
45831 var extraparam;
45832 if(attr.substr(0, 6) === 'LAYOUT') {
45833 extraparam = layoutNP(gd.layout, attr.replace('LAYOUT', ''));
45834 } else {
45835 var tracei = traces[i];
45836 var preGUI = fullLayout._tracePreGUI[getFullTrace(tracei)._fullInput.uid];
45837 extraparam = makeNP(preGUI, guiEditFlag)(data[tracei], attr);
45838 }
45839
45840 if(!(attr in undoit)) {
45841 undoit[attr] = a0();
45842 }
45843 if(undoit[attr][i] === undefined) {
45844 undoit[attr][i] = undefinedToNull(extraparam.get());
45845 }
45846 if(val !== undefined) {
45847 extraparam.set(val);
45848 }
45849 }
45850
45851 function allBins(binAttr) {
45852 return function(j) {
45853 return fullData[j][binAttr];
45854 };
45855 }
45856
45857 function arrayBins(binAttr) {
45858 return function(vij, j) {
45859 return vij === false ? fullData[traces[j]][binAttr] : null;
45860 };
45861 }
45862
45863 // now make the changes to gd.data (and occasionally gd.layout)
45864 // and figure out what kind of graphics update we need to do
45865 for(var ai in aobj) {
45866 if(helpers.hasParent(aobj, ai)) {
45867 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
45868 }
45869
45870 var vi = aobj[ai];
45871 var cont;
45872 var contFull;
45873 var param;
45874 var oldVal;
45875 var newVal;
45876 var valObject;
45877
45878 // Backward compatibility shim for turning histogram autobin on,
45879 // or freezing previous autobinned values.
45880 // Replace obsolete `autobin(x|y): true` with `(x|y)bins: null`
45881 // and `autobin(x|y): false` with the `(x|y)bins` in `fullData`
45882 if(ai === 'autobinx' || ai === 'autobiny') {
45883 ai = ai.charAt(ai.length - 1) + 'bins';
45884 if(Array.isArray(vi)) vi = vi.map(arrayBins(ai));
45885 else if(vi === false) vi = traces.map(allBins(ai));
45886 else vi = null;
45887 }
45888
45889 redoit[ai] = vi;
45890
45891 if(ai.substr(0, 6) === 'LAYOUT') {
45892 param = layoutNP(gd.layout, ai.replace('LAYOUT', ''));
45893 undoit[ai] = [undefinedToNull(param.get())];
45894 // since we're allowing val to be an array, allow it here too,
45895 // even though that's meaningless
45896 param.set(Array.isArray(vi) ? vi[0] : vi);
45897 // ironically, the layout attrs in restyle only require replot,
45898 // not relayout
45899 flags.calc = true;
45900 continue;
45901 }
45902
45903 // set attribute in gd.data
45904 undoit[ai] = a0();
45905 for(i = 0; i < traces.length; i++) {
45906 cont = data[traces[i]];
45907 contFull = getFullTrace(traces[i]);
45908 var preGUI = fullLayout._tracePreGUI[contFull._fullInput.uid];
45909 param = makeNP(preGUI, guiEditFlag)(cont, ai);
45910 oldVal = param.get();
45911 newVal = Array.isArray(vi) ? vi[i % vi.length] : vi;
45912
45913 if(newVal === undefined) continue;
45914
45915 var finalPart = param.parts[param.parts.length - 1];
45916 var prefix = ai.substr(0, ai.length - finalPart.length - 1);
45917 var prefixDot = prefix ? prefix + '.' : '';
45918 var innerContFull = prefix ?
45919 nestedProperty(contFull, prefix).get() : contFull;
45920
45921 valObject = PlotSchema.getTraceValObject(contFull, param.parts);
45922
45923 if(valObject && valObject.impliedEdits && newVal !== null) {
45924 for(var impliedKey in valObject.impliedEdits) {
45925 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey], i);
45926 }
45927 } else if((finalPart === 'thicknessmode' || finalPart === 'lenmode') &&
45928 oldVal !== newVal &&
45929 (newVal === 'fraction' || newVal === 'pixels') &&
45930 innerContFull
45931 ) {
45932 // changing colorbar size modes,
45933 // make the resulting size not change
45934 // note that colorbar fractional sizing is based on the
45935 // original plot size, before anything (like a colorbar)
45936 // increases the margins
45937
45938 var gs = fullLayout._size;
45939 var orient = innerContFull.orient;
45940 var topOrBottom = (orient === 'top') || (orient === 'bottom');
45941 if(finalPart === 'thicknessmode') {
45942 var thicknorm = topOrBottom ? gs.h : gs.w;
45943 doextra(prefixDot + 'thickness', innerContFull.thickness *
45944 (newVal === 'fraction' ? 1 / thicknorm : thicknorm), i);
45945 } else {
45946 var lennorm = topOrBottom ? gs.w : gs.h;
45947 doextra(prefixDot + 'len', innerContFull.len *
45948 (newVal === 'fraction' ? 1 / lennorm : lennorm), i);
45949 }
45950 } else if(ai === 'type' && (
45951 (newVal === 'pie') !== (oldVal === 'pie') ||
45952 (newVal === 'funnelarea') !== (oldVal === 'funnelarea')
45953 )) {
45954 var labelsTo = 'x';
45955 var valuesTo = 'y';
45956 if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
45957 labelsTo = 'y';
45958 valuesTo = 'x';
45959 }
45960 Lib.swapAttrs(cont, ['?', '?src'], 'labels', labelsTo);
45961 Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo);
45962 Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo);
45963
45964 if(oldVal === 'pie' || oldVal === 'funnelarea') {
45965 nestedProperty(cont, 'marker.color')
45966 .set(nestedProperty(cont, 'marker.colors').get());
45967
45968 // super kludgy - but if all pies are gone we won't remove them otherwise
45969 fullLayout._pielayer.selectAll('g.trace').remove();
45970 } else if(Registry.traceIs(cont, 'cartesian')) {
45971 nestedProperty(cont, 'marker.colors')
45972 .set(nestedProperty(cont, 'marker.color').get());
45973 }
45974 }
45975
45976 undoit[ai][i] = undefinedToNull(oldVal);
45977 // set the new value - if val is an array, it's one el per trace
45978 // first check for attributes that get more complex alterations
45979 var swapAttrs = [
45980 'swapxy', 'swapxyaxes', 'orientation', 'orientationaxes'
45981 ];
45982 if(swapAttrs.indexOf(ai) !== -1) {
45983 // setting an orientation: make sure it's changing
45984 // before we swap everything else
45985 if(ai === 'orientation') {
45986 param.set(newVal);
45987 // obnoxious that we need this level of coupling... but in order to
45988 // properly handle setting orientation to `null` we need to mimic
45989 // the logic inside Bars.supplyDefaults for default orientation
45990 var defaultOrientation = (cont.x && !cont.y) ? 'h' : 'v';
45991 if((param.get() || defaultOrientation) === contFull.orientation) {
45992 continue;
45993 }
45994 } else if(ai === 'orientationaxes') {
45995 // orientationaxes has no value,
45996 // it flips everything and the axes
45997
45998 cont.orientation =
45999 {v: 'h', h: 'v'}[contFull.orientation];
46000 }
46001 helpers.swapXYData(cont);
46002 flags.calc = flags.clearAxisTypes = true;
46003 } else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) {
46004 // TODO: use manageArrays.applyContainerArrayChanges here too
46005 helpers.manageArrayContainers(param, newVal, undoit);
46006 flags.calc = true;
46007 } else {
46008 if(valObject) {
46009 // must redo calcdata when restyling array values of arrayOk attributes
46010 // ... but no need to this for regl-based traces
46011 if(valObject.arrayOk &&
46012 !Registry.traceIs(contFull, 'regl') &&
46013 (Lib.isArrayOrTypedArray(newVal) || Lib.isArrayOrTypedArray(oldVal))
46014 ) {
46015 flags.calc = true;
46016 } else editTypes.update(flags, valObject);
46017 } else {
46018 /*
46019 * if we couldn't find valObject, assume a full recalc.
46020 * This can happen if you're changing type and making
46021 * some other edits too, so the modules we're
46022 * looking at don't have these attributes in them.
46023 */
46024 flags.calc = true;
46025 }
46026
46027 // all the other ones, just modify that one attribute
46028 param.set(newVal);
46029 }
46030 }
46031
46032 // swap the data attributes of the relevant x and y axes?
46033 if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) {
46034 Axes.swap(gd, traces);
46035 }
46036
46037 // swap hovermode if set to "compare x/y data"
46038 if(ai === 'orientationaxes') {
46039 var hovermode = nestedProperty(gd.layout, 'hovermode');
46040 if(hovermode.get() === 'x') {
46041 hovermode.set('y');
46042 } else if(hovermode.get() === 'y') {
46043 hovermode.set('x');
46044 } else if(hovermode.get() === 'x unified') {
46045 hovermode.set('y unified');
46046 } else if(hovermode.get() === 'y unified') {
46047 hovermode.set('x unified');
46048 }
46049 }
46050
46051 // Major enough changes deserve autoscale and
46052 // non-reversed axes so people don't get confused
46053 //
46054 // Note: autobin (or its new analog bin clearing) is not included here
46055 // since we're not pushing bins back to gd.data, so if we have bin
46056 // info it was explicitly provided by the user.
46057 if(['orientation', 'type'].indexOf(ai) !== -1) {
46058 axlist = [];
46059 for(i = 0; i < traces.length; i++) {
46060 var trace = data[traces[i]];
46061
46062 if(Registry.traceIs(trace, 'cartesian')) {
46063 addToAxlist(trace.xaxis || 'x');
46064 addToAxlist(trace.yaxis || 'y');
46065 }
46066 }
46067
46068 doextra(axlist.map(autorangeAttr), true, 0);
46069 doextra(axlist.map(rangeAttr), [0, 1], 0);
46070 }
46071 }
46072
46073 if(flags.calc || flags.plot) {
46074 flags.fullReplot = true;
46075 }
46076
46077 return {
46078 flags: flags,
46079 undoit: undoit,
46080 redoit: redoit,
46081 traces: traces,
46082 eventData: Lib.extendDeepNoArrays([], [eventData, traces])
46083 };
46084}
46085
46086/**
46087 * Converts deprecated attribute keys to
46088 * the current API to ensure backwards compatibility.
46089 *
46090 * This is needed for the update mechanism to determine which
46091 * subroutines to run based on the actual attribute
46092 * definitions (that don't include the deprecated ones).
46093 *
46094 * E.g. Maps {'xaxis.title': 'A chart'} to {'xaxis.title.text': 'A chart'}
46095 * and {titlefont: {...}} to {'title.font': {...}}.
46096 *
46097 * @param aobj
46098 */
46099function cleanDeprecatedAttributeKeys(aobj) {
46100 var oldAxisTitleRegex = Lib.counterRegex('axis', '\.title', false, false);
46101 var colorbarRegex = /colorbar\.title$/;
46102 var keys = Object.keys(aobj);
46103 var i, key, value;
46104
46105 for(i = 0; i < keys.length; i++) {
46106 key = keys[i];
46107 value = aobj[key];
46108
46109 if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) &&
46110 (typeof value === 'string' || typeof value === 'number')) {
46111 replace(key, key.replace('title', 'title.text'));
46112 } else if(key.indexOf('titlefont') > -1) {
46113 replace(key, key.replace('titlefont', 'title.font'));
46114 } else if(key.indexOf('titleposition') > -1) {
46115 replace(key, key.replace('titleposition', 'title.position'));
46116 } else if(key.indexOf('titleside') > -1) {
46117 replace(key, key.replace('titleside', 'title.side'));
46118 } else if(key.indexOf('titleoffset') > -1) {
46119 replace(key, key.replace('titleoffset', 'title.offset'));
46120 }
46121 }
46122
46123 function replace(oldAttrStr, newAttrStr) {
46124 aobj[newAttrStr] = aobj[oldAttrStr];
46125 delete aobj[oldAttrStr];
46126 }
46127}
46128
46129/**
46130 * relayout: update layout attributes of an existing plot
46131 *
46132 * Can be called two ways:
46133 *
46134 * Signature 1:
46135 * @param {String | HTMLDivElement} gd
46136 * the id or dom element of the graph container div
46137 * @param {String} astr
46138 * attribute string (like `'xaxis.range[0]'`) to update
46139 * @param {*} val
46140 * value to give this attribute
46141 *
46142 * Signature 2:
46143 * @param {String | HTMLDivElement} gd
46144 * (as in signature 1)
46145 * @param {Object} aobj
46146 * attribute object `{astr1: val1, astr2: val2 ...}`
46147 * allows setting multiple attributes simultaneously
46148 */
46149function relayout(gd, astr, val) {
46150 gd = Lib.getGraphDiv(gd);
46151 helpers.clearPromiseQueue(gd);
46152
46153 if(gd.framework && gd.framework.isPolar) {
46154 return Promise.resolve(gd);
46155 }
46156
46157 var aobj = {};
46158 if(typeof astr === 'string') {
46159 aobj[astr] = val;
46160 } else if(Lib.isPlainObject(astr)) {
46161 aobj = Lib.extendFlat({}, astr);
46162 } else {
46163 Lib.warn('Relayout fail.', astr, val);
46164 return Promise.reject();
46165 }
46166
46167 if(Object.keys(aobj).length) gd.changed = true;
46168
46169 var specs = _relayout(gd, aobj);
46170 var flags = specs.flags;
46171
46172 // clear calcdata if required
46173 if(flags.calc) gd.calcdata = undefined;
46174
46175 // fill in redraw sequence
46176
46177 // even if we don't have anything left in aobj,
46178 // something may have happened within relayout that we
46179 // need to wait for
46180 var seq = [Plots.previousPromises];
46181
46182 if(flags.layoutReplot) {
46183 seq.push(subroutines.layoutReplot);
46184 } else if(Object.keys(aobj).length) {
46185 axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd);
46186
46187 if(flags.legend) seq.push(subroutines.doLegend);
46188 if(flags.layoutstyle) seq.push(subroutines.layoutStyles);
46189 if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered);
46190 if(flags.ticks) seq.push(subroutines.doTicksRelayout);
46191 if(flags.modebar) seq.push(subroutines.doModeBar);
46192 if(flags.camera) seq.push(subroutines.doCamera);
46193 if(flags.colorbars) seq.push(subroutines.doColorBars);
46194
46195 seq.push(emitAfterPlot);
46196 }
46197
46198 seq.push(Plots.rehover, Plots.redrag);
46199
46200 Queue.add(gd,
46201 relayout, [gd, specs.undoit],
46202 relayout, [gd, specs.redoit]
46203 );
46204
46205 var plotDone = Lib.syncOrAsync(seq, gd);
46206 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
46207
46208 return plotDone.then(function() {
46209 gd.emit('plotly_relayout', specs.eventData);
46210 return gd;
46211 });
46212}
46213
46214// Optimization mostly for large splom traces where
46215// Plots.supplyDefaults can take > 100ms
46216function axRangeSupplyDefaultsByPass(gd, flags, specs) {
46217 var fullLayout = gd._fullLayout;
46218
46219 if(!flags.axrange) return false;
46220
46221 for(var k in flags) {
46222 if(k !== 'axrange' && flags[k]) return false;
46223 }
46224
46225 for(var axId in specs.rangesAltered) {
46226 var axName = Axes.id2name(axId);
46227 var axIn = gd.layout[axName];
46228 var axOut = fullLayout[axName];
46229 axOut.autorange = axIn.autorange;
46230 axOut.range = axIn.range.slice();
46231 axOut.cleanRange();
46232
46233 if(axOut._matchGroup) {
46234 for(var axId2 in axOut._matchGroup) {
46235 if(axId2 !== axId) {
46236 var ax2 = fullLayout[Axes.id2name(axId2)];
46237 ax2.autorange = axOut.autorange;
46238 ax2.range = axOut.range.slice();
46239 ax2._input.range = axOut.range.slice();
46240 }
46241 }
46242 }
46243 }
46244
46245 return true;
46246}
46247
46248function addAxRangeSequence(seq, rangesAltered) {
46249 // N.B. leave as sequence of subroutines (for now) instead of
46250 // subroutine of its own so that finalDraw always gets
46251 // executed after drawData
46252 var drawAxes = rangesAltered ?
46253 function(gd) {
46254 var axIds = [];
46255 var skipTitle = true;
46256
46257 for(var id in rangesAltered) {
46258 var ax = Axes.getFromId(gd, id);
46259 axIds.push(id);
46260
46261 if(ax._matchGroup) {
46262 for(var id2 in ax._matchGroup) {
46263 if(!rangesAltered[id2]) {
46264 axIds.push(id2);
46265 }
46266 }
46267 }
46268
46269 if(ax.automargin) skipTitle = false;
46270 }
46271
46272 return Axes.draw(gd, axIds, {skipTitle: skipTitle});
46273 } :
46274 function(gd) {
46275 return Axes.draw(gd, 'redraw');
46276 };
46277
46278 seq.push(
46279 clearSelect,
46280 subroutines.doAutoRangeAndConstraints,
46281 drawAxes,
46282 subroutines.drawData,
46283 subroutines.finalDraw
46284 );
46285}
46286
46287var AX_RANGE_RE = /^[xyz]axis[0-9]*\.range(\[[0|1]\])?$/;
46288var AX_AUTORANGE_RE = /^[xyz]axis[0-9]*\.autorange$/;
46289var AX_DOMAIN_RE = /^[xyz]axis[0-9]*\.domain(\[[0|1]\])?$/;
46290
46291function _relayout(gd, aobj) {
46292 var layout = gd.layout;
46293 var fullLayout = gd._fullLayout;
46294 var guiEditFlag = fullLayout._guiEditing;
46295 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
46296 var keys = Object.keys(aobj);
46297 var axes = Axes.list(gd);
46298 var eventData = Lib.extendDeepAll({}, aobj);
46299 var arrayEdits = {};
46300
46301 var arrayStr, i, j;
46302
46303 cleanDeprecatedAttributeKeys(aobj);
46304 keys = Object.keys(aobj);
46305
46306 // look for 'allaxes', split out into all axes
46307 // in case of 3D the axis are nested within a scene which is held in _id
46308 for(i = 0; i < keys.length; i++) {
46309 if(keys[i].indexOf('allaxes') === 0) {
46310 for(j = 0; j < axes.length; j++) {
46311 var scene = axes[j]._id.substr(1);
46312 var axisAttr = (scene.indexOf('scene') !== -1) ? (scene + '.') : '';
46313 var newkey = keys[i].replace('allaxes', axisAttr + axes[j]._name);
46314
46315 if(!aobj[newkey]) aobj[newkey] = aobj[keys[i]];
46316 }
46317
46318 delete aobj[keys[i]];
46319 }
46320 }
46321
46322 // initialize flags
46323 var flags = editTypes.layoutFlags();
46324
46325 // copies of the change (and previous values of anything affected)
46326 // for the undo / redo queue
46327 var redoit = {};
46328 var undoit = {};
46329
46330 // for attrs that interact (like scales & autoscales), save the
46331 // old vals before making the change
46332 // val=undefined will not set a value, just record what the value was.
46333 // attr can be an array to set several at once (all to the same val)
46334 function doextra(attr, val) {
46335 if(Array.isArray(attr)) {
46336 attr.forEach(function(a) { doextra(a, val); });
46337 return;
46338 }
46339
46340 // if we have another value for this attribute (explicitly or
46341 // via a parent) do not override with this auto-generated extra
46342 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
46343
46344 var p = layoutNP(layout, attr);
46345 if(!(attr in undoit)) {
46346 undoit[attr] = undefinedToNull(p.get());
46347 }
46348 if(val !== undefined) p.set(val);
46349 }
46350
46351 // for constraint enforcement: keep track of all axes (as {id: name})
46352 // we're editing the (auto)range of, so we can tell the others constrained
46353 // to scale with them that it's OK for them to shrink
46354 var rangesAltered = {};
46355 var axId;
46356
46357 function recordAlteredAxis(pleafPlus) {
46358 var axId = Axes.name2id(pleafPlus.split('.')[0]);
46359 rangesAltered[axId] = 1;
46360 return axId;
46361 }
46362
46363 // alter gd.layout
46364 for(var ai in aobj) {
46365 if(helpers.hasParent(aobj, ai)) {
46366 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
46367 }
46368
46369 var p = layoutNP(layout, ai);
46370 var vi = aobj[ai];
46371 var plen = p.parts.length;
46372 // p.parts may end with an index integer if the property is an array
46373 var pend = plen - 1;
46374 while(pend > 0 && typeof p.parts[pend] !== 'string') pend--;
46375 // last property in chain (leaf node)
46376 var pleaf = p.parts[pend];
46377 // leaf plus immediate parent
46378 var pleafPlus = p.parts[pend - 1] + '.' + pleaf;
46379 // trunk nodes (everything except the leaf)
46380 var ptrunk = p.parts.slice(0, pend).join('.');
46381 var parentIn = nestedProperty(gd.layout, ptrunk).get();
46382 var parentFull = nestedProperty(fullLayout, ptrunk).get();
46383 var vOld = p.get();
46384
46385 if(vi === undefined) continue;
46386
46387 redoit[ai] = vi;
46388
46389 // axis reverse is special - it is its own inverse
46390 // op and has no flag.
46391 undoit[ai] = (pleaf === 'reverse') ? vi : undefinedToNull(vOld);
46392
46393 var valObject = PlotSchema.getLayoutValObject(fullLayout, p.parts);
46394
46395 if(valObject && valObject.impliedEdits && vi !== null) {
46396 for(var impliedKey in valObject.impliedEdits) {
46397 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey]);
46398 }
46399 }
46400
46401 // Setting width or height to null must reset the graph's width / height
46402 // back to its initial value as computed during the first pass in Plots.plotAutoSize.
46403 //
46404 // To do so, we must manually set them back here using the _initialAutoSize cache.
46405 // can't use impliedEdits for this because behavior depends on vi
46406 if(['width', 'height'].indexOf(ai) !== -1) {
46407 if(vi) {
46408 doextra('autosize', null);
46409 // currently we don't support autosize one dim only - so
46410 // explicitly set the other one. Note that doextra will
46411 // ignore this if the same relayout call also provides oppositeAttr
46412 var oppositeAttr = ai === 'height' ? 'width' : 'height';
46413 doextra(oppositeAttr, fullLayout[oppositeAttr]);
46414 } else {
46415 fullLayout[ai] = gd._initialAutoSize[ai];
46416 }
46417 } else if(ai === 'autosize') {
46418 // depends on vi here too, so again can't use impliedEdits
46419 doextra('width', vi ? null : fullLayout.width);
46420 doextra('height', vi ? null : fullLayout.height);
46421 } else if(pleafPlus.match(AX_RANGE_RE)) {
46422 // check autorange vs range
46423
46424 recordAlteredAxis(pleafPlus);
46425 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
46426 } else if(pleafPlus.match(AX_AUTORANGE_RE)) {
46427 recordAlteredAxis(pleafPlus);
46428 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
46429 var axFull = nestedProperty(fullLayout, ptrunk).get();
46430 if(axFull._inputDomain) {
46431 // if we're autoranging and this axis has a constrained domain,
46432 // reset it so we don't get locked into a shrunken size
46433 axFull._input.domain = axFull._inputDomain.slice();
46434 }
46435 } else if(pleafPlus.match(AX_DOMAIN_RE)) {
46436 nestedProperty(fullLayout, ptrunk + '._inputDomain').set(null);
46437 }
46438
46439 // toggling axis type between log and linear: we need to convert
46440 // positions for components that are still using linearized values,
46441 // not data values like newer components.
46442 // previously we did this for log <-> not-log, but now only do it
46443 // for log <-> linear
46444 if(pleaf === 'type') {
46445 var ax = parentIn;
46446 var toLog = parentFull.type === 'linear' && vi === 'log';
46447 var fromLog = parentFull.type === 'log' && vi === 'linear';
46448
46449 if(toLog || fromLog) {
46450 if(!ax || !ax.range) {
46451 // 2D never gets here, but 3D does
46452 // I don't think this is needed, but left here in case there
46453 // are edge cases I'm not thinking of.
46454 doextra(ptrunk + '.autorange', true);
46455 } else if(!parentFull.autorange) {
46456 // toggling log without autorange: need to also recalculate ranges
46457 // because log axes use linearized values for range endpoints
46458 var r0 = ax.range[0];
46459 var r1 = ax.range[1];
46460 if(toLog) {
46461 // if both limits are negative, autorange
46462 if(r0 <= 0 && r1 <= 0) {
46463 doextra(ptrunk + '.autorange', true);
46464 }
46465 // if one is negative, set it 6 orders below the other.
46466 if(r0 <= 0) r0 = r1 / 1e6;
46467 else if(r1 <= 0) r1 = r0 / 1e6;
46468 // now set the range values as appropriate
46469 doextra(ptrunk + '.range[0]', Math.log(r0) / Math.LN10);
46470 doextra(ptrunk + '.range[1]', Math.log(r1) / Math.LN10);
46471 } else {
46472 doextra(ptrunk + '.range[0]', Math.pow(10, r0));
46473 doextra(ptrunk + '.range[1]', Math.pow(10, r1));
46474 }
46475 } else if(toLog) {
46476 // just make sure the range is positive and in the right
46477 // order, it'll get recalculated later
46478 ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1];
46479 }
46480
46481 // clear polar view initial stash for radial range so that
46482 // value get recomputed in correct units
46483 if(Array.isArray(fullLayout._subplots.polar) &&
46484 fullLayout._subplots.polar.length &&
46485 fullLayout[p.parts[0]] &&
46486 p.parts[1] === 'radialaxis'
46487 ) {
46488 delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range'];
46489 }
46490
46491 // Annotations and images also need to convert to/from linearized coords
46492 // Shapes do not need this :)
46493 Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra);
46494 Registry.getComponentMethod('images', 'convertCoords')(gd, parentFull, vi, doextra);
46495 } else {
46496 // any other type changes: the range from the previous type
46497 // will not make sense, so autorange it.
46498 doextra(ptrunk + '.autorange', true);
46499 doextra(ptrunk + '.range', null);
46500 }
46501 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
46502 } else if(pleaf.match(AX_NAME_PATTERN)) {
46503 var fullProp = nestedProperty(fullLayout, ai).get();
46504 var newType = (vi || {}).type;
46505
46506 // This can potentially cause strange behavior if the autotype is not
46507 // numeric (linear, because we don't auto-log) but the previous type
46508 // was log. That's a very strange edge case though
46509 if(!newType || newType === '-') newType = 'linear';
46510 Registry.getComponentMethod('annotations', 'convertCoords')(gd, fullProp, newType, doextra);
46511 Registry.getComponentMethod('images', 'convertCoords')(gd, fullProp, newType, doextra);
46512 }
46513
46514 // alter gd.layout
46515
46516 // collect array component edits for execution all together
46517 // so we can ensure consistent behavior adding/removing items
46518 // and order-independence for add/remove/edit all together in
46519 // one relayout call
46520 var containerArrayMatch = manageArrays.containerArrayMatch(ai);
46521 if(containerArrayMatch) {
46522 arrayStr = containerArrayMatch.array;
46523 i = containerArrayMatch.index;
46524 var propStr = containerArrayMatch.property;
46525 var updateValObject = valObject || {editType: 'calc'};
46526
46527 if(i !== '' && propStr === '') {
46528 // special handling of undoit if we're adding or removing an element
46529 // ie 'annotations[2]' which can be {...} (add) or null,
46530 // does not work when replacing the entire array
46531 if(manageArrays.isAddVal(vi)) {
46532 undoit[ai] = null;
46533 } else if(manageArrays.isRemoveVal(vi)) {
46534 undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i];
46535 } else {
46536 Lib.warn('unrecognized full object value', aobj);
46537 }
46538 }
46539 editTypes.update(flags, updateValObject);
46540
46541 // prepare the edits object we'll send to applyContainerArrayChanges
46542 if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};
46543 var objEdits = arrayEdits[arrayStr][i];
46544 if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {};
46545 objEdits[propStr] = vi;
46546
46547 delete aobj[ai];
46548 } else if(pleaf === 'reverse') {
46549 // handle axis reversal explicitly, as there's no 'reverse' attribute
46550
46551 if(parentIn.range) parentIn.range.reverse();
46552 else {
46553 doextra(ptrunk + '.autorange', true);
46554 parentIn.range = [1, 0];
46555 }
46556
46557 if(parentFull.autorange) flags.calc = true;
46558 else flags.plot = true;
46559 } else {
46560 if((fullLayout._has('scatter-like') && fullLayout._has('regl')) &&
46561 (ai === 'dragmode' &&
46562 (vi === 'lasso' || vi === 'select') &&
46563 !(vOld === 'lasso' || vOld === 'select'))
46564 ) {
46565 flags.plot = true;
46566 } else if(fullLayout._has('gl2d')) {
46567 flags.plot = true;
46568 } else if(valObject) editTypes.update(flags, valObject);
46569 else flags.calc = true;
46570
46571 p.set(vi);
46572 }
46573 }
46574
46575 // now we've collected component edits - execute them all together
46576 for(arrayStr in arrayEdits) {
46577 var finished = manageArrays.applyContainerArrayChanges(gd,
46578 layoutNP(layout, arrayStr), arrayEdits[arrayStr], flags, layoutNP);
46579 if(!finished) flags.plot = true;
46580 }
46581
46582 // figure out if we need to recalculate axis constraints
46583 var constraints = fullLayout._axisConstraintGroups || [];
46584 for(axId in rangesAltered) {
46585 for(i = 0; i < constraints.length; i++) {
46586 var group = constraints[i];
46587 if(group[axId]) {
46588 // Always recalc if we're changing constrained ranges.
46589 // Otherwise it's possible to violate the constraints by
46590 // specifying arbitrary ranges for all axes in the group.
46591 // this way some ranges may expand beyond what's specified,
46592 // as they do at first draw, to satisfy the constraints.
46593 flags.calc = true;
46594 for(var groupAxId in group) {
46595 if(!rangesAltered[groupAxId]) {
46596 Axes.getFromId(gd, groupAxId)._constraintShrinkable = true;
46597 }
46598 }
46599 }
46600 }
46601 }
46602
46603 // If the autosize changed or height or width was explicitly specified,
46604 // this triggers a redraw
46605 // TODO: do we really need special aobj.height/width handling here?
46606 // couldn't editType do this?
46607 if(updateAutosize(gd) || aobj.height || aobj.width) flags.plot = true;
46608
46609 if(flags.plot || flags.calc) {
46610 flags.layoutReplot = true;
46611 }
46612
46613 // now all attribute mods are done, as are
46614 // redo and undo so we can save them
46615
46616 return {
46617 flags: flags,
46618 rangesAltered: rangesAltered,
46619 undoit: undoit,
46620 redoit: redoit,
46621 eventData: eventData
46622 };
46623}
46624
46625/*
46626 * updateAutosize: we made a change, does it change the autosize result?
46627 * puts the new size into fullLayout
46628 * returns true if either height or width changed
46629 */
46630function updateAutosize(gd) {
46631 var fullLayout = gd._fullLayout;
46632 var oldWidth = fullLayout.width;
46633 var oldHeight = fullLayout.height;
46634
46635 // calculate autosizing
46636 if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout);
46637
46638 return (fullLayout.width !== oldWidth) || (fullLayout.height !== oldHeight);
46639}
46640
46641/**
46642 * update: update trace and layout attributes of an existing plot
46643 *
46644 * @param {String | HTMLDivElement} gd
46645 * the id or DOM element of the graph container div
46646 * @param {Object} traceUpdate
46647 * attribute object `{astr1: val1, astr2: val2 ...}`
46648 * corresponding to updates in the plot's traces
46649 * @param {Object} layoutUpdate
46650 * attribute object `{astr1: val1, astr2: val2 ...}`
46651 * corresponding to updates in the plot's layout
46652 * @param {Number[] | Number} [traces]
46653 * integer or array of integers for the traces to alter (all if omitted)
46654 *
46655 */
46656function update(gd, traceUpdate, layoutUpdate, _traces) {
46657 gd = Lib.getGraphDiv(gd);
46658 helpers.clearPromiseQueue(gd);
46659
46660 if(gd.framework && gd.framework.isPolar) {
46661 return Promise.resolve(gd);
46662 }
46663
46664 if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {};
46665 if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {};
46666
46667 if(Object.keys(traceUpdate).length) gd.changed = true;
46668 if(Object.keys(layoutUpdate).length) gd.changed = true;
46669
46670 var traces = helpers.coerceTraceIndices(gd, _traces);
46671
46672 var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces);
46673 var restyleFlags = restyleSpecs.flags;
46674
46675 var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate));
46676 var relayoutFlags = relayoutSpecs.flags;
46677
46678 // clear calcdata and/or axis types if required
46679 if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
46680 if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate);
46681
46682 // fill in redraw sequence
46683 var seq = [];
46684
46685 if(relayoutFlags.layoutReplot) {
46686 // N.B. works fine when both
46687 // relayoutFlags.layoutReplot and restyleFlags.fullReplot are true
46688 seq.push(subroutines.layoutReplot);
46689 } else if(restyleFlags.fullReplot) {
46690 seq.push(exports.plot);
46691 } else {
46692 seq.push(Plots.previousPromises);
46693 axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd);
46694
46695 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
46696 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
46697 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
46698 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
46699 if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered);
46700 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
46701 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
46702 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
46703
46704 seq.push(emitAfterPlot);
46705 }
46706
46707 seq.push(Plots.rehover, Plots.redrag);
46708
46709 Queue.add(gd,
46710 update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
46711 update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]
46712 );
46713
46714 var plotDone = Lib.syncOrAsync(seq, gd);
46715 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
46716
46717 return plotDone.then(function() {
46718 gd.emit('plotly_update', {
46719 data: restyleSpecs.eventData,
46720 layout: relayoutSpecs.eventData
46721 });
46722
46723 return gd;
46724 });
46725}
46726
46727/*
46728 * internal-use-only restyle/relayout/update variants that record the initial
46729 * values in (fullLayout|fullTrace)._preGUI so changes can be persisted across
46730 * Plotly.react data updates, dependent on uirevision attributes
46731 */
46732function guiEdit(func) {
46733 return function wrappedEdit(gd) {
46734 gd._fullLayout._guiEditing = true;
46735 var p = func.apply(null, arguments);
46736 gd._fullLayout._guiEditing = false;
46737 return p;
46738 };
46739}
46740
46741// For connecting edited layout attributes to uirevision attrs
46742// If no `attr` we use `match[1] + '.uirevision'`
46743// Ordered by most common edits first, to minimize our search time
46744var layoutUIControlPatterns = [
46745 {pattern: /^hiddenlabels/, attr: 'legend.uirevision'},
46746 {pattern: /^((x|y)axis\d*)\.((auto)?range|title\.text)/},
46747
46748 // showspikes and modes include those nested inside scenes
46749 {pattern: /axis\d*\.showspikes$/, attr: 'modebar.uirevision'},
46750 {pattern: /(hover|drag)mode$/, attr: 'modebar.uirevision'},
46751
46752 {pattern: /^(scene\d*)\.camera/},
46753 {pattern: /^(geo\d*)\.(projection|center|fitbounds)/},
46754 {pattern: /^(ternary\d*\.[abc]axis)\.(min|title\.text)$/},
46755 {pattern: /^(polar\d*\.radialaxis)\.((auto)?range|angle|title\.text)/},
46756 {pattern: /^(polar\d*\.angularaxis)\.rotation/},
46757 {pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/},
46758
46759 {pattern: /^legend\.(x|y)$/, attr: 'editrevision'},
46760 {pattern: /^(shapes|annotations)/, attr: 'editrevision'},
46761 {pattern: /^title\.text$/, attr: 'editrevision'}
46762];
46763
46764// same for trace attributes: if `attr` is given it's in layout,
46765// or with no `attr` we use `trace.uirevision`
46766var traceUIControlPatterns = [
46767 {pattern: /^selectedpoints$/, attr: 'selectionrevision'},
46768 // "visible" includes trace.transforms[i].styles[j].value.visible
46769 {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'},
46770 {pattern: /^dimensions\[\d+\]\.constraintrange/},
46771 {pattern: /^node\.(x|y|groups)/}, // for Sankey nodes
46772 {pattern: /^level$/}, // for Sunburst & Treemap traces
46773
46774 // below this you must be in editable: true mode
46775 // TODO: I still put name and title with `trace.uirevision`
46776 // reasonable or should these be `editrevision`?
46777 // Also applies to axis titles up in the layout section
46778
46779 // "name" also includes transform.styles
46780 {pattern: /(^|value\.)name$/},
46781 // including nested colorbar attributes (ie marker.colorbar)
46782 {pattern: /colorbar\.title\.text$/},
46783 {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'}
46784];
46785
46786function findUIPattern(key, patternSpecs) {
46787 for(var i = 0; i < patternSpecs.length; i++) {
46788 var spec = patternSpecs[i];
46789 var match = key.match(spec.pattern);
46790 if(match) {
46791 return {head: match[1], attr: spec.attr};
46792 }
46793 }
46794}
46795
46796// We're finding the new uirevision before supplyDefaults, so do the
46797// inheritance manually. Note that only `undefined` inherits - other
46798// falsy values are returned.
46799function getNewRev(revAttr, container) {
46800 var newRev = nestedProperty(container, revAttr).get();
46801 if(newRev !== undefined) return newRev;
46802
46803 var parts = revAttr.split('.');
46804 parts.pop();
46805 while(parts.length > 1) {
46806 parts.pop();
46807 newRev = nestedProperty(container, parts.join('.') + '.uirevision').get();
46808 if(newRev !== undefined) return newRev;
46809 }
46810
46811 return container.uirevision;
46812}
46813
46814function getFullTraceIndexFromUid(uid, fullData) {
46815 for(var i = 0; i < fullData.length; i++) {
46816 if(fullData[i]._fullInput.uid === uid) return i;
46817 }
46818 return -1;
46819}
46820
46821function getTraceIndexFromUid(uid, data, tracei) {
46822 for(var i = 0; i < data.length; i++) {
46823 if(data[i].uid === uid) return i;
46824 }
46825 // fall back on trace order, but only if user didn't provide a uid for that trace
46826 return (!data[tracei] || data[tracei].uid) ? -1 : tracei;
46827}
46828
46829function valsMatch(v1, v2) {
46830 var v1IsObj = Lib.isPlainObject(v1);
46831 var v1IsArray = Array.isArray(v1);
46832 if(v1IsObj || v1IsArray) {
46833 return (
46834 (v1IsObj && Lib.isPlainObject(v2)) ||
46835 (v1IsArray && Array.isArray(v2))
46836 ) && JSON.stringify(v1) === JSON.stringify(v2);
46837 }
46838 return v1 === v2;
46839}
46840
46841function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
46842 var layoutPreGUI = oldFullLayout._preGUI;
46843 var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
46844 var bothInheritAutorange = [];
46845 var newRangeAccepted = {};
46846 for(key in layoutPreGUI) {
46847 match = findUIPattern(key, layoutUIControlPatterns);
46848 if(match) {
46849 revAttr = match.attr || (match.head + '.uirevision');
46850 oldRev = nestedProperty(oldFullLayout, revAttr).get();
46851 newRev = oldRev && getNewRev(revAttr, layout);
46852 if(newRev && (newRev === oldRev)) {
46853 preGUIVal = layoutPreGUI[key];
46854 if(preGUIVal === null) preGUIVal = undefined;
46855 newNP = nestedProperty(layout, key);
46856 newVal = newNP.get();
46857 if(valsMatch(newVal, preGUIVal)) {
46858 if(newVal === undefined && key.substr(key.length - 9) === 'autorange') {
46859 bothInheritAutorange.push(key.substr(0, key.length - 10));
46860 }
46861 newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
46862 continue;
46863 }
46864 }
46865 } else {
46866 Lib.warn('unrecognized GUI edit: ' + key);
46867 }
46868 // if we got this far, the new value was accepted as the new starting
46869 // point (either because it changed or revision changed)
46870 // so remove it from _preGUI for next time.
46871 delete layoutPreGUI[key];
46872
46873 if(key.substr(key.length - 8, 6) === 'range[') {
46874 newRangeAccepted[key.substr(0, key.length - 9)] = 1;
46875 }
46876 }
46877
46878 // Special logic for `autorange`, since it interacts with `range`:
46879 // If the new figure's matching `range` was kept, and `autorange`
46880 // wasn't supplied explicitly in either the original or the new figure,
46881 // we shouldn't alter that - but we may just have done that, so fix it.
46882 for(var i = 0; i < bothInheritAutorange.length; i++) {
46883 var axAttr = bothInheritAutorange[i];
46884 if(newRangeAccepted[axAttr]) {
46885 var newAx = nestedProperty(layout, axAttr).get();
46886 if(newAx) delete newAx.autorange;
46887 }
46888 }
46889
46890 // Now traces - try to match them up by uid (in case we added/deleted in
46891 // the middle), then fall back on index.
46892 var allTracePreGUI = oldFullLayout._tracePreGUI;
46893 for(var uid in allTracePreGUI) {
46894 var tracePreGUI = allTracePreGUI[uid];
46895 var newTrace = null;
46896 var fullInput;
46897 for(key in tracePreGUI) {
46898 // wait until we know we have preGUI values to look for traces
46899 // but if we don't find both, stop looking at this uid
46900 if(!newTrace) {
46901 var fulli = getFullTraceIndexFromUid(uid, oldFullData);
46902 if(fulli < 0) {
46903 // Somehow we didn't even have this trace in oldFullData...
46904 // I guess this could happen with `deleteTraces` or something
46905 delete allTracePreGUI[uid];
46906 break;
46907 }
46908 var fullTrace = oldFullData[fulli];
46909 fullInput = fullTrace._fullInput;
46910
46911 var newTracei = getTraceIndexFromUid(uid, data, fullInput.index);
46912 if(newTracei < 0) {
46913 // No match in new data
46914 delete allTracePreGUI[uid];
46915 break;
46916 }
46917 newTrace = data[newTracei];
46918 }
46919
46920 match = findUIPattern(key, traceUIControlPatterns);
46921 if(match) {
46922 if(match.attr) {
46923 oldRev = nestedProperty(oldFullLayout, match.attr).get();
46924 newRev = oldRev && getNewRev(match.attr, layout);
46925 } else {
46926 oldRev = fullInput.uirevision;
46927 // inheritance for trace.uirevision is simple, just layout.uirevision
46928 newRev = newTrace.uirevision;
46929 if(newRev === undefined) newRev = layout.uirevision;
46930 }
46931
46932 if(newRev && newRev === oldRev) {
46933 preGUIVal = tracePreGUI[key];
46934 if(preGUIVal === null) preGUIVal = undefined;
46935 newNP = nestedProperty(newTrace, key);
46936 newVal = newNP.get();
46937 if(valsMatch(newVal, preGUIVal)) {
46938 newNP.set(undefinedToNull(nestedProperty(fullInput, key).get()));
46939 continue;
46940 }
46941 }
46942 } else {
46943 Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid);
46944 }
46945 delete tracePreGUI[key];
46946 }
46947 }
46948}
46949
46950/**
46951 * Plotly.react:
46952 * A plot/update method that takes the full plot state (same API as plot/newPlot)
46953 * and diffs to determine the minimal update pathway
46954 *
46955 * @param {string id or DOM element} gd
46956 * the id or DOM element of the graph container div
46957 * @param {array of objects} data
46958 * array of traces, containing the data and display information for each trace
46959 * @param {object} layout
46960 * object describing the overall display of the plot,
46961 * all the stuff that doesn't pertain to any individual trace
46962 * @param {object} config
46963 * configuration options (see ./plot_config.js for more info)
46964 *
46965 * OR
46966 *
46967 * @param {string id or DOM element} gd
46968 * the id or DOM element of the graph container div
46969 * @param {object} figure
46970 * object containing `data`, `layout`, `config`, and `frames` members
46971 *
46972 */
46973function react(gd, data, layout, config) {
46974 var frames, plotDone;
46975
46976 function addFrames() { return exports.addFrames(gd, frames); }
46977
46978 gd = Lib.getGraphDiv(gd);
46979 helpers.clearPromiseQueue(gd);
46980
46981 var oldFullData = gd._fullData;
46982 var oldFullLayout = gd._fullLayout;
46983
46984 // you can use this as the initial draw as well as to update
46985 if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) {
46986 plotDone = exports.newPlot(gd, data, layout, config);
46987 } else {
46988 if(Lib.isPlainObject(data)) {
46989 var obj = data;
46990 data = obj.data;
46991 layout = obj.layout;
46992 config = obj.config;
46993 frames = obj.frames;
46994 }
46995
46996 var configChanged = false;
46997 // assume that if there's a config at all, we're reacting to it too,
46998 // and completely replace the previous config
46999 if(config) {
47000 var oldConfig = Lib.extendDeep({}, gd._context);
47001 gd._context = undefined;
47002 setPlotContext(gd, config);
47003 configChanged = diffConfig(oldConfig, gd._context);
47004 }
47005
47006 gd.data = data || [];
47007 helpers.cleanData(gd.data);
47008 gd.layout = layout || {};
47009 helpers.cleanLayout(gd.layout);
47010
47011 applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout);
47012
47013 // "true" skips updating calcdata and remapping arrays from calcTransforms,
47014 // which supplyDefaults usually does at the end, but we may need to NOT do
47015 // if the diff (which we haven't determined yet) says we'll recalc
47016 Plots.supplyDefaults(gd, {skipUpdateCalc: true});
47017
47018 var newFullData = gd._fullData;
47019 var newFullLayout = gd._fullLayout;
47020 var immutable = newFullLayout.datarevision === undefined;
47021 var transition = newFullLayout.transition;
47022
47023 var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition);
47024 var newDataRevision = relayoutFlags.newDataRevision;
47025 var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision);
47026
47027 // TODO: how to translate this part of relayout to Plotly.react?
47028 // // Setting width or height to null must reset the graph's width / height
47029 // // back to its initial value as computed during the first pass in Plots.plotAutoSize.
47030 // //
47031 // // To do so, we must manually set them back here using the _initialAutoSize cache.
47032 // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) {
47033 // fullLayout[ai] = gd._initialAutoSize[ai];
47034 // }
47035
47036 if(updateAutosize(gd)) relayoutFlags.layoutReplot = true;
47037
47038 // clear calcdata if required
47039 if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
47040 // otherwise do the calcdata updates and calcTransform array remaps that we skipped earlier
47041 else Plots.supplyDefaultsUpdateCalc(gd.calcdata, newFullData);
47042
47043 // Note: what restyle/relayout use impliedEdits and clearAxisTypes for
47044 // must be handled by the user when using Plotly.react.
47045
47046 // fill in redraw sequence
47047 var seq = [];
47048
47049 if(frames) {
47050 gd._transitionData = {};
47051 Plots.createTransitionData(gd);
47052 seq.push(addFrames);
47053 }
47054
47055 // Transition pathway,
47056 // only used when 'transition' is set by user and
47057 // when at least one animatable attribute has changed,
47058 // N.B. config changed aren't animatable
47059 if(newFullLayout.transition && !configChanged && (restyleFlags.anim || relayoutFlags.anim)) {
47060 Plots.doCalcdata(gd);
47061 subroutines.doAutoRangeAndConstraints(gd);
47062
47063 seq.push(function() {
47064 return Plots.transitionFromReact(gd, restyleFlags, relayoutFlags, oldFullLayout);
47065 });
47066 } else if(restyleFlags.fullReplot || relayoutFlags.layoutReplot || configChanged) {
47067 gd._fullLayout._skipDefaults = true;
47068 seq.push(exports.plot);
47069 } else {
47070 for(var componentType in relayoutFlags.arrays) {
47071 var indices = relayoutFlags.arrays[componentType];
47072 if(indices.length) {
47073 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
47074 if(drawOne !== Lib.noop) {
47075 for(var i = 0; i < indices.length; i++) {
47076 drawOne(gd, indices[i]);
47077 }
47078 } else {
47079 var draw = Registry.getComponentMethod(componentType, 'draw');
47080 if(draw === Lib.noop) {
47081 throw new Error('cannot draw components: ' + componentType);
47082 }
47083 draw(gd);
47084 }
47085 }
47086 }
47087
47088 seq.push(Plots.previousPromises);
47089 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
47090 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
47091 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
47092 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
47093 if(relayoutFlags.axrange) addAxRangeSequence(seq);
47094 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
47095 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
47096 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
47097 seq.push(emitAfterPlot);
47098 }
47099
47100 seq.push(Plots.rehover, Plots.redrag);
47101
47102 plotDone = Lib.syncOrAsync(seq, gd);
47103 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
47104 }
47105
47106 return plotDone.then(function() {
47107 gd.emit('plotly_react', {
47108 data: data,
47109 layout: layout
47110 });
47111
47112 return gd;
47113 });
47114}
47115
47116function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) {
47117 var sameTraceLength = oldFullData.length === newFullData.length;
47118
47119 if(!transition && !sameTraceLength) {
47120 return {
47121 fullReplot: true,
47122 calc: true
47123 };
47124 }
47125
47126 var flags = editTypes.traceFlags();
47127 flags.arrays = {};
47128 flags.nChanges = 0;
47129 flags.nChangesAnim = 0;
47130
47131 var i, trace;
47132
47133 function getTraceValObject(parts) {
47134 var out = PlotSchema.getTraceValObject(trace, parts);
47135 if(!trace._module.animatable && out.anim) {
47136 out.anim = false;
47137 }
47138 return out;
47139 }
47140
47141 var diffOpts = {
47142 getValObject: getTraceValObject,
47143 flags: flags,
47144 immutable: immutable,
47145 transition: transition,
47146 newDataRevision: newDataRevision,
47147 gd: gd
47148 };
47149
47150 var seenUIDs = {};
47151
47152 for(i = 0; i < oldFullData.length; i++) {
47153 if(newFullData[i]) {
47154 trace = newFullData[i]._fullInput;
47155 if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
47156 if(seenUIDs[trace.uid]) continue;
47157 seenUIDs[trace.uid] = 1;
47158
47159 getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
47160 }
47161 }
47162
47163 if(flags.calc || flags.plot) {
47164 flags.fullReplot = true;
47165 }
47166
47167 if(transition && flags.nChanges && flags.nChangesAnim) {
47168 flags.anim = (flags.nChanges === flags.nChangesAnim) && sameTraceLength ? 'all' : 'some';
47169 }
47170
47171 return flags;
47172}
47173
47174function diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition) {
47175 var flags = editTypes.layoutFlags();
47176 flags.arrays = {};
47177 flags.rangesAltered = {};
47178 flags.nChanges = 0;
47179 flags.nChangesAnim = 0;
47180
47181 function getLayoutValObject(parts) {
47182 return PlotSchema.getLayoutValObject(newFullLayout, parts);
47183 }
47184
47185 var diffOpts = {
47186 getValObject: getLayoutValObject,
47187 flags: flags,
47188 immutable: immutable,
47189 transition: transition,
47190 gd: gd
47191 };
47192
47193 getDiffFlags(oldFullLayout, newFullLayout, [], diffOpts);
47194
47195 if(flags.plot || flags.calc) {
47196 flags.layoutReplot = true;
47197 }
47198
47199 if(transition && flags.nChanges && flags.nChangesAnim) {
47200 flags.anim = flags.nChanges === flags.nChangesAnim ? 'all' : 'some';
47201 }
47202
47203 return flags;
47204}
47205
47206function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
47207 var valObject, key, astr;
47208
47209 var getValObject = opts.getValObject;
47210 var flags = opts.flags;
47211 var immutable = opts.immutable;
47212 var inArray = opts.inArray;
47213 var arrayIndex = opts.arrayIndex;
47214
47215 function changed() {
47216 var editType = valObject.editType;
47217 if(inArray && editType.indexOf('arraydraw') !== -1) {
47218 Lib.pushUnique(flags.arrays[inArray], arrayIndex);
47219 return;
47220 }
47221 editTypes.update(flags, valObject);
47222
47223 if(editType !== 'none') {
47224 flags.nChanges++;
47225 }
47226
47227 // track animatable changes
47228 if(opts.transition && valObject.anim) {
47229 flags.nChangesAnim++;
47230 }
47231
47232 // track cartesian axes with altered ranges
47233 if(AX_RANGE_RE.test(astr) || AX_AUTORANGE_RE.test(astr)) {
47234 flags.rangesAltered[outerparts[0]] = 1;
47235 }
47236
47237 // clear _inputDomain on cartesian axes with altered domains
47238 if(AX_DOMAIN_RE.test(astr)) {
47239 nestedProperty(newContainer, '_inputDomain').set(null);
47240 }
47241
47242 // track datarevision changes
47243 if(key === 'datarevision') {
47244 flags.newDataRevision = 1;
47245 }
47246 }
47247
47248 function valObjectCanBeDataArray(valObject) {
47249 return valObject.valType === 'data_array' || valObject.arrayOk;
47250 }
47251
47252 for(key in oldContainer) {
47253 // short-circuit based on previous calls or previous keys that already maximized the pathway
47254 if(flags.calc && !opts.transition) return;
47255
47256 var oldVal = oldContainer[key];
47257 var newVal = newContainer[key];
47258 var parts = outerparts.concat(key);
47259 astr = parts.join('.');
47260
47261 if(key.charAt(0) === '_' || typeof oldVal === 'function' || oldVal === newVal) continue;
47262
47263 // FIXME: ax.tick0 and dtick get filled in during plotting (except for geo subplots),
47264 // and unlike other auto values they don't make it back into the input,
47265 // so newContainer won't have them.
47266 if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
47267 var tickMode = newContainer.tickmode;
47268 if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
47269 }
47270 // FIXME: Similarly for axis ranges for 3D
47271 // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
47272 if(key === 'range' && newContainer.autorange) continue;
47273 if((key === 'zmin' || key === 'zmax') && newContainer.type === 'contourcarpet') continue;
47274
47275 valObject = getValObject(parts);
47276
47277 // in case type changed, we may not even *have* a valObject.
47278 if(!valObject) continue;
47279
47280 if(valObject._compareAsJSON && JSON.stringify(oldVal) === JSON.stringify(newVal)) continue;
47281
47282 var valType = valObject.valType;
47283 var i;
47284
47285 var canBeDataArray = valObjectCanBeDataArray(valObject);
47286 var wasArray = Array.isArray(oldVal);
47287 var nowArray = Array.isArray(newVal);
47288
47289 // hack for traces that modify the data in supplyDefaults, like
47290 // converting 1D to 2D arrays, which will always create new objects
47291 if(wasArray && nowArray) {
47292 var inputKey = '_input_' + key;
47293 var oldValIn = oldContainer[inputKey];
47294 var newValIn = newContainer[inputKey];
47295 if(Array.isArray(oldValIn) && oldValIn === newValIn) continue;
47296 }
47297
47298 if(newVal === undefined) {
47299 if(canBeDataArray && wasArray) flags.calc = true;
47300 else changed();
47301 } else if(valObject._isLinkedToArray) {
47302 var arrayEditIndices = [];
47303 var extraIndices = false;
47304 if(!inArray) flags.arrays[key] = arrayEditIndices;
47305
47306 var minLen = Math.min(oldVal.length, newVal.length);
47307 var maxLen = Math.max(oldVal.length, newVal.length);
47308 if(minLen !== maxLen) {
47309 if(valObject.editType === 'arraydraw') {
47310 extraIndices = true;
47311 } else {
47312 changed();
47313 continue;
47314 }
47315 }
47316
47317 for(i = 0; i < minLen; i++) {
47318 getDiffFlags(oldVal[i], newVal[i], parts.concat(i),
47319 // add array indices, but not if we're already in an array
47320 Lib.extendFlat({inArray: key, arrayIndex: i}, opts));
47321 }
47322
47323 // put this at the end so that we know our collected array indices are sorted
47324 // but the check for length changes happens up front so we can short-circuit
47325 // diffing if appropriate
47326 if(extraIndices) {
47327 for(i = minLen; i < maxLen; i++) {
47328 arrayEditIndices.push(i);
47329 }
47330 }
47331 } else if(!valType && Lib.isPlainObject(oldVal)) {
47332 getDiffFlags(oldVal, newVal, parts, opts);
47333 } else if(canBeDataArray) {
47334 if(wasArray && nowArray) {
47335 // don't try to diff two data arrays. If immutable we know the data changed,
47336 // if not, assume it didn't and let `layout.datarevision` tell us if it did
47337 if(immutable) {
47338 flags.calc = true;
47339 }
47340
47341 // look for animatable attributes when the data changed
47342 if(immutable || opts.newDataRevision) {
47343 changed();
47344 }
47345 } else if(wasArray !== nowArray) {
47346 flags.calc = true;
47347 } else changed();
47348 } else if(wasArray && nowArray) {
47349 // info array, colorscale, 'any' - these are short, just stringify.
47350 // I don't *think* that covers up any real differences post-validation, does it?
47351 // otherwise we need to dive in 1 (info_array) or 2 (colorscale) levels and compare
47352 // all elements.
47353 if(oldVal.length !== newVal.length || String(oldVal) !== String(newVal)) {
47354 changed();
47355 }
47356 } else {
47357 changed();
47358 }
47359 }
47360
47361 for(key in newContainer) {
47362 if(!(key in oldContainer || key.charAt(0) === '_' || typeof newContainer[key] === 'function')) {
47363 valObject = getValObject(outerparts.concat(key));
47364
47365 if(valObjectCanBeDataArray(valObject) && Array.isArray(newContainer[key])) {
47366 flags.calc = true;
47367 return;
47368 } else changed();
47369 }
47370 }
47371}
47372
47373/*
47374 * simple diff for config - for now, just treat all changes as equivalent
47375 */
47376function diffConfig(oldConfig, newConfig) {
47377 var key;
47378
47379 for(key in oldConfig) {
47380 if(key.charAt(0) === '_') continue;
47381 var oldVal = oldConfig[key];
47382 var newVal = newConfig[key];
47383 if(oldVal !== newVal) {
47384 if(Lib.isPlainObject(oldVal) && Lib.isPlainObject(newVal)) {
47385 if(diffConfig(oldVal, newVal)) {
47386 return true;
47387 }
47388 } else if(Array.isArray(oldVal) && Array.isArray(newVal)) {
47389 if(oldVal.length !== newVal.length) {
47390 return true;
47391 }
47392 for(var i = 0; i < oldVal.length; i++) {
47393 if(oldVal[i] !== newVal[i]) {
47394 if(Lib.isPlainObject(oldVal[i]) && Lib.isPlainObject(newVal[i])) {
47395 if(diffConfig(oldVal[i], newVal[i])) {
47396 return true;
47397 }
47398 } else {
47399 return true;
47400 }
47401 }
47402 }
47403 } else {
47404 return true;
47405 }
47406 }
47407 }
47408}
47409
47410/**
47411 * Animate to a frame, sequence of frame, frame group, or frame definition
47412 *
47413 * @param {string id or DOM element} gd
47414 * the id or DOM element of the graph container div
47415 *
47416 * @param {string or object or array of strings or array of objects} frameOrGroupNameOrFrameList
47417 * a single frame, array of frames, or group to which to animate. The intent is
47418 * inferred by the type of the input. Valid inputs are:
47419 *
47420 * - string, e.g. 'groupname': animate all frames of a given `group` in the order
47421 * in which they are defined via `Plotly.addFrames`.
47422 *
47423 * - array of strings, e.g. ['frame1', frame2']: a list of frames by name to which
47424 * to animate in sequence
47425 *
47426 * - object: {data: ...}: a frame definition to which to animate. The frame is not
47427 * and does not need to be added via `Plotly.addFrames`. It may contain any of
47428 * the properties of a frame, including `data`, `layout`, and `traces`. The
47429 * frame is used as provided and does not use the `baseframe` property.
47430 *
47431 * - array of objects, e.g. [{data: ...}, {data: ...}]: a list of frame objects,
47432 * each following the same rules as a single `object`.
47433 *
47434 * @param {object} animationOpts
47435 * configuration for the animation
47436 */
47437function animate(gd, frameOrGroupNameOrFrameList, animationOpts) {
47438 gd = Lib.getGraphDiv(gd);
47439
47440 if(!Lib.isPlotDiv(gd)) {
47441 throw new Error(
47442 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
47443 'to create a plot before animating it. For more details, see ' +
47444 'https://plotly.com/javascript/animations/'
47445 );
47446 }
47447
47448 var trans = gd._transitionData;
47449
47450 // This is the queue of frames that will be animated as soon as possible. They
47451 // are popped immediately upon the *start* of a transition:
47452 if(!trans._frameQueue) {
47453 trans._frameQueue = [];
47454 }
47455
47456 animationOpts = Plots.supplyAnimationDefaults(animationOpts);
47457 var transitionOpts = animationOpts.transition;
47458 var frameOpts = animationOpts.frame;
47459
47460 // Since frames are popped immediately, an empty queue only means all frames have
47461 // *started* to transition, not that the animation is complete. To solve that,
47462 // track a separate counter that increments at the same time as frames are added
47463 // to the queue, but decrements only when the transition is complete.
47464 if(trans._frameWaitingCnt === undefined) {
47465 trans._frameWaitingCnt = 0;
47466 }
47467
47468 function getTransitionOpts(i) {
47469 if(Array.isArray(transitionOpts)) {
47470 if(i >= transitionOpts.length) {
47471 return transitionOpts[0];
47472 } else {
47473 return transitionOpts[i];
47474 }
47475 } else {
47476 return transitionOpts;
47477 }
47478 }
47479
47480 function getFrameOpts(i) {
47481 if(Array.isArray(frameOpts)) {
47482 if(i >= frameOpts.length) {
47483 return frameOpts[0];
47484 } else {
47485 return frameOpts[i];
47486 }
47487 } else {
47488 return frameOpts;
47489 }
47490 }
47491
47492 // Execute a callback after the wrapper function has been called n times.
47493 // This is used to defer the resolution until a transition has resovled *and*
47494 // the frame has completed. If it's not done this way, then we get a race
47495 // condition in which the animation might resolve before a transition is complete
47496 // or vice versa.
47497 function callbackOnNthTime(cb, n) {
47498 var cnt = 0;
47499 return function() {
47500 if(cb && ++cnt === n) {
47501 return cb();
47502 }
47503 };
47504 }
47505
47506 return new Promise(function(resolve, reject) {
47507 function discardExistingFrames() {
47508 if(trans._frameQueue.length === 0) {
47509 return;
47510 }
47511
47512 while(trans._frameQueue.length) {
47513 var next = trans._frameQueue.pop();
47514 if(next.onInterrupt) {
47515 next.onInterrupt();
47516 }
47517 }
47518
47519 gd.emit('plotly_animationinterrupted', []);
47520 }
47521
47522 function queueFrames(frameList) {
47523 if(frameList.length === 0) return;
47524
47525 for(var i = 0; i < frameList.length; i++) {
47526 var computedFrame;
47527
47528 if(frameList[i].type === 'byname') {
47529 // If it's a named frame, compute it:
47530 computedFrame = Plots.computeFrame(gd, frameList[i].name);
47531 } else {
47532 // Otherwise we must have been given a simple object, so treat
47533 // the input itself as the computed frame.
47534 computedFrame = frameList[i].data;
47535 }
47536
47537 var frameOpts = getFrameOpts(i);
47538 var transitionOpts = getTransitionOpts(i);
47539
47540 // It doesn't make much sense for the transition duration to be greater than
47541 // the frame duration, so limit it:
47542 transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration);
47543
47544 var nextFrame = {
47545 frame: computedFrame,
47546 name: frameList[i].name,
47547 frameOpts: frameOpts,
47548 transitionOpts: transitionOpts,
47549 };
47550 if(i === frameList.length - 1) {
47551 // The last frame in this .animate call stores the promise resolve
47552 // and reject callbacks. This is how we ensure that the animation
47553 // loop (which may exist as a result of a *different* .animate call)
47554 // still resolves or rejecdts this .animate call's promise. once it's
47555 // complete.
47556 nextFrame.onComplete = callbackOnNthTime(resolve, 2);
47557 nextFrame.onInterrupt = reject;
47558 }
47559
47560 trans._frameQueue.push(nextFrame);
47561 }
47562
47563 // Set it as never having transitioned to a frame. This will cause the animation
47564 // loop to immediately transition to the next frame (which, for immediate mode,
47565 // is the first frame in the list since all others would have been discarded
47566 // below)
47567 if(animationOpts.mode === 'immediate') {
47568 trans._lastFrameAt = -Infinity;
47569 }
47570
47571 // Only it's not already running, start a RAF loop. This could be avoided in the
47572 // case that there's only one frame, but it significantly complicated the logic
47573 // and only sped things up by about 5% or so for a lorenz attractor simulation.
47574 // It would be a fine thing to implement, but the benefit of that optimization
47575 // doesn't seem worth the extra complexity.
47576 if(!trans._animationRaf) {
47577 beginAnimationLoop();
47578 }
47579 }
47580
47581 function stopAnimationLoop() {
47582 gd.emit('plotly_animated');
47583
47584 // Be sure to unset also since it's how we know whether a loop is already running:
47585 window.cancelAnimationFrame(trans._animationRaf);
47586 trans._animationRaf = null;
47587 }
47588
47589 function nextFrame() {
47590 if(trans._currentFrame && trans._currentFrame.onComplete) {
47591 // Execute the callback and unset it to ensure it doesn't
47592 // accidentally get called twice
47593 trans._currentFrame.onComplete();
47594 }
47595
47596 var newFrame = trans._currentFrame = trans._frameQueue.shift();
47597
47598 if(newFrame) {
47599 // Since it's sometimes necessary to do deep digging into frame data,
47600 // we'll consider it not 100% impossible for nulls or numbers to sneak through,
47601 // so check when casting the name, just to be absolutely certain:
47602 var stringName = newFrame.name ? newFrame.name.toString() : null;
47603 gd._fullLayout._currentFrame = stringName;
47604
47605 trans._lastFrameAt = Date.now();
47606 trans._timeToNext = newFrame.frameOpts.duration;
47607
47608 // This is simply called and it's left to .transition to decide how to manage
47609 // interrupting current transitions. That means we don't need to worry about
47610 // how it resolves or what happens after this:
47611 Plots.transition(gd,
47612 newFrame.frame.data,
47613 newFrame.frame.layout,
47614 helpers.coerceTraceIndices(gd, newFrame.frame.traces),
47615 newFrame.frameOpts,
47616 newFrame.transitionOpts
47617 ).then(function() {
47618 if(newFrame.onComplete) {
47619 newFrame.onComplete();
47620 }
47621 });
47622
47623 gd.emit('plotly_animatingframe', {
47624 name: stringName,
47625 frame: newFrame.frame,
47626 animation: {
47627 frame: newFrame.frameOpts,
47628 transition: newFrame.transitionOpts,
47629 }
47630 });
47631 } else {
47632 // If there are no more frames, then stop the RAF loop:
47633 stopAnimationLoop();
47634 }
47635 }
47636
47637 function beginAnimationLoop() {
47638 gd.emit('plotly_animating');
47639
47640 // If no timer is running, then set last frame = long ago so that the next
47641 // frame is immediately transitioned:
47642 trans._lastFrameAt = -Infinity;
47643 trans._timeToNext = 0;
47644 trans._runningTransitions = 0;
47645 trans._currentFrame = null;
47646
47647 var doFrame = function() {
47648 // This *must* be requested before nextFrame since nextFrame may decide
47649 // to cancel it if there's nothing more to animated:
47650 trans._animationRaf = window.requestAnimationFrame(doFrame);
47651
47652 // Check if we're ready for a new frame:
47653 if(Date.now() - trans._lastFrameAt > trans._timeToNext) {
47654 nextFrame();
47655 }
47656 };
47657
47658 doFrame();
47659 }
47660
47661 // This is an animate-local counter that helps match up option input list
47662 // items with the particular frame.
47663 var configCounter = 0;
47664 function setTransitionConfig(frame) {
47665 if(Array.isArray(transitionOpts)) {
47666 if(configCounter >= transitionOpts.length) {
47667 frame.transitionOpts = transitionOpts[configCounter];
47668 } else {
47669 frame.transitionOpts = transitionOpts[0];
47670 }
47671 } else {
47672 frame.transitionOpts = transitionOpts;
47673 }
47674 configCounter++;
47675 return frame;
47676 }
47677
47678 // Disambiguate what's sort of frames have been received
47679 var i, frame;
47680 var frameList = [];
47681 var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null;
47682 var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList);
47683 var isSingleFrame = !allFrames && !isFrameArray && Lib.isPlainObject(frameOrGroupNameOrFrameList);
47684
47685 if(isSingleFrame) {
47686 // In this case, a simple object has been passed to animate.
47687 frameList.push({
47688 type: 'object',
47689 data: setTransitionConfig(Lib.extendFlat({}, frameOrGroupNameOrFrameList))
47690 });
47691 } else if(allFrames || ['string', 'number'].indexOf(typeof frameOrGroupNameOrFrameList) !== -1) {
47692 // In this case, null or undefined has been passed so that we want to
47693 // animate *all* currently defined frames
47694 for(i = 0; i < trans._frames.length; i++) {
47695 frame = trans._frames[i];
47696
47697 if(!frame) continue;
47698
47699 if(allFrames || String(frame.group) === String(frameOrGroupNameOrFrameList)) {
47700 frameList.push({
47701 type: 'byname',
47702 name: String(frame.name),
47703 data: setTransitionConfig({name: frame.name})
47704 });
47705 }
47706 }
47707 } else if(isFrameArray) {
47708 for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) {
47709 var frameOrName = frameOrGroupNameOrFrameList[i];
47710 if(['number', 'string'].indexOf(typeof frameOrName) !== -1) {
47711 frameOrName = String(frameOrName);
47712 // In this case, there's an array and this frame is a string name:
47713 frameList.push({
47714 type: 'byname',
47715 name: frameOrName,
47716 data: setTransitionConfig({name: frameOrName})
47717 });
47718 } else if(Lib.isPlainObject(frameOrName)) {
47719 frameList.push({
47720 type: 'object',
47721 data: setTransitionConfig(Lib.extendFlat({}, frameOrName))
47722 });
47723 }
47724 }
47725 }
47726
47727 // Verify that all of these frames actually exist; return and reject if not:
47728 for(i = 0; i < frameList.length; i++) {
47729 frame = frameList[i];
47730 if(frame.type === 'byname' && !trans._frameHash[frame.data.name]) {
47731 Lib.warn('animate failure: frame not found: "' + frame.data.name + '"');
47732 reject();
47733 return;
47734 }
47735 }
47736
47737 // If the mode is either next or immediate, then all currently queued frames must
47738 // be dumped and the corresponding .animate promises rejected.
47739 if(['next', 'immediate'].indexOf(animationOpts.mode) !== -1) {
47740 discardExistingFrames();
47741 }
47742
47743 if(animationOpts.direction === 'reverse') {
47744 frameList.reverse();
47745 }
47746
47747 var currentFrame = gd._fullLayout._currentFrame;
47748 if(currentFrame && animationOpts.fromcurrent) {
47749 var idx = -1;
47750 for(i = 0; i < frameList.length; i++) {
47751 frame = frameList[i];
47752 if(frame.type === 'byname' && frame.name === currentFrame) {
47753 idx = i;
47754 break;
47755 }
47756 }
47757
47758 if(idx > 0 && idx < frameList.length - 1) {
47759 var filteredFrameList = [];
47760 for(i = 0; i < frameList.length; i++) {
47761 frame = frameList[i];
47762 if(frameList[i].type !== 'byname' || i > idx) {
47763 filteredFrameList.push(frame);
47764 }
47765 }
47766 frameList = filteredFrameList;
47767 }
47768 }
47769
47770 if(frameList.length > 0) {
47771 queueFrames(frameList);
47772 } else {
47773 // This is the case where there were simply no frames. It's a little strange
47774 // since there's not much to do:
47775 gd.emit('plotly_animated');
47776 resolve();
47777 }
47778 });
47779}
47780
47781/**
47782 * Register new frames
47783 *
47784 * @param {string id or DOM element} gd
47785 * the id or DOM element of the graph container div
47786 *
47787 * @param {array of objects} frameList
47788 * list of frame definitions, in which each object includes any of:
47789 * - name: {string} name of frame to add
47790 * - data: {array of objects} trace data
47791 * - layout {object} layout definition
47792 * - traces {array} trace indices
47793 * - baseframe {string} name of frame from which this frame gets defaults
47794 *
47795 * @param {array of integers} indices
47796 * an array of integer indices matching the respective frames in `frameList`. If not
47797 * provided, an index will be provided in serial order. If already used, the frame
47798 * will be overwritten.
47799 */
47800function addFrames(gd, frameList, indices) {
47801 gd = Lib.getGraphDiv(gd);
47802
47803 if(frameList === null || frameList === undefined) {
47804 return Promise.resolve();
47805 }
47806
47807 if(!Lib.isPlotDiv(gd)) {
47808 throw new Error(
47809 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
47810 'to create a plot before adding frames. For more details, see ' +
47811 'https://plotly.com/javascript/animations/'
47812 );
47813 }
47814
47815 var i, frame, j, idx;
47816 var _frames = gd._transitionData._frames;
47817 var _frameHash = gd._transitionData._frameHash;
47818
47819
47820 if(!Array.isArray(frameList)) {
47821 throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList);
47822 }
47823
47824 // Create a sorted list of insertions since we run into lots of problems if these
47825 // aren't in ascending order of index:
47826 //
47827 // Strictly for sorting. Make sure this is guaranteed to never collide with any
47828 // already-exisisting indices:
47829 var bigIndex = _frames.length + frameList.length * 2;
47830
47831 var insertions = [];
47832 var _frameHashLocal = {};
47833 for(i = frameList.length - 1; i >= 0; i--) {
47834 if(!Lib.isPlainObject(frameList[i])) continue;
47835
47836 // The entire logic for checking for this type of name collision can be removed once we migrate to ES6 and
47837 // use a Map instead of an Object instance, as Map keys aren't converted to strings.
47838 var lookupName = frameList[i].name;
47839 var name = (_frameHash[lookupName] || _frameHashLocal[lookupName] || {}).name;
47840 var newName = frameList[i].name;
47841 var collisionPresent = _frameHash[name] || _frameHashLocal[name];
47842
47843 if(name && newName && typeof newName === 'number' && collisionPresent && numericNameWarningCount < numericNameWarningCountLimit) {
47844 numericNameWarningCount++;
47845
47846 Lib.warn('addFrames: overwriting frame "' + (_frameHash[name] || _frameHashLocal[name]).name +
47847 '" with a frame whose name of type "number" also equates to "' +
47848 name + '". This is valid but may potentially lead to unexpected ' +
47849 'behavior since all plotly.js frame names are stored internally ' +
47850 'as strings.');
47851
47852 if(numericNameWarningCount === numericNameWarningCountLimit) {
47853 Lib.warn('addFrames: This API call has yielded too many of these warnings. ' +
47854 'For the rest of this call, further warnings about numeric frame ' +
47855 'names will be suppressed.');
47856 }
47857 }
47858
47859 _frameHashLocal[lookupName] = {name: lookupName};
47860
47861 insertions.push({
47862 frame: Plots.supplyFrameDefaults(frameList[i]),
47863 index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
47864 });
47865 }
47866
47867 // Sort this, taking note that undefined insertions end up at the end:
47868 insertions.sort(function(a, b) {
47869 if(a.index > b.index) return -1;
47870 if(a.index < b.index) return 1;
47871 return 0;
47872 });
47873
47874 var ops = [];
47875 var revops = [];
47876 var frameCount = _frames.length;
47877
47878 for(i = insertions.length - 1; i >= 0; i--) {
47879 frame = insertions[i].frame;
47880
47881 if(typeof frame.name === 'number') {
47882 Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' +
47883 'implicitly cast to strings');
47884 }
47885
47886 if(!frame.name) {
47887 // Repeatedly assign a default name, incrementing the counter each time until
47888 // we get a name that's not in the hashed lookup table:
47889 while(_frameHash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
47890 }
47891
47892 if(_frameHash[frame.name]) {
47893 // If frame is present, overwrite its definition:
47894 for(j = 0; j < _frames.length; j++) {
47895 if((_frames[j] || {}).name === frame.name) break;
47896 }
47897 ops.push({type: 'replace', index: j, value: frame});
47898 revops.unshift({type: 'replace', index: j, value: _frames[j]});
47899 } else {
47900 // Otherwise insert it at the end of the list:
47901 idx = Math.max(0, Math.min(insertions[i].index, frameCount));
47902
47903 ops.push({type: 'insert', index: idx, value: frame});
47904 revops.unshift({type: 'delete', index: idx});
47905 frameCount++;
47906 }
47907 }
47908
47909 var undoFunc = Plots.modifyFrames;
47910 var redoFunc = Plots.modifyFrames;
47911 var undoArgs = [gd, revops];
47912 var redoArgs = [gd, ops];
47913
47914 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
47915
47916 return Plots.modifyFrames(gd, ops);
47917}
47918
47919/**
47920 * Delete frame
47921 *
47922 * @param {string id or DOM element} gd
47923 * the id or DOM element of the graph container div
47924 *
47925 * @param {array of integers} frameList
47926 * list of integer indices of frames to be deleted
47927 */
47928function deleteFrames(gd, frameList) {
47929 gd = Lib.getGraphDiv(gd);
47930
47931 if(!Lib.isPlotDiv(gd)) {
47932 throw new Error('This element is not a Plotly plot: ' + gd);
47933 }
47934
47935 var i, idx;
47936 var _frames = gd._transitionData._frames;
47937 var ops = [];
47938 var revops = [];
47939
47940 if(!frameList) {
47941 frameList = [];
47942 for(i = 0; i < _frames.length; i++) {
47943 frameList.push(i);
47944 }
47945 }
47946
47947 frameList = frameList.slice();
47948 frameList.sort();
47949
47950 for(i = frameList.length - 1; i >= 0; i--) {
47951 idx = frameList[i];
47952 ops.push({type: 'delete', index: idx});
47953 revops.unshift({type: 'insert', index: idx, value: _frames[idx]});
47954 }
47955
47956 var undoFunc = Plots.modifyFrames;
47957 var redoFunc = Plots.modifyFrames;
47958 var undoArgs = [gd, revops];
47959 var redoArgs = [gd, ops];
47960
47961 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
47962
47963 return Plots.modifyFrames(gd, ops);
47964}
47965
47966/**
47967 * Purge a graph container div back to its initial pre-Plotly.plot state
47968 *
47969 * @param {string id or DOM element} gd
47970 * the id or DOM element of the graph container div
47971 */
47972function purge(gd) {
47973 gd = Lib.getGraphDiv(gd);
47974
47975 var fullLayout = gd._fullLayout || {};
47976 var fullData = gd._fullData || [];
47977
47978 // remove gl contexts
47979 Plots.cleanPlot([], {}, fullData, fullLayout);
47980
47981 // purge properties
47982 Plots.purge(gd);
47983
47984 // purge event emitter methods
47985 Events.purge(gd);
47986
47987 // remove plot container
47988 if(fullLayout._container) fullLayout._container.remove();
47989
47990 // in contrast to Plotly.Plots.purge which does NOT clear _context!
47991 delete gd._context;
47992
47993 return gd;
47994}
47995
47996// -------------------------------------------------------
47997// makePlotFramework: Create the plot container and axes
47998// -------------------------------------------------------
47999function makePlotFramework(gd) {
48000 var gd3 = d3.select(gd);
48001 var fullLayout = gd._fullLayout;
48002
48003 // Plot container
48004 fullLayout._container = gd3.selectAll('.plot-container').data([0]);
48005 fullLayout._container.enter().insert('div', ':first-child')
48006 .classed('plot-container', true)
48007 .classed('plotly', true);
48008
48009 // Make the svg container
48010 fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
48011 fullLayout._paperdiv.enter().append('div')
48012 .classed('svg-container', true)
48013 .style('position', 'relative');
48014
48015 // Make the graph containers
48016 // start fresh each time we get here, so we know the order comes out
48017 // right, rather than enter/exit which can muck up the order
48018 // TODO: sort out all the ordering so we don't have to
48019 // explicitly delete anything
48020 // FIXME: parcoords reuses this object, not the best pattern
48021 fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
48022 .data([{}]);
48023
48024 fullLayout._glcontainer.enter().append('div')
48025 .classed('gl-container', true);
48026
48027 fullLayout._paperdiv.selectAll('.main-svg').remove();
48028 fullLayout._paperdiv.select('.modebar-container').remove();
48029
48030 fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')
48031 .classed('main-svg', true);
48032
48033 fullLayout._toppaper = fullLayout._paperdiv.append('svg')
48034 .classed('main-svg', true);
48035
48036 fullLayout._modebardiv = fullLayout._paperdiv.append('div');
48037
48038 fullLayout._hoverpaper = fullLayout._paperdiv.append('svg')
48039 .classed('main-svg', true);
48040
48041 if(!fullLayout._uid) {
48042 var otherUids = {};
48043 d3.selectAll('defs').each(function() {
48044 if(this.id) otherUids[this.id.split('-')[1]] = 1;
48045 });
48046 fullLayout._uid = Lib.randstr(otherUids);
48047 }
48048
48049 fullLayout._paperdiv.selectAll('.main-svg')
48050 .attr(xmlnsNamespaces.svgAttrs);
48051
48052 fullLayout._defs = fullLayout._paper.append('defs')
48053 .attr('id', 'defs-' + fullLayout._uid);
48054
48055 fullLayout._clips = fullLayout._defs.append('g')
48056 .classed('clips', true);
48057
48058 fullLayout._topdefs = fullLayout._toppaper.append('defs')
48059 .attr('id', 'topdefs-' + fullLayout._uid);
48060
48061 fullLayout._topclips = fullLayout._topdefs.append('g')
48062 .classed('clips', true);
48063
48064 fullLayout._bgLayer = fullLayout._paper.append('g')
48065 .classed('bglayer', true);
48066
48067 fullLayout._draggers = fullLayout._paper.append('g')
48068 .classed('draglayer', true);
48069
48070 // lower shape/image layer - note that this is behind
48071 // all subplots data/grids but above the backgrounds
48072 // except inset subplots, whose backgrounds are drawn
48073 // inside their own group so that they appear above
48074 // the data for the main subplot
48075 // lower shapes and images which are fully referenced to
48076 // a subplot still get drawn within the subplot's group
48077 // so they will work correctly on insets
48078 var layerBelow = fullLayout._paper.append('g')
48079 .classed('layer-below', true);
48080 fullLayout._imageLowerLayer = layerBelow.append('g')
48081 .classed('imagelayer', true);
48082 fullLayout._shapeLowerLayer = layerBelow.append('g')
48083 .classed('shapelayer', true);
48084
48085 // single cartesian layer for the whole plot
48086 fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true);
48087
48088 // single polar layer for the whole plot
48089 fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true);
48090
48091 // single ternary layer for the whole plot
48092 fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true);
48093
48094 // single geo layer for the whole plot
48095 fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
48096
48097 // single funnelarea layer for the whole plot
48098 fullLayout._funnelarealayer = fullLayout._paper.append('g').classed('funnelarealayer', true);
48099
48100 // single pie layer for the whole plot
48101 fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
48102
48103 // single treemap layer for the whole plot
48104 fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true);
48105
48106 // single sunburst layer for the whole plot
48107 fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true);
48108
48109 // single indicator layer for the whole plot
48110 fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true);
48111
48112 // fill in image server scrape-svg
48113 fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true);
48114
48115 // lastly upper shapes, info (legend, annotations) and hover layers go on top
48116 // these are in a different svg element normally, but get collapsed into a single
48117 // svg when exporting (after inserting 3D)
48118 // upper shapes/images are only those drawn above the whole plot, including subplots
48119 var layerAbove = fullLayout._toppaper.append('g')
48120 .classed('layer-above', true);
48121 fullLayout._imageUpperLayer = layerAbove.append('g')
48122 .classed('imagelayer', true);
48123 fullLayout._shapeUpperLayer = layerAbove.append('g')
48124 .classed('shapelayer', true);
48125
48126 fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
48127 fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true);
48128 fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
48129 fullLayout._hoverlayer = fullLayout._hoverpaper.append('g').classed('hoverlayer', true);
48130
48131 // Make the modebar container
48132 fullLayout._modebardiv
48133 .classed('modebar-container', true)
48134 .style('position', 'absolute')
48135 .style('top', '0px')
48136 .style('right', '0px');
48137
48138 gd.emit('plotly_framework');
48139}
48140
48141exports.animate = animate;
48142exports.addFrames = addFrames;
48143exports.deleteFrames = deleteFrames;
48144
48145exports.addTraces = addTraces;
48146exports.deleteTraces = deleteTraces;
48147exports.extendTraces = extendTraces;
48148exports.moveTraces = moveTraces;
48149exports.prependTraces = prependTraces;
48150
48151exports.newPlot = newPlot;
48152exports.plot = plot;
48153exports.purge = purge;
48154
48155exports.react = react;
48156exports.redraw = redraw;
48157exports.relayout = relayout;
48158exports.restyle = restyle;
48159
48160exports.setPlotConfig = setPlotConfig;
48161
48162exports.update = update;
48163
48164exports._guiRelayout = guiEdit(relayout);
48165exports._guiRestyle = guiEdit(restyle);
48166exports._guiUpdate = guiEdit(update);
48167
48168exports._storeDirectGUIEdit = _storeDirectGUIEdit;
48169
48170},{"../components/color":52,"../components/drawing":74,"../constants/xmlns_namespaces":159,"../lib":178,"../lib/events":172,"../lib/queue":192,"../lib/svg_text_utils":199,"../plots/cartesian/axes":222,"../plots/cartesian/constants":228,"../plots/cartesian/graph_interact":231,"../plots/cartesian/select":241,"../plots/plots":256,"../plots/polar/legacy":259,"../registry":269,"./edit_types":205,"./helpers":206,"./manage_arrays":208,"./plot_config":210,"./plot_schema":211,"./subroutines":213,"d3":16,"fast-isnumeric":18,"has-hover":20}],210:[function(_dereq_,module,exports){
48171/**
48172* Copyright 2012-2020, Plotly, Inc.
48173* All rights reserved.
48174*
48175* This source code is licensed under the MIT license found in the
48176* LICENSE file in the root directory of this source tree.
48177*/
48178
48179'use strict';
48180
48181/**
48182 * This will be transferred over to gd and overridden by
48183 * config args to Plotly.plot.
48184 *
48185 * The defaults are the appropriate settings for plotly.js,
48186 * so we get the right experience without any config argument.
48187 *
48188 * N.B. the config options are not coerced using Lib.coerce so keys
48189 * like `valType` and `values` are only set for documentation purposes
48190 * at the moment.
48191 */
48192
48193var configAttributes = {
48194 staticPlot: {
48195 valType: 'boolean',
48196 dflt: false,
48197
48198 },
48199
48200 plotlyServerURL: {
48201 valType: 'string',
48202 dflt: '',
48203
48204 },
48205
48206 editable: {
48207 valType: 'boolean',
48208 dflt: false,
48209
48210 },
48211 edits: {
48212 annotationPosition: {
48213 valType: 'boolean',
48214 dflt: false,
48215
48216 },
48217 annotationTail: {
48218 valType: 'boolean',
48219 dflt: false,
48220
48221 },
48222 annotationText: {
48223 valType: 'boolean',
48224 dflt: false,
48225
48226 },
48227 axisTitleText: {
48228 valType: 'boolean',
48229 dflt: false,
48230
48231 },
48232 colorbarPosition: {
48233 valType: 'boolean',
48234 dflt: false,
48235
48236 },
48237 colorbarTitleText: {
48238 valType: 'boolean',
48239 dflt: false,
48240
48241 },
48242 legendPosition: {
48243 valType: 'boolean',
48244 dflt: false,
48245
48246 },
48247 legendText: {
48248 valType: 'boolean',
48249 dflt: false,
48250
48251 },
48252 shapePosition: {
48253 valType: 'boolean',
48254 dflt: false,
48255
48256 },
48257 titleText: {
48258 valType: 'boolean',
48259 dflt: false,
48260
48261 }
48262 },
48263
48264 autosizable: {
48265 valType: 'boolean',
48266 dflt: false,
48267
48268 },
48269 responsive: {
48270 valType: 'boolean',
48271 dflt: false,
48272
48273 },
48274 fillFrame: {
48275 valType: 'boolean',
48276 dflt: false,
48277
48278 },
48279 frameMargins: {
48280 valType: 'number',
48281 dflt: 0,
48282 min: 0,
48283 max: 0.5,
48284
48285 },
48286
48287 scrollZoom: {
48288 valType: 'flaglist',
48289 flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
48290 extras: [true, false],
48291 dflt: 'gl3d+geo+mapbox',
48292
48293 },
48294 doubleClick: {
48295 valType: 'enumerated',
48296 values: [false, 'reset', 'autosize', 'reset+autosize'],
48297 dflt: 'reset+autosize',
48298
48299 },
48300 doubleClickDelay: {
48301 valType: 'number',
48302 dflt: 300,
48303 min: 0,
48304
48305 },
48306
48307 showAxisDragHandles: {
48308 valType: 'boolean',
48309 dflt: true,
48310
48311 },
48312 showAxisRangeEntryBoxes: {
48313 valType: 'boolean',
48314 dflt: true,
48315
48316 },
48317
48318 showTips: {
48319 valType: 'boolean',
48320 dflt: true,
48321
48322 },
48323
48324 showLink: {
48325 valType: 'boolean',
48326 dflt: false,
48327
48328 },
48329 linkText: {
48330 valType: 'string',
48331 dflt: 'Edit chart',
48332 noBlank: true,
48333
48334 },
48335 sendData: {
48336 valType: 'boolean',
48337 dflt: true,
48338
48339 },
48340 showSources: {
48341 valType: 'any',
48342 dflt: false,
48343
48344 },
48345
48346 displayModeBar: {
48347 valType: 'enumerated',
48348 values: ['hover', true, false],
48349 dflt: 'hover',
48350
48351 },
48352 showSendToCloud: {
48353 valType: 'boolean',
48354 dflt: false,
48355
48356 },
48357 showEditInChartStudio: {
48358 valType: 'boolean',
48359 dflt: false,
48360
48361 },
48362 modeBarButtonsToRemove: {
48363 valType: 'any',
48364 dflt: [],
48365
48366 },
48367 modeBarButtonsToAdd: {
48368 valType: 'any',
48369 dflt: [],
48370
48371 },
48372 modeBarButtons: {
48373 valType: 'any',
48374 dflt: false,
48375
48376 },
48377 toImageButtonOptions: {
48378 valType: 'any',
48379 dflt: {},
48380
48381 },
48382 displaylogo: {
48383 valType: 'boolean',
48384 dflt: true,
48385
48386 },
48387 watermark: {
48388 valType: 'boolean',
48389 dflt: false,
48390
48391 },
48392
48393 plotGlPixelRatio: {
48394 valType: 'number',
48395 dflt: 2,
48396 min: 1,
48397 max: 4,
48398
48399 },
48400
48401 setBackground: {
48402 valType: 'any',
48403 dflt: 'transparent',
48404
48405 },
48406
48407 topojsonURL: {
48408 valType: 'string',
48409 noBlank: true,
48410 dflt: 'https://cdn.plot.ly/',
48411
48412 },
48413
48414 mapboxAccessToken: {
48415 valType: 'string',
48416 dflt: null,
48417
48418 },
48419
48420 logging: {
48421 valType: 'integer',
48422 min: 0,
48423 max: 2,
48424 dflt: 1,
48425
48426 },
48427
48428 notifyOnLogging: {
48429 valType: 'integer',
48430 min: 0,
48431 max: 2,
48432 dflt: 0,
48433
48434 },
48435
48436 queueLength: {
48437 valType: 'integer',
48438 min: 0,
48439 dflt: 0,
48440
48441 },
48442
48443 globalTransforms: {
48444 valType: 'any',
48445 dflt: [],
48446
48447 },
48448
48449 locale: {
48450 valType: 'string',
48451 dflt: 'en-US',
48452
48453 },
48454
48455 locales: {
48456 valType: 'any',
48457 dflt: {},
48458
48459 }
48460};
48461
48462var dfltConfig = {};
48463
48464function crawl(src, target) {
48465 for(var k in src) {
48466 var obj = src[k];
48467 if(obj.valType) {
48468 target[k] = obj.dflt;
48469 } else {
48470 if(!target[k]) {
48471 target[k] = {};
48472 }
48473 crawl(obj, target[k]);
48474 }
48475 }
48476}
48477
48478crawl(configAttributes, dfltConfig);
48479
48480module.exports = {
48481 configAttributes: configAttributes,
48482 dfltConfig: dfltConfig
48483};
48484
48485},{}],211:[function(_dereq_,module,exports){
48486/**
48487* Copyright 2012-2020, Plotly, Inc.
48488* All rights reserved.
48489*
48490* This source code is licensed under the MIT license found in the
48491* LICENSE file in the root directory of this source tree.
48492*/
48493
48494'use strict';
48495
48496var Registry = _dereq_('../registry');
48497var Lib = _dereq_('../lib');
48498
48499var baseAttributes = _dereq_('../plots/attributes');
48500var baseLayoutAttributes = _dereq_('../plots/layout_attributes');
48501var frameAttributes = _dereq_('../plots/frame_attributes');
48502var animationAttributes = _dereq_('../plots/animation_attributes');
48503var configAttributes = _dereq_('./plot_config').configAttributes;
48504
48505// polar attributes are not part of the Registry yet
48506var polarAreaAttrs = _dereq_('../plots/polar/legacy/area_attributes');
48507var polarAxisAttrs = _dereq_('../plots/polar/legacy/axis_attributes');
48508
48509var editTypes = _dereq_('./edit_types');
48510
48511var extendFlat = Lib.extendFlat;
48512var extendDeepAll = Lib.extendDeepAll;
48513var isPlainObject = Lib.isPlainObject;
48514var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
48515var nestedProperty = Lib.nestedProperty;
48516var valObjectMeta = Lib.valObjectMeta;
48517
48518var IS_SUBPLOT_OBJ = '_isSubplotObj';
48519var IS_LINKED_TO_ARRAY = '_isLinkedToArray';
48520var ARRAY_ATTR_REGEXPS = '_arrayAttrRegexps';
48521var DEPRECATED = '_deprecated';
48522var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, ARRAY_ATTR_REGEXPS, DEPRECATED];
48523
48524exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ;
48525exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY;
48526exports.DEPRECATED = DEPRECATED;
48527exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS;
48528
48529/** Outputs the full plotly.js plot schema
48530 *
48531 * @return {object}
48532 * - defs
48533 * - traces
48534 * - layout
48535 * - transforms
48536 * - frames
48537 * - animations
48538 * - config
48539 */
48540exports.get = function() {
48541 var traces = {};
48542
48543 Registry.allTypes.concat('area').forEach(function(type) {
48544 traces[type] = getTraceAttributes(type);
48545 });
48546
48547 var transforms = {};
48548
48549 Object.keys(Registry.transformsRegistry).forEach(function(type) {
48550 transforms[type] = getTransformAttributes(type);
48551 });
48552
48553 return {
48554 defs: {
48555 valObjects: valObjectMeta,
48556 metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']),
48557 editType: {
48558 traces: editTypes.traces,
48559 layout: editTypes.layout
48560 },
48561 impliedEdits: {
48562
48563 }
48564 },
48565
48566 traces: traces,
48567 layout: getLayoutAttributes(),
48568
48569 transforms: transforms,
48570
48571 frames: getFramesAttributes(),
48572 animation: formatAttributes(animationAttributes),
48573
48574 config: formatAttributes(configAttributes)
48575 };
48576};
48577
48578/**
48579 * Crawl the attribute tree, recursively calling a callback function
48580 *
48581 * @param {object} attrs
48582 * The node of the attribute tree (e.g. the root) from which recursion originates
48583 * @param {Function} callback
48584 * A callback function with the signature:
48585 * @callback callback
48586 * @param {object} attr an attribute
48587 * @param {String} attrName name string
48588 * @param {object[]} attrs all the attributes
48589 * @param {Number} level the recursion level, 0 at the root
48590 * @param {String} fullAttrString full attribute name (ie 'marker.line')
48591 * @param {Number} [specifiedLevel]
48592 * The level in the tree, in order to let the callback function detect descend or backtrack,
48593 * typically unsupplied (implied 0), just used by the self-recursive call.
48594 * The necessity arises because the tree traversal is not controlled by callback return values.
48595 * The decision to not use callback return values for controlling tree pruning arose from
48596 * the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions
48597 * precedes the callback call.
48598 * @param {string} [attrString]
48599 * the path to the current attribute, as an attribute string (ie 'marker.line')
48600 * typically unsupplied, but you may supply it if you want to disambiguate which attrs tree you
48601 * are starting from
48602 *
48603 * @return {object} transformOut
48604 * copy of transformIn that contains attribute defaults
48605 */
48606exports.crawl = function(attrs, callback, specifiedLevel, attrString) {
48607 var level = specifiedLevel || 0;
48608 attrString = attrString || '';
48609
48610 Object.keys(attrs).forEach(function(attrName) {
48611 var attr = attrs[attrName];
48612
48613 if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return;
48614
48615 var fullAttrString = (attrString ? attrString + '.' : '') + attrName;
48616 callback(attr, attrName, attrs, level, fullAttrString);
48617
48618 if(exports.isValObject(attr)) return;
48619
48620 if(isPlainObject(attr) && attrName !== 'impliedEdits') {
48621 exports.crawl(attr, callback, level + 1, fullAttrString);
48622 }
48623 });
48624};
48625
48626/** Is object a value object (or a container object)?
48627 *
48628 * @param {object} obj
48629 * @return {boolean}
48630 * returns true for a valid value object and
48631 * false for tree nodes in the attribute hierarchy
48632 */
48633exports.isValObject = function(obj) {
48634 return obj && obj.valType !== undefined;
48635};
48636
48637/**
48638 * Find all data array attributes in a given trace object - including
48639 * `arrayOk` attributes.
48640 *
48641 * @param {object} trace
48642 * full trace object that contains a reference to `_module.attributes`
48643 *
48644 * @return {array} arrayAttributes
48645 * list of array attributes for the given trace
48646 */
48647exports.findArrayAttributes = function(trace) {
48648 var arrayAttributes = [];
48649 var stack = [];
48650 var isArrayStack = [];
48651 var baseContainer, baseAttrName;
48652
48653 function callback(attr, attrName, attrs, level) {
48654 stack = stack.slice(0, level).concat([attrName]);
48655 isArrayStack = isArrayStack.slice(0, level).concat([attr && attr._isLinkedToArray]);
48656
48657 var splittableAttr = (
48658 attr &&
48659 (attr.valType === 'data_array' || attr.arrayOk === true) &&
48660 !(stack[level - 1] === 'colorbar' && (attrName === 'ticktext' || attrName === 'tickvals'))
48661 );
48662
48663 // Manually exclude 'colorbar.tickvals' and 'colorbar.ticktext' for now
48664 // which are declared as `valType: 'data_array'` but scale independently of
48665 // the coordinate arrays.
48666 //
48667 // Down the road, we might want to add a schema field (e.g `uncorrelatedArray: true`)
48668 // to distinguish attributes of the likes.
48669
48670 if(!splittableAttr) return;
48671
48672 crawlIntoTrace(baseContainer, 0, '');
48673 }
48674
48675 function crawlIntoTrace(container, i, astrPartial) {
48676 var item = container[stack[i]];
48677 var newAstrPartial = astrPartial + stack[i];
48678 if(i === stack.length - 1) {
48679 if(isArrayOrTypedArray(item)) {
48680 arrayAttributes.push(baseAttrName + newAstrPartial);
48681 }
48682 } else {
48683 if(isArrayStack[i]) {
48684 if(Array.isArray(item)) {
48685 for(var j = 0; j < item.length; j++) {
48686 if(isPlainObject(item[j])) {
48687 crawlIntoTrace(item[j], i + 1, newAstrPartial + '[' + j + '].');
48688 }
48689 }
48690 }
48691 } else if(isPlainObject(item)) {
48692 crawlIntoTrace(item, i + 1, newAstrPartial + '.');
48693 }
48694 }
48695 }
48696
48697 baseContainer = trace;
48698 baseAttrName = '';
48699 exports.crawl(baseAttributes, callback);
48700 if(trace._module && trace._module.attributes) {
48701 exports.crawl(trace._module.attributes, callback);
48702 }
48703
48704 var transforms = trace.transforms;
48705 if(transforms) {
48706 for(var i = 0; i < transforms.length; i++) {
48707 var transform = transforms[i];
48708 var module = transform._module;
48709
48710 if(module) {
48711 baseAttrName = 'transforms[' + i + '].';
48712 baseContainer = transform;
48713
48714 exports.crawl(module.attributes, callback);
48715 }
48716 }
48717 }
48718
48719 return arrayAttributes;
48720};
48721
48722/*
48723 * Find the valObject for one attribute in an existing trace
48724 *
48725 * @param {object} trace
48726 * full trace object that contains a reference to `_module.attributes`
48727 * @param {object} parts
48728 * an array of parts, like ['transforms', 1, 'value']
48729 * typically from nestedProperty(...).parts
48730 *
48731 * @return {object|false}
48732 * the valObject for this attribute, or the last found parent
48733 * in some cases the innermost valObject will not exist, for example
48734 * `valType: 'any'` attributes where we might set a part of the attribute.
48735 * In that case, stop at the deepest valObject we *do* find.
48736 */
48737exports.getTraceValObject = function(trace, parts) {
48738 var head = parts[0];
48739 var i = 1; // index to start recursing from
48740 var moduleAttrs, valObject;
48741
48742 if(head === 'transforms') {
48743 if(parts.length === 1) {
48744 return baseAttributes.transforms;
48745 }
48746 var transforms = trace.transforms;
48747 if(!Array.isArray(transforms) || !transforms.length) return false;
48748 var tNum = parts[1];
48749 if(!isIndex(tNum) || tNum >= transforms.length) {
48750 return false;
48751 }
48752 moduleAttrs = (Registry.transformsRegistry[transforms[tNum].type] || {}).attributes;
48753 valObject = moduleAttrs && moduleAttrs[parts[2]];
48754 i = 3; // start recursing only inside the transform
48755 } else if(trace.type === 'area') {
48756 valObject = polarAreaAttrs[head];
48757 } else {
48758 // first look in the module for this trace
48759 // components have already merged their trace attributes in here
48760 var _module = trace._module;
48761 if(!_module) _module = (Registry.modules[trace.type || baseAttributes.type.dflt] || {})._module;
48762 if(!_module) return false;
48763
48764 moduleAttrs = _module.attributes;
48765 valObject = moduleAttrs && moduleAttrs[head];
48766
48767 // then look in the subplot attributes
48768 if(!valObject) {
48769 var subplotModule = _module.basePlotModule;
48770 if(subplotModule && subplotModule.attributes) {
48771 valObject = subplotModule.attributes[head];
48772 }
48773 }
48774
48775 // finally look in the global attributes
48776 if(!valObject) valObject = baseAttributes[head];
48777 }
48778
48779 return recurseIntoValObject(valObject, parts, i);
48780};
48781
48782/*
48783 * Find the valObject for one layout attribute
48784 *
48785 * @param {array} parts
48786 * an array of parts, like ['annotations', 1, 'x']
48787 * typically from nestedProperty(...).parts
48788 *
48789 * @return {object|false}
48790 * the valObject for this attribute, or the last found parent
48791 * in some cases the innermost valObject will not exist, for example
48792 * `valType: 'any'` attributes where we might set a part of the attribute.
48793 * In that case, stop at the deepest valObject we *do* find.
48794 */
48795exports.getLayoutValObject = function(fullLayout, parts) {
48796 var valObject = layoutHeadAttr(fullLayout, parts[0]);
48797
48798 return recurseIntoValObject(valObject, parts, 1);
48799};
48800
48801function layoutHeadAttr(fullLayout, head) {
48802 var i, key, _module, attributes;
48803
48804 // look for attributes of the subplot types used on the plot
48805 var basePlotModules = fullLayout._basePlotModules;
48806 if(basePlotModules) {
48807 var out;
48808 for(i = 0; i < basePlotModules.length; i++) {
48809 _module = basePlotModules[i];
48810 if(_module.attrRegex && _module.attrRegex.test(head)) {
48811 // if a module defines overrides, these take precedence
48812 // initially this is to allow gl2d different editTypes from svg cartesian
48813 if(_module.layoutAttrOverrides) return _module.layoutAttrOverrides;
48814
48815 // otherwise take the first attributes we find
48816 if(!out && _module.layoutAttributes) out = _module.layoutAttributes;
48817 }
48818
48819 // a module can also override the behavior of base (and component) module layout attrs
48820 // again see gl2d for initial use case
48821 var baseOverrides = _module.baseLayoutAttrOverrides;
48822 if(baseOverrides && head in baseOverrides) return baseOverrides[head];
48823 }
48824 if(out) return out;
48825 }
48826
48827 // look for layout attributes contributed by traces on the plot
48828 var modules = fullLayout._modules;
48829 if(modules) {
48830 for(i = 0; i < modules.length; i++) {
48831 attributes = modules[i].layoutAttributes;
48832 if(attributes && head in attributes) {
48833 return attributes[head];
48834 }
48835 }
48836 }
48837
48838 /*
48839 * Next look in components.
48840 * Components that define a schema have already merged this into
48841 * base and subplot attribute defs, so ignore these.
48842 * Others (older style) all put all their attributes
48843 * inside a container matching the module `name`
48844 * eg `attributes` (array) or `legend` (object)
48845 */
48846 for(key in Registry.componentsRegistry) {
48847 _module = Registry.componentsRegistry[key];
48848 if(_module.name === 'colorscale' && head.indexOf('coloraxis') === 0) {
48849 return _module.layoutAttributes[head];
48850 } else if(!_module.schema && (head === _module.name)) {
48851 return _module.layoutAttributes;
48852 }
48853 }
48854
48855 if(head in baseLayoutAttributes) return baseLayoutAttributes[head];
48856
48857 // Polar doesn't populate _modules or _basePlotModules
48858 // just fall back on these when the others fail
48859 if(head === 'radialaxis' || head === 'angularaxis') {
48860 return polarAxisAttrs[head];
48861 }
48862 return polarAxisAttrs.layout[head] || false;
48863}
48864
48865function recurseIntoValObject(valObject, parts, i) {
48866 if(!valObject) return false;
48867
48868 if(valObject._isLinkedToArray) {
48869 // skip array index, abort if we try to dive into an array without an index
48870 if(isIndex(parts[i])) i++;
48871 else if(i < parts.length) return false;
48872 }
48873
48874 // now recurse as far as we can. Occasionally we have an attribute
48875 // setting an internal part below what's in the schema; just return
48876 // the innermost schema item we find.
48877 for(; i < parts.length; i++) {
48878 var newValObject = valObject[parts[i]];
48879 if(isPlainObject(newValObject)) valObject = newValObject;
48880 else break;
48881
48882 if(i === parts.length - 1) break;
48883
48884 if(valObject._isLinkedToArray) {
48885 i++;
48886 if(!isIndex(parts[i])) return false;
48887 } else if(valObject.valType === 'info_array') {
48888 i++;
48889 var index = parts[i];
48890 if(!isIndex(index)) return false;
48891
48892 var items = valObject.items;
48893 if(Array.isArray(items)) {
48894 if(index >= items.length) return false;
48895 if(valObject.dimensions === 2) {
48896 i++;
48897 if(parts.length === i) return valObject;
48898 var index2 = parts[i];
48899 if(!isIndex(index2)) return false;
48900 valObject = items[index][index2];
48901 } else valObject = items[index];
48902 } else {
48903 valObject = items;
48904 }
48905 }
48906 }
48907
48908 return valObject;
48909}
48910
48911// note: this is different from Lib.isIndex, this one doesn't accept numeric
48912// strings, only actual numbers.
48913function isIndex(val) {
48914 return val === Math.round(val) && val >= 0;
48915}
48916
48917function getTraceAttributes(type) {
48918 var _module, basePlotModule;
48919
48920 if(type === 'area') {
48921 _module = { attributes: polarAreaAttrs };
48922 basePlotModule = {};
48923 } else {
48924 _module = Registry.modules[type]._module,
48925 basePlotModule = _module.basePlotModule;
48926 }
48927
48928 var attributes = {};
48929
48930 // make 'type' the first attribute in the object
48931 attributes.type = null;
48932
48933 var copyBaseAttributes = extendDeepAll({}, baseAttributes);
48934 var copyModuleAttributes = extendDeepAll({}, _module.attributes);
48935
48936 // prune global-level trace attributes that are already defined in a trace
48937 exports.crawl(copyModuleAttributes, function(attr, attrName, attrs, level, fullAttrString) {
48938 nestedProperty(copyBaseAttributes, fullAttrString).set(undefined);
48939 // Prune undefined attributes
48940 if(attr === undefined) nestedProperty(copyModuleAttributes, fullAttrString).set(undefined);
48941 });
48942
48943 // base attributes (same for all trace types)
48944 extendDeepAll(attributes, copyBaseAttributes);
48945
48946 // prune-out base attributes based on trace module categories
48947 if(Registry.traceIs(type, 'noOpacity')) {
48948 delete attributes.opacity;
48949 }
48950 if(!Registry.traceIs(type, 'showLegend')) {
48951 delete attributes.showlegend;
48952 delete attributes.legendgroup;
48953 }
48954 if(Registry.traceIs(type, 'noHover')) {
48955 delete attributes.hoverinfo;
48956 delete attributes.hoverlabel;
48957 }
48958 if(!_module.selectPoints) {
48959 delete attributes.selectedpoints;
48960 }
48961
48962 // module attributes
48963 extendDeepAll(attributes, copyModuleAttributes);
48964
48965 // subplot attributes
48966 if(basePlotModule.attributes) {
48967 extendDeepAll(attributes, basePlotModule.attributes);
48968 }
48969
48970 // 'type' gets overwritten by baseAttributes; reset it here
48971 attributes.type = type;
48972
48973 var out = {
48974 meta: _module.meta || {},
48975 categories: _module.categories || {},
48976 animatable: Boolean(_module.animatable),
48977 type: type,
48978 attributes: formatAttributes(attributes),
48979 };
48980
48981 // trace-specific layout attributes
48982 if(_module.layoutAttributes) {
48983 var layoutAttributes = {};
48984
48985 extendDeepAll(layoutAttributes, _module.layoutAttributes);
48986 out.layoutAttributes = formatAttributes(layoutAttributes);
48987 }
48988
48989 // drop anim:true in non-animatable modules
48990 if(!_module.animatable) {
48991 exports.crawl(out, function(attr) {
48992 if(exports.isValObject(attr) && 'anim' in attr) {
48993 delete attr.anim;
48994 }
48995 });
48996 }
48997
48998 return out;
48999}
49000
49001function getLayoutAttributes() {
49002 var layoutAttributes = {};
49003 var key, _module;
49004
49005 // global layout attributes
49006 extendDeepAll(layoutAttributes, baseLayoutAttributes);
49007
49008 // add base plot module layout attributes
49009 for(key in Registry.subplotsRegistry) {
49010 _module = Registry.subplotsRegistry[key];
49011
49012 if(!_module.layoutAttributes) continue;
49013
49014 if(Array.isArray(_module.attr)) {
49015 for(var i = 0; i < _module.attr.length; i++) {
49016 handleBasePlotModule(layoutAttributes, _module, _module.attr[i]);
49017 }
49018 } else {
49019 var astr = _module.attr === 'subplot' ? _module.name : _module.attr;
49020 handleBasePlotModule(layoutAttributes, _module, astr);
49021 }
49022 }
49023
49024 // polar layout attributes
49025 layoutAttributes = assignPolarLayoutAttrs(layoutAttributes);
49026
49027 // add registered components layout attributes
49028 for(key in Registry.componentsRegistry) {
49029 _module = Registry.componentsRegistry[key];
49030 var schema = _module.schema;
49031
49032 if(schema && (schema.subplots || schema.layout)) {
49033 /*
49034 * Components with defined schema have already been merged in at register time
49035 * but a few components define attributes that apply only to xaxis
49036 * not yaxis (rangeselector, rangeslider) - delete from y schema.
49037 * Note that the input attributes for xaxis/yaxis are the same object
49038 * so it's not possible to only add them to xaxis from the start.
49039 * If we ever have such asymmetry the other way, or anywhere else,
49040 * we will need to extend both this code and mergeComponentAttrsToSubplot
49041 * (which will not find yaxis only for example)
49042 */
49043 var subplots = schema.subplots;
49044 if(subplots && subplots.xaxis && !subplots.yaxis) {
49045 for(var xkey in subplots.xaxis) {
49046 delete layoutAttributes.yaxis[xkey];
49047 }
49048 }
49049 } else if(_module.name === 'colorscale') {
49050 extendDeepAll(layoutAttributes, _module.layoutAttributes);
49051 } else if(_module.layoutAttributes) {
49052 // older style without schema need to be explicitly merged in now
49053 insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
49054 }
49055 }
49056
49057 return {
49058 layoutAttributes: formatAttributes(layoutAttributes)
49059 };
49060}
49061
49062function getTransformAttributes(type) {
49063 var _module = Registry.transformsRegistry[type];
49064 var attributes = extendDeepAll({}, _module.attributes);
49065
49066 // add registered components transform attributes
49067 Object.keys(Registry.componentsRegistry).forEach(function(k) {
49068 var _module = Registry.componentsRegistry[k];
49069
49070 if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) {
49071 Object.keys(_module.schema.transforms[type]).forEach(function(v) {
49072 insertAttrs(attributes, _module.schema.transforms[type][v], v);
49073 });
49074 }
49075 });
49076
49077 return {
49078 attributes: formatAttributes(attributes)
49079 };
49080}
49081
49082function getFramesAttributes() {
49083 var attrs = {
49084 frames: extendDeepAll({}, frameAttributes)
49085 };
49086
49087 formatAttributes(attrs);
49088
49089 return attrs.frames;
49090}
49091
49092function formatAttributes(attrs) {
49093 mergeValTypeAndRole(attrs);
49094 formatArrayContainers(attrs);
49095 stringify(attrs);
49096
49097 return attrs;
49098}
49099
49100function mergeValTypeAndRole(attrs) {
49101 function makeSrcAttr(attrName) {
49102 return {
49103 valType: 'string',
49104
49105
49106 editType: 'none'
49107 };
49108 }
49109
49110 function callback(attr, attrName, attrs) {
49111 if(exports.isValObject(attr)) {
49112 if(attr.valType === 'data_array') {
49113 // all 'data_array' attrs have role 'data'
49114 attr.role = 'data';
49115 // all 'data_array' attrs have a corresponding 'src' attr
49116 attrs[attrName + 'src'] = makeSrcAttr(attrName);
49117 } else if(attr.arrayOk === true) {
49118 // all 'arrayOk' attrs have a corresponding 'src' attr
49119 attrs[attrName + 'src'] = makeSrcAttr(attrName);
49120 }
49121 } else if(isPlainObject(attr)) {
49122 // all attrs container objects get role 'object'
49123 attr.role = 'object';
49124 }
49125 }
49126
49127 exports.crawl(attrs, callback);
49128}
49129
49130function formatArrayContainers(attrs) {
49131 function callback(attr, attrName, attrs) {
49132 if(!attr) return;
49133
49134 var itemName = attr[IS_LINKED_TO_ARRAY];
49135
49136 if(!itemName) return;
49137
49138 delete attr[IS_LINKED_TO_ARRAY];
49139
49140 attrs[attrName] = { items: {} };
49141 attrs[attrName].items[itemName] = attr;
49142 attrs[attrName].role = 'object';
49143 }
49144
49145 exports.crawl(attrs, callback);
49146}
49147
49148// this can take around 10ms and should only be run from PlotSchema.get(),
49149// to ensure JSON.stringify(PlotSchema.get()) gives the intended result.
49150function stringify(attrs) {
49151 function walk(attr) {
49152 for(var k in attr) {
49153 if(isPlainObject(attr[k])) {
49154 walk(attr[k]);
49155 } else if(Array.isArray(attr[k])) {
49156 for(var i = 0; i < attr[k].length; i++) {
49157 walk(attr[k][i]);
49158 }
49159 } else {
49160 // as JSON.stringify(/test/) // => {}
49161 if(attr[k] instanceof RegExp) {
49162 attr[k] = attr[k].toString();
49163 }
49164 }
49165 }
49166 }
49167
49168 walk(attrs);
49169}
49170
49171function assignPolarLayoutAttrs(layoutAttributes) {
49172 extendFlat(layoutAttributes, {
49173 radialaxis: polarAxisAttrs.radialaxis,
49174 angularaxis: polarAxisAttrs.angularaxis
49175 });
49176
49177 extendFlat(layoutAttributes, polarAxisAttrs.layout);
49178
49179 return layoutAttributes;
49180}
49181
49182function handleBasePlotModule(layoutAttributes, _module, astr) {
49183 var np = nestedProperty(layoutAttributes, astr);
49184 var attrs = extendDeepAll({}, _module.layoutAttributes);
49185
49186 attrs[IS_SUBPLOT_OBJ] = true;
49187 np.set(attrs);
49188}
49189
49190function insertAttrs(baseAttrs, newAttrs, astr) {
49191 var np = nestedProperty(baseAttrs, astr);
49192
49193 np.set(extendDeepAll(np.get() || {}, newAttrs));
49194}
49195
49196},{"../lib":178,"../plots/animation_attributes":217,"../plots/attributes":219,"../plots/frame_attributes":251,"../plots/layout_attributes":254,"../plots/polar/legacy/area_attributes":257,"../plots/polar/legacy/axis_attributes":258,"../registry":269,"./edit_types":205,"./plot_config":210}],212:[function(_dereq_,module,exports){
49197/**
49198* Copyright 2012-2020, Plotly, Inc.
49199* All rights reserved.
49200*
49201* This source code is licensed under the MIT license found in the
49202* LICENSE file in the root directory of this source tree.
49203*/
49204
49205
49206'use strict';
49207
49208var Lib = _dereq_('../lib');
49209var plotAttributes = _dereq_('../plots/attributes');
49210
49211var TEMPLATEITEMNAME = 'templateitemname';
49212
49213var templateAttrs = {
49214 name: {
49215 valType: 'string',
49216
49217 editType: 'none',
49218
49219 }
49220};
49221templateAttrs[TEMPLATEITEMNAME] = {
49222 valType: 'string',
49223
49224 editType: 'calc',
49225
49226};
49227
49228/**
49229 * templatedArray: decorate an attributes object with templating (and array)
49230 * properties.
49231 *
49232 * @param {string} name: the singular form of the array name. Sets
49233 * `_isLinkedToArray` to this, so the schema knows to treat this as an array.
49234 * @param {object} attrs: the item attributes. Since all callers are expected
49235 * to be constructing this object on the spot, we mutate it here for
49236 * performance, rather than extending a new object with it.
49237 *
49238 * @returns {object}: the decorated `attrs` object
49239 */
49240exports.templatedArray = function(name, attrs) {
49241 attrs._isLinkedToArray = name;
49242 attrs.name = templateAttrs.name;
49243 attrs[TEMPLATEITEMNAME] = templateAttrs[TEMPLATEITEMNAME];
49244 return attrs;
49245};
49246
49247/**
49248 * traceTemplater: logic for matching traces to trace templates
49249 *
49250 * @param {object} dataTemplate: collection of {traceType: [{template}, ...]}
49251 * ie each type the template applies to contains a list of template objects,
49252 * to be provided cyclically to data traces of that type.
49253 *
49254 * @returns {object}: {newTrace}, a function:
49255 * newTrace(traceIn): that takes the input traceIn, coerces its type, then
49256 * uses that type to find the next template to apply. returns the output
49257 * traceOut with template attached, ready to continue supplyDefaults.
49258 */
49259exports.traceTemplater = function(dataTemplate) {
49260 var traceCounts = {};
49261 var traceType, typeTemplates;
49262
49263 for(traceType in dataTemplate) {
49264 typeTemplates = dataTemplate[traceType];
49265 if(Array.isArray(typeTemplates) && typeTemplates.length) {
49266 traceCounts[traceType] = 0;
49267 }
49268 }
49269
49270 function newTrace(traceIn) {
49271 traceType = Lib.coerce(traceIn, {}, plotAttributes, 'type');
49272 var traceOut = {type: traceType, _template: null};
49273 if(traceType in traceCounts) {
49274 typeTemplates = dataTemplate[traceType];
49275 // cycle through traces in the template set for this type
49276 var typei = traceCounts[traceType] % typeTemplates.length;
49277 traceCounts[traceType]++;
49278 traceOut._template = typeTemplates[typei];
49279 } else {
49280 // TODO: anything we should do for types missing from the template?
49281 // try to apply some other type? Or just bail as we do here?
49282 // Actually I think yes, we should apply other types; would be nice
49283 // if all scatter* could inherit from each other, and if histogram
49284 // could inherit from bar, etc... but how to specify this? And do we
49285 // compose them, or if a type is present require it to be complete?
49286 // Actually this could apply to layout too - 3D annotations
49287 // inheriting from 2D, axes of different types inheriting from each
49288 // other...
49289 }
49290 return traceOut;
49291 }
49292
49293 return {
49294 newTrace: newTrace
49295 // TODO: function to figure out what's left & what didn't work
49296 };
49297};
49298
49299/**
49300 * newContainer: Create a new sub-container inside `container` and propagate any
49301 * applicable template to it. If there's no template, still propagates
49302 * `undefined` so relinkPrivate will not retain an old template!
49303 *
49304 * @param {object} container: the outer container, should already have _template
49305 * if there *is* a template for this plot
49306 * @param {string} name: the key of the new container to make
49307 * @param {string} baseName: if applicable, a base attribute to take the
49308 * template from, ie for xaxis3 the base would be xaxis
49309 *
49310 * @returns {object}: an object for inclusion _full*, empty except for the
49311 * appropriate template piece
49312 */
49313exports.newContainer = function(container, name, baseName) {
49314 var template = container._template;
49315 var part = template && (template[name] || (baseName && template[baseName]));
49316 if(!Lib.isPlainObject(part)) part = null;
49317
49318 var out = container[name] = {_template: part};
49319 return out;
49320};
49321
49322/**
49323 * arrayTemplater: special logic for templating both defaults and specific items
49324 * in a container array (annotations etc)
49325 *
49326 * @param {object} container: the outer container, should already have _template
49327 * if there *is* a template for this plot
49328 * @param {string} name: the name of the array to template (ie 'annotations')
49329 * will be used to find default ('annotationdefaults' object) and specific
49330 * ('annotations' array) template specs.
49331 * @param {string} inclusionAttr: the attribute determining this item's
49332 * inclusion in the output, usually 'visible' or 'enabled'
49333 *
49334 * @returns {object}: {newItem, defaultItems}, both functions:
49335 * newItem(itemIn): create an output item, bare except for the correct
49336 * template and name(s), as the base for supplyDefaults
49337 * defaultItems(): to be called after all newItem calls, return any
49338 * specific template items that have not already beeen included,
49339 * also as bare output items ready for supplyDefaults.
49340 */
49341exports.arrayTemplater = function(container, name, inclusionAttr) {
49342 var template = container._template;
49343 var defaultsTemplate = template && template[arrayDefaultKey(name)];
49344 var templateItems = template && template[name];
49345 if(!Array.isArray(templateItems) || !templateItems.length) {
49346 templateItems = [];
49347 }
49348
49349 var usedNames = {};
49350
49351 function newItem(itemIn) {
49352 // include name and templateitemname in the output object for ALL
49353 // container array items. Note: you could potentially use different
49354 // name and templateitemname, if you're using one template to make
49355 // another template. templateitemname would be the name in the original
49356 // template, and name is the new "subclassed" item name.
49357 var out = {name: itemIn.name, _input: itemIn};
49358 var templateItemName = out[TEMPLATEITEMNAME] = itemIn[TEMPLATEITEMNAME];
49359
49360 // no itemname: use the default template
49361 if(!validItemName(templateItemName)) {
49362 out._template = defaultsTemplate;
49363 return out;
49364 }
49365
49366 // look for an item matching this itemname
49367 // note these do not inherit from the default template, only the item.
49368 for(var i = 0; i < templateItems.length; i++) {
49369 var templateItem = templateItems[i];
49370 if(templateItem.name === templateItemName) {
49371 // Note: it's OK to use a template item more than once
49372 // but using it at least once will stop it from generating
49373 // a default item at the end.
49374 usedNames[templateItemName] = 1;
49375 out._template = templateItem;
49376 return out;
49377 }
49378 }
49379
49380 // Didn't find a matching template item, so since this item is intended
49381 // to only be modifications it's most likely broken. Hide it unless
49382 // it's explicitly marked visible - in which case it gets NO template,
49383 // not even the default.
49384 out[inclusionAttr] = itemIn[inclusionAttr] || false;
49385 // special falsy value we can look for in validateTemplate
49386 out._template = false;
49387 return out;
49388 }
49389
49390 function defaultItems() {
49391 var out = [];
49392 for(var i = 0; i < templateItems.length; i++) {
49393 var templateItem = templateItems[i];
49394 var name = templateItem.name;
49395 // only allow named items to be added as defaults,
49396 // and only allow each name once
49397 if(validItemName(name) && !usedNames[name]) {
49398 var outi = {
49399 _template: templateItem,
49400 name: name,
49401 _input: {_templateitemname: name}
49402 };
49403 outi[TEMPLATEITEMNAME] = templateItem[TEMPLATEITEMNAME];
49404 out.push(outi);
49405 usedNames[name] = 1;
49406 }
49407 }
49408 return out;
49409 }
49410
49411 return {
49412 newItem: newItem,
49413 defaultItems: defaultItems
49414 };
49415};
49416
49417function validItemName(name) {
49418 return name && typeof name === 'string';
49419}
49420
49421function arrayDefaultKey(name) {
49422 var lastChar = name.length - 1;
49423 if(name.charAt(lastChar) !== 's') {
49424 Lib.warn('bad argument to arrayDefaultKey: ' + name);
49425 }
49426 return name.substr(0, name.length - 1) + 'defaults';
49427}
49428exports.arrayDefaultKey = arrayDefaultKey;
49429
49430/**
49431 * arrayEditor: helper for editing array items that may have come from
49432 * template defaults (in which case they will not exist in the input yet)
49433 *
49434 * @param {object} parentIn: the input container (eg gd.layout)
49435 * @param {string} containerStr: the attribute string for the container inside
49436 * `parentIn`.
49437 * @param {object} itemOut: the _full* item (eg gd._fullLayout.annotations[0])
49438 * that we'll be editing. Assumed to have been created by `arrayTemplater`.
49439 *
49440 * @returns {object}: {modifyBase, modifyItem, getUpdateObj, applyUpdate}, all functions:
49441 * modifyBase(attr, value): Add an update that's *not* related to the item.
49442 * `attr` is the full attribute string.
49443 * modifyItem(attr, value): Add an update to the item. `attr` is just the
49444 * portion of the attribute string inside the item.
49445 * getUpdateObj(): Get the final constructed update object, to use in
49446 * `restyle` or `relayout`. Also resets the update object in case this
49447 * update was canceled.
49448 * applyUpdate(attr, value): optionally add an update `attr: value`,
49449 * then apply it to `parent` which should be the parent of `containerIn`,
49450 * ie the object to which `containerStr` is the attribute string.
49451 */
49452exports.arrayEditor = function(parentIn, containerStr, itemOut) {
49453 var lengthIn = (Lib.nestedProperty(parentIn, containerStr).get() || []).length;
49454 var index = itemOut._index;
49455 // Check that we are indeed off the end of this container.
49456 // Otherwise a devious user could put a key `_templateitemname` in their
49457 // own input and break lots of things.
49458 var templateItemName = (index >= lengthIn) && (itemOut._input || {})._templateitemname;
49459 if(templateItemName) index = lengthIn;
49460 var itemStr = containerStr + '[' + index + ']';
49461
49462 var update;
49463 function resetUpdate() {
49464 update = {};
49465 if(templateItemName) {
49466 update[itemStr] = {};
49467 update[itemStr][TEMPLATEITEMNAME] = templateItemName;
49468 }
49469 }
49470 resetUpdate();
49471
49472 function modifyBase(attr, value) {
49473 update[attr] = value;
49474 }
49475
49476 function modifyItem(attr, value) {
49477 if(templateItemName) {
49478 // we're making a new object: edit that object
49479 Lib.nestedProperty(update[itemStr], attr).set(value);
49480 } else {
49481 // we're editing an existing object: include *just* the edit
49482 update[itemStr + '.' + attr] = value;
49483 }
49484 }
49485
49486 function getUpdateObj() {
49487 var updateOut = update;
49488 resetUpdate();
49489 return updateOut;
49490 }
49491
49492 function applyUpdate(attr, value) {
49493 if(attr) modifyItem(attr, value);
49494 var updateToApply = getUpdateObj();
49495 for(var key in updateToApply) {
49496 Lib.nestedProperty(parentIn, key).set(updateToApply[key]);
49497 }
49498 }
49499
49500 return {
49501 modifyBase: modifyBase,
49502 modifyItem: modifyItem,
49503 getUpdateObj: getUpdateObj,
49504 applyUpdate: applyUpdate
49505 };
49506};
49507
49508},{"../lib":178,"../plots/attributes":219}],213:[function(_dereq_,module,exports){
49509/**
49510* Copyright 2012-2020, Plotly, Inc.
49511* All rights reserved.
49512*
49513* This source code is licensed under the MIT license found in the
49514* LICENSE file in the root directory of this source tree.
49515*/
49516
49517'use strict';
49518
49519var d3 = _dereq_('d3');
49520var Registry = _dereq_('../registry');
49521var Plots = _dereq_('../plots/plots');
49522
49523var Lib = _dereq_('../lib');
49524var clearGlCanvases = _dereq_('../lib/clear_gl_canvases');
49525
49526var Color = _dereq_('../components/color');
49527var Drawing = _dereq_('../components/drawing');
49528var Titles = _dereq_('../components/titles');
49529var ModeBar = _dereq_('../components/modebar');
49530
49531var Axes = _dereq_('../plots/cartesian/axes');
49532var alignmentConstants = _dereq_('../constants/alignment');
49533var axisConstraints = _dereq_('../plots/cartesian/constraints');
49534var enforceAxisConstraints = axisConstraints.enforce;
49535var cleanAxisConstraints = axisConstraints.clean;
49536var doAutoRange = _dereq_('../plots/cartesian/autorange').doAutoRange;
49537
49538var SVG_TEXT_ANCHOR_START = 'start';
49539var SVG_TEXT_ANCHOR_MIDDLE = 'middle';
49540var SVG_TEXT_ANCHOR_END = 'end';
49541
49542exports.layoutStyles = function(gd) {
49543 return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd);
49544};
49545
49546function overlappingDomain(xDomain, yDomain, domains) {
49547 for(var i = 0; i < domains.length; i++) {
49548 var existingX = domains[i][0];
49549 var existingY = domains[i][1];
49550
49551 if(existingX[0] >= xDomain[1] || existingX[1] <= xDomain[0]) {
49552 continue;
49553 }
49554 if(existingY[0] < yDomain[1] && existingY[1] > yDomain[0]) {
49555 return true;
49556 }
49557 }
49558 return false;
49559}
49560
49561function lsInner(gd) {
49562 var fullLayout = gd._fullLayout;
49563 var gs = fullLayout._size;
49564 var pad = gs.p;
49565 var axList = Axes.list(gd, '', true);
49566 var i, subplot, plotinfo, ax, xa, ya;
49567
49568 fullLayout._paperdiv.style({
49569 width: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroWidth && !gd.layout.width) ? '100%' : fullLayout.width + 'px',
49570 height: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroHeight && !gd.layout.height) ? '100%' : fullLayout.height + 'px'
49571 })
49572 .selectAll('.main-svg')
49573 .call(Drawing.setSize, fullLayout.width, fullLayout.height);
49574 gd._context.setBackground(gd, fullLayout.paper_bgcolor);
49575
49576 exports.drawMainTitle(gd);
49577 ModeBar.manage(gd);
49578
49579 // _has('cartesian') means SVG specifically, not GL2D - but GL2D
49580 // can still get here because it makes some of the SVG structure
49581 // for shared features like selections.
49582 if(!fullLayout._has('cartesian')) {
49583 return Plots.previousPromises(gd);
49584 }
49585
49586 function getLinePosition(ax, counterAx, side) {
49587 var lwHalf = ax._lw / 2;
49588
49589 if(ax._id.charAt(0) === 'x') {
49590 if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1);
49591 else if(side === 'top') return counterAx._offset - pad - lwHalf;
49592 return counterAx._offset + counterAx._length + pad + lwHalf;
49593 }
49594
49595 if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1);
49596 else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf;
49597 return counterAx._offset - pad - lwHalf;
49598 }
49599
49600 // some preparation of axis position info
49601 for(i = 0; i < axList.length; i++) {
49602 ax = axList[i];
49603
49604 var counterAx = ax._anchorAxis;
49605
49606 // clear axis line positions, to be set in the subplot loop below
49607 ax._linepositions = {};
49608
49609 // stash crispRounded linewidth so we don't need to pass gd all over the place
49610 ax._lw = Drawing.crispRound(gd, ax.linewidth, 1);
49611
49612 // figure out the main axis line and main mirror line position.
49613 // it's easier to follow the logic if we handle these separately from
49614 // ax._linepositions, which are only used by mirror=allticks
49615 // for non-main-subplot ticks, and mirror=all(ticks)? for zero line
49616 // hiding logic
49617 ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side);
49618 ax._mainMirrorPosition = (ax.mirror && counterAx) ?
49619 getLinePosition(ax, counterAx,
49620 alignmentConstants.OPPOSITE_SIDE[ax.side]) : null;
49621 }
49622
49623 // figure out which backgrounds we need to draw,
49624 // and in which layers to put them
49625 var lowerBackgroundIDs = [];
49626 var backgroundIds = [];
49627 var lowerDomains = [];
49628 // no need to draw background when paper and plot color are the same color,
49629 // activate mode just for large splom (which benefit the most from this
49630 // optimization), but this could apply to all cartesian subplots.
49631 var noNeedForBg = (
49632 Color.opacity(fullLayout.paper_bgcolor) === 1 &&
49633 Color.opacity(fullLayout.plot_bgcolor) === 1 &&
49634 fullLayout.paper_bgcolor === fullLayout.plot_bgcolor
49635 );
49636
49637 for(subplot in fullLayout._plots) {
49638 plotinfo = fullLayout._plots[subplot];
49639
49640 if(plotinfo.mainplot) {
49641 // mainplot is a reference to the main plot this one is overlaid on
49642 // so if it exists, this is an overlaid plot and we don't need to
49643 // give it its own background
49644 if(plotinfo.bg) {
49645 plotinfo.bg.remove();
49646 }
49647 plotinfo.bg = undefined;
49648 } else {
49649 var xDomain = plotinfo.xaxis.domain;
49650 var yDomain = plotinfo.yaxis.domain;
49651 var plotgroup = plotinfo.plotgroup;
49652
49653 if(overlappingDomain(xDomain, yDomain, lowerDomains)) {
49654 var pgNode = plotgroup.node();
49655 var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg');
49656 pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]);
49657 backgroundIds.push(subplot);
49658 } else {
49659 plotgroup.select('rect.bg').remove();
49660 lowerDomains.push([xDomain, yDomain]);
49661 if(!noNeedForBg) {
49662 lowerBackgroundIDs.push(subplot);
49663 backgroundIds.push(subplot);
49664 }
49665 }
49666 }
49667 }
49668
49669 // now create all the lower-layer backgrounds at once now that
49670 // we have the list of subplots that need them
49671 var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg')
49672 .data(lowerBackgroundIDs);
49673
49674 lowerBackgrounds.enter().append('rect')
49675 .classed('bg', true);
49676
49677 lowerBackgrounds.exit().remove();
49678
49679 lowerBackgrounds.each(function(subplot) {
49680 fullLayout._plots[subplot].bg = d3.select(this);
49681 });
49682
49683 // style all backgrounds
49684 for(i = 0; i < backgroundIds.length; i++) {
49685 plotinfo = fullLayout._plots[backgroundIds[i]];
49686 xa = plotinfo.xaxis;
49687 ya = plotinfo.yaxis;
49688
49689 if(plotinfo.bg) {
49690 plotinfo.bg
49691 .call(Drawing.setRect,
49692 xa._offset - pad, ya._offset - pad,
49693 xa._length + 2 * pad, ya._length + 2 * pad)
49694 .call(Color.fill, fullLayout.plot_bgcolor)
49695 .style('stroke-width', 0);
49696 }
49697 }
49698
49699 if(!fullLayout._hasOnlyLargeSploms) {
49700 for(subplot in fullLayout._plots) {
49701 plotinfo = fullLayout._plots[subplot];
49702 xa = plotinfo.xaxis;
49703 ya = plotinfo.yaxis;
49704
49705 // Clip so that data only shows up on the plot area.
49706 var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
49707
49708 var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
49709 s.classed('plotclip', true)
49710 .append('rect');
49711 });
49712
49713 plotinfo.clipRect = plotClip.select('rect').attr({
49714 width: xa._length,
49715 height: ya._length
49716 });
49717
49718 Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset);
49719
49720 var plotClipId;
49721 var layerClipId;
49722
49723 if(plotinfo._hasClipOnAxisFalse) {
49724 plotClipId = null;
49725 layerClipId = clipId;
49726 } else {
49727 plotClipId = clipId;
49728 layerClipId = null;
49729 }
49730
49731 Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);
49732
49733 // stash layer clipId value (null or same as clipId)
49734 // to DRY up Drawing.setClipUrl calls on trace-module and trace layers
49735 // downstream
49736 plotinfo.layerClipId = layerClipId;
49737 }
49738 }
49739
49740 var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop,
49741 leftYLineWidth, rightYLineWidth;
49742 var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight,
49743 connectYBottom, connectYTop;
49744 var extraSubplot;
49745
49746 function xLinePath(y) {
49747 return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight;
49748 }
49749
49750 function xLinePathFree(y) {
49751 return 'M' + xa._offset + ',' + y + 'h' + xa._length;
49752 }
49753
49754 function yLinePath(x) {
49755 return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom;
49756 }
49757
49758 function yLinePathFree(x) {
49759 return 'M' + x + ',' + ya._offset + 'v' + ya._length;
49760 }
49761
49762 function mainPath(ax, pathFn, pathFnFree) {
49763 if(!ax.showline || subplot !== ax._mainSubplot) return '';
49764 if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition);
49765 var out = pathFn(ax._mainLinePosition);
49766 if(ax.mirror) out += pathFn(ax._mainMirrorPosition);
49767 return out;
49768 }
49769
49770 for(subplot in fullLayout._plots) {
49771 plotinfo = fullLayout._plots[subplot];
49772 xa = plotinfo.xaxis;
49773 ya = plotinfo.yaxis;
49774
49775 /*
49776 * x lines get longer where they meet y lines, to make a crisp corner.
49777 * The x lines get the padding (margin.pad) plus the y line width to
49778 * fill up the corner nicely. Free x lines are excluded - they always
49779 * span exactly the data area of the plot
49780 *
49781 * | XXXXX
49782 * | XXXXX
49783 * |
49784 * +------
49785 * x1
49786 * -----
49787 * x2
49788 */
49789 var xPath = 'M0,0';
49790 if(shouldShowLinesOrTicks(xa, subplot)) {
49791 leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList);
49792 xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0);
49793 rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList);
49794 xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0);
49795 xLinesYBottom = getLinePosition(xa, ya, 'bottom');
49796 xLinesYTop = getLinePosition(xa, ya, 'top');
49797
49798 // save axis line positions for extra ticks to reference
49799 // each subplot that gets ticks from "allticks" gets an entry:
49800 // [left or bottom, right or top]
49801 extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot);
49802 if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) {
49803 xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
49804 }
49805
49806 xPath = mainPath(xa, xLinePath, xLinePathFree);
49807 if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) {
49808 xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop);
49809 }
49810
49811 plotinfo.xlines
49812 .style('stroke-width', xa._lw + 'px')
49813 .call(Color.stroke, xa.showline ?
49814 xa.linecolor : 'rgba(0,0,0,0)');
49815 }
49816 plotinfo.xlines.attr('d', xPath);
49817
49818 /*
49819 * y lines that meet x axes get longer only by margin.pad, because
49820 * the x axes fill in the corner space. Free y axes, like free x axes,
49821 * always span exactly the data area of the plot
49822 *
49823 * | | XXXX
49824 * y2| y1| XXXX
49825 * | | XXXX
49826 * |
49827 * +-----
49828 */
49829 var yPath = 'M0,0';
49830 if(shouldShowLinesOrTicks(ya, subplot)) {
49831 connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList);
49832 yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0);
49833 connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList);
49834 yLinesYTop = ya._offset - (connectYTop ? pad : 0);
49835 yLinesXLeft = getLinePosition(ya, xa, 'left');
49836 yLinesXRight = getLinePosition(ya, xa, 'right');
49837
49838 extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot);
49839 if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) {
49840 ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
49841 }
49842
49843 yPath = mainPath(ya, yLinePath, yLinePathFree);
49844 if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) {
49845 yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight);
49846 }
49847
49848 plotinfo.ylines
49849 .style('stroke-width', ya._lw + 'px')
49850 .call(Color.stroke, ya.showline ?
49851 ya.linecolor : 'rgba(0,0,0,0)');
49852 }
49853 plotinfo.ylines.attr('d', yPath);
49854 }
49855
49856 Axes.makeClipPaths(gd);
49857
49858 return Plots.previousPromises(gd);
49859}
49860
49861function shouldShowLinesOrTicks(ax, subplot) {
49862 return (ax.ticks || ax.showline) &&
49863 (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks');
49864}
49865
49866/*
49867 * should we draw a line on counterAx at this side of ax?
49868 * It's assumed that counterAx is known to overlay the subplot we're working on
49869 * but it may not be its main axis.
49870 */
49871function shouldShowLineThisSide(ax, side, counterAx) {
49872 // does counterAx get a line at all?
49873 if(!counterAx.showline || !counterAx._lw) return false;
49874
49875 // are we drawing *all* lines for counterAx?
49876 if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true;
49877
49878 var anchorAx = counterAx._anchorAxis;
49879
49880 // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring
49881 if(!anchorAx) return false;
49882
49883 // in order to handle cases where the user forgot to anchor this axis correctly
49884 // (because its default anchor has the same domain on the relevant end)
49885 // check whether the relevant position is the same.
49886 var sideIndex = alignmentConstants.FROM_BL[side];
49887 if(counterAx.side === side) {
49888 return anchorAx.domain[sideIndex] === ax.domain[sideIndex];
49889 }
49890 return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex];
49891}
49892
49893/*
49894 * Is there another axis intersecting `side` end of `ax`?
49895 * First look at `counterAx` (the axis for this subplot),
49896 * then at all other potential counteraxes on or overlaying this subplot.
49897 * Take the line width from the first one that has a line.
49898 */
49899function findCounterAxisLineWidth(ax, side, counterAx, axList) {
49900 if(shouldShowLineThisSide(ax, side, counterAx)) {
49901 return counterAx._lw;
49902 }
49903 for(var i = 0; i < axList.length; i++) {
49904 var axi = axList[i];
49905 if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) {
49906 return axi._lw;
49907 }
49908 }
49909 return 0;
49910}
49911
49912exports.drawMainTitle = function(gd) {
49913 var fullLayout = gd._fullLayout;
49914
49915 var textAnchor = getMainTitleTextAnchor(fullLayout);
49916 var dy = getMainTitleDy(fullLayout);
49917
49918 Titles.draw(gd, 'gtitle', {
49919 propContainer: fullLayout,
49920 propName: 'title.text',
49921 placeholder: fullLayout._dfltTitle.plot,
49922 attributes: {
49923 x: getMainTitleX(fullLayout, textAnchor),
49924 y: getMainTitleY(fullLayout, dy),
49925 'text-anchor': textAnchor,
49926 dy: dy
49927 }
49928 });
49929};
49930
49931function getMainTitleX(fullLayout, textAnchor) {
49932 var title = fullLayout.title;
49933 var gs = fullLayout._size;
49934 var hPadShift = 0;
49935
49936 if(textAnchor === SVG_TEXT_ANCHOR_START) {
49937 hPadShift = title.pad.l;
49938 } else if(textAnchor === SVG_TEXT_ANCHOR_END) {
49939 hPadShift = -title.pad.r;
49940 }
49941
49942 switch(title.xref) {
49943 case 'paper':
49944 return gs.l + gs.w * title.x + hPadShift;
49945 case 'container':
49946 default:
49947 return fullLayout.width * title.x + hPadShift;
49948 }
49949}
49950
49951function getMainTitleY(fullLayout, dy) {
49952 var title = fullLayout.title;
49953 var gs = fullLayout._size;
49954 var vPadShift = 0;
49955
49956 if(dy === '0em' || !dy) {
49957 vPadShift = -title.pad.b;
49958 } else if(dy === alignmentConstants.CAP_SHIFT + 'em') {
49959 vPadShift = title.pad.t;
49960 }
49961
49962 if(title.y === 'auto') {
49963 return gs.t / 2;
49964 } else {
49965 switch(title.yref) {
49966 case 'paper':
49967 return gs.t + gs.h - gs.h * title.y + vPadShift;
49968 case 'container':
49969 default:
49970 return fullLayout.height - fullLayout.height * title.y + vPadShift;
49971 }
49972 }
49973}
49974
49975function getMainTitleTextAnchor(fullLayout) {
49976 var title = fullLayout.title;
49977
49978 var textAnchor = SVG_TEXT_ANCHOR_MIDDLE;
49979 if(Lib.isRightAnchor(title)) {
49980 textAnchor = SVG_TEXT_ANCHOR_END;
49981 } else if(Lib.isLeftAnchor(title)) {
49982 textAnchor = SVG_TEXT_ANCHOR_START;
49983 }
49984
49985 return textAnchor;
49986}
49987
49988function getMainTitleDy(fullLayout) {
49989 var title = fullLayout.title;
49990
49991 var dy = '0em';
49992 if(Lib.isTopAnchor(title)) {
49993 dy = alignmentConstants.CAP_SHIFT + 'em';
49994 } else if(Lib.isMiddleAnchor(title)) {
49995 dy = alignmentConstants.MID_SHIFT + 'em';
49996 }
49997
49998 return dy;
49999}
50000
50001exports.doTraceStyle = function(gd) {
50002 var calcdata = gd.calcdata;
50003 var editStyleCalls = [];
50004 var i;
50005
50006 for(i = 0; i < calcdata.length; i++) {
50007 var cd = calcdata[i];
50008 var cd0 = cd[0] || {};
50009 var trace = cd0.trace || {};
50010 var _module = trace._module || {};
50011
50012 // See if we need to do arraysToCalcdata
50013 // call it regardless of what change we made, in case
50014 // supplyDefaults brought in an array that was already
50015 // in gd.data but not in gd._fullData previously
50016 var arraysToCalcdata = _module.arraysToCalcdata;
50017 if(arraysToCalcdata) arraysToCalcdata(cd, trace);
50018
50019 var editStyle = _module.editStyle;
50020 if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0});
50021 }
50022
50023 if(editStyleCalls.length) {
50024 for(i = 0; i < editStyleCalls.length; i++) {
50025 var edit = editStyleCalls[i];
50026 edit.fn(gd, edit.cd0);
50027 }
50028 clearGlCanvases(gd);
50029 exports.redrawReglTraces(gd);
50030 }
50031
50032 Plots.style(gd);
50033 Registry.getComponentMethod('legend', 'draw')(gd);
50034
50035 return Plots.previousPromises(gd);
50036};
50037
50038exports.doColorBars = function(gd) {
50039 Registry.getComponentMethod('colorbar', 'draw')(gd);
50040 return Plots.previousPromises(gd);
50041};
50042
50043// force plot() to redo the layout and replot with the modified layout
50044exports.layoutReplot = function(gd) {
50045 var layout = gd.layout;
50046 gd.layout = undefined;
50047 return Registry.call('plot', gd, '', layout);
50048};
50049
50050exports.doLegend = function(gd) {
50051 Registry.getComponentMethod('legend', 'draw')(gd);
50052 return Plots.previousPromises(gd);
50053};
50054
50055exports.doTicksRelayout = function(gd) {
50056 Axes.draw(gd, 'redraw');
50057
50058 if(gd._fullLayout._hasOnlyLargeSploms) {
50059 Registry.subplotsRegistry.splom.updateGrid(gd);
50060 clearGlCanvases(gd);
50061 exports.redrawReglTraces(gd);
50062 }
50063
50064 exports.drawMainTitle(gd);
50065 return Plots.previousPromises(gd);
50066};
50067
50068exports.doModeBar = function(gd) {
50069 var fullLayout = gd._fullLayout;
50070
50071 ModeBar.manage(gd);
50072
50073 for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
50074 var updateFx = fullLayout._basePlotModules[i].updateFx;
50075 if(updateFx) updateFx(gd);
50076 }
50077
50078 return Plots.previousPromises(gd);
50079};
50080
50081exports.doCamera = function(gd) {
50082 var fullLayout = gd._fullLayout;
50083 var sceneIds = fullLayout._subplots.gl3d;
50084
50085 for(var i = 0; i < sceneIds.length; i++) {
50086 var sceneLayout = fullLayout[sceneIds[i]];
50087 var scene = sceneLayout._scene;
50088
50089 scene.setViewport(sceneLayout);
50090 }
50091};
50092
50093exports.drawData = function(gd) {
50094 var fullLayout = gd._fullLayout;
50095
50096 clearGlCanvases(gd);
50097
50098 // loop over the base plot modules present on graph
50099 var basePlotModules = fullLayout._basePlotModules;
50100 for(var i = 0; i < basePlotModules.length; i++) {
50101 basePlotModules[i].plot(gd);
50102 }
50103
50104 exports.redrawReglTraces(gd);
50105
50106 // styling separate from drawing
50107 Plots.style(gd);
50108
50109 // draw components that can be drawn on axes,
50110 // and that do not push the margins
50111 Registry.getComponentMethod('shapes', 'draw')(gd);
50112 Registry.getComponentMethod('annotations', 'draw')(gd);
50113 Registry.getComponentMethod('images', 'draw')(gd);
50114
50115 // Mark the first render as complete
50116 fullLayout._replotting = false;
50117
50118 return Plots.previousPromises(gd);
50119};
50120
50121// Draw (or redraw) all regl-based traces in one go,
50122// useful during drag and selection where buffers of targeted traces are updated,
50123// but all traces need to be redrawn following clearGlCanvases.
50124//
50125// Note that _module.plot for regl trace does NOT draw things
50126// on the canvas, they only update the buffers.
50127// Drawing is perform here.
50128//
50129// TODO try adding per-subplot option using gl.SCISSOR_TEST for
50130// non-overlaying, disjoint subplots.
50131//
50132// TODO try to include parcoords in here.
50133// https://github.com/plotly/plotly.js/issues/3069
50134exports.redrawReglTraces = function(gd) {
50135 var fullLayout = gd._fullLayout;
50136
50137 if(fullLayout._has('regl')) {
50138 var fullData = gd._fullData;
50139 var cartesianIds = [];
50140 var polarIds = [];
50141 var i, sp;
50142
50143 if(fullLayout._hasOnlyLargeSploms) {
50144 fullLayout._splomGrid.draw();
50145 }
50146
50147 // N.B.
50148 // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering
50149 // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces
50150 // of a given module are `visible !== true`
50151 for(i = 0; i < fullData.length; i++) {
50152 var trace = fullData[i];
50153
50154 if(trace.visible === true && trace._length !== 0) {
50155 if(trace.type === 'splom') {
50156 fullLayout._splomScenes[trace.uid].draw();
50157 } else if(trace.type === 'scattergl') {
50158 Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis);
50159 } else if(trace.type === 'scatterpolargl') {
50160 Lib.pushUnique(polarIds, trace.subplot);
50161 }
50162 }
50163 }
50164
50165 for(i = 0; i < cartesianIds.length; i++) {
50166 sp = fullLayout._plots[cartesianIds[i]];
50167 if(sp._scene) sp._scene.draw();
50168 }
50169
50170 for(i = 0; i < polarIds.length; i++) {
50171 sp = fullLayout[polarIds[i]]._subplot;
50172 if(sp._scene) sp._scene.draw();
50173 }
50174 }
50175};
50176
50177exports.doAutoRangeAndConstraints = function(gd) {
50178 var fullLayout = gd._fullLayout;
50179 var axList = Axes.list(gd, '', true);
50180 var matchGroups = fullLayout._axisMatchGroups || [];
50181 var axLookup = {};
50182 var ax;
50183 var axRng;
50184
50185 for(var i = 0; i < axList.length; i++) {
50186 ax = axList[i];
50187 cleanAxisConstraints(gd, ax);
50188 doAutoRange(gd, ax);
50189 axLookup[ax._id] = 1;
50190 }
50191
50192 enforceAxisConstraints(gd);
50193
50194 groupLoop:
50195 for(var j = 0; j < matchGroups.length; j++) {
50196 var group = matchGroups[j];
50197 var rng = null;
50198 var id;
50199
50200 for(id in group) {
50201 ax = Axes.getFromId(gd, id);
50202
50203 // skip over 'missing' axes which do not pass through doAutoRange
50204 if(!axLookup[ax._id]) continue;
50205 // if one axis has autorange false, we're done
50206 if(ax.autorange === false) continue groupLoop;
50207
50208 axRng = Lib.simpleMap(ax.range, ax.r2l);
50209 if(rng) {
50210 if(rng[0] < rng[1]) {
50211 rng[0] = Math.min(rng[0], axRng[0]);
50212 rng[1] = Math.max(rng[1], axRng[1]);
50213 } else {
50214 rng[0] = Math.max(rng[0], axRng[0]);
50215 rng[1] = Math.min(rng[1], axRng[1]);
50216 }
50217 } else {
50218 rng = axRng;
50219 }
50220 }
50221
50222 for(id in group) {
50223 ax = Axes.getFromId(gd, id);
50224 ax.range = Lib.simpleMap(rng, ax.l2r);
50225 ax._input.range = ax.range.slice();
50226 ax.setScale();
50227 }
50228 }
50229};
50230
50231// An initial paint must be completed before these components can be
50232// correctly sized and the whole plot re-margined. fullLayout._replotting must
50233// be set to false before these will work properly.
50234exports.finalDraw = function(gd) {
50235 // TODO: rangesliders really belong in marginPushers but they need to be
50236 // drawn after data - can we at least get the margin pushing part separated
50237 // out and done earlier?
50238 Registry.getComponentMethod('rangeslider', 'draw')(gd);
50239 // TODO: rangeselector only needs to be here (in addition to drawMarginPushers)
50240 // because the margins need to be fully determined before we can call
50241 // autorange and update axis ranges (which rangeselector needs to know which
50242 // button is active). Can we break out its automargin step from its draw step?
50243 Registry.getComponentMethod('rangeselector', 'draw')(gd);
50244};
50245
50246exports.drawMarginPushers = function(gd) {
50247 Registry.getComponentMethod('legend', 'draw')(gd);
50248 Registry.getComponentMethod('rangeselector', 'draw')(gd);
50249 Registry.getComponentMethod('sliders', 'draw')(gd);
50250 Registry.getComponentMethod('updatemenus', 'draw')(gd);
50251 Registry.getComponentMethod('colorbar', 'draw')(gd);
50252};
50253
50254},{"../components/color":52,"../components/drawing":74,"../components/modebar":112,"../components/titles":147,"../constants/alignment":154,"../lib":178,"../lib/clear_gl_canvases":167,"../plots/cartesian/autorange":221,"../plots/cartesian/axes":222,"../plots/cartesian/constraints":229,"../plots/plots":256,"../registry":269,"d3":16}],214:[function(_dereq_,module,exports){
50255/**
50256* Copyright 2012-2020, Plotly, Inc.
50257* All rights reserved.
50258*
50259* This source code is licensed under the MIT license found in the
50260* LICENSE file in the root directory of this source tree.
50261*/
50262
50263
50264'use strict';
50265
50266var Lib = _dereq_('../lib');
50267var isPlainObject = Lib.isPlainObject;
50268var PlotSchema = _dereq_('./plot_schema');
50269var Plots = _dereq_('../plots/plots');
50270var plotAttributes = _dereq_('../plots/attributes');
50271var Template = _dereq_('./plot_template');
50272var dfltConfig = _dereq_('./plot_config').dfltConfig;
50273
50274/**
50275 * Plotly.makeTemplate: create a template off an existing figure to reuse
50276 * style attributes on other figures.
50277 *
50278 * Note: separated from the rest of templates because otherwise we get circular
50279 * references due to PlotSchema.
50280 *
50281 * @param {object|DOM element|string} figure: The figure to base the template on
50282 * should contain a trace array `figure.data`
50283 * and a layout object `figure.layout`
50284 * @returns {object} template: the extracted template - can then be used as
50285 * `layout.template` in another figure.
50286 */
50287exports.makeTemplate = function(figure) {
50288 figure = Lib.isPlainObject(figure) ? figure : Lib.getGraphDiv(figure);
50289 figure = Lib.extendDeep({_context: dfltConfig}, {data: figure.data, layout: figure.layout});
50290 Plots.supplyDefaults(figure);
50291 var data = figure.data || [];
50292 var layout = figure.layout || {};
50293 // copy over a few items to help follow the schema
50294 layout._basePlotModules = figure._fullLayout._basePlotModules;
50295 layout._modules = figure._fullLayout._modules;
50296
50297 var template = {
50298 data: {},
50299 layout: {}
50300 };
50301
50302 /*
50303 * Note: we do NOT validate template values, we just take what's in the
50304 * user inputs data and layout, not the validated values in fullData and
50305 * fullLayout. Even if we were to validate here, there's no guarantee that
50306 * these values would still be valid when applied to a new figure, which
50307 * may contain different trace modes, different axes, etc. So it's
50308 * important that when applying a template we still validate the template
50309 * values, rather than just using them as defaults.
50310 */
50311
50312 data.forEach(function(trace) {
50313 // TODO: What if no style info is extracted for this trace. We may
50314 // not want an empty object as the null value.
50315 // TODO: allow transforms to contribute to templates?
50316 // as it stands they are ignored, which may be for the best...
50317
50318 var traceTemplate = {};
50319 walkStyleKeys(trace, traceTemplate, getTraceInfo.bind(null, trace));
50320
50321 var traceType = Lib.coerce(trace, {}, plotAttributes, 'type');
50322 var typeTemplates = template.data[traceType];
50323 if(!typeTemplates) typeTemplates = template.data[traceType] = [];
50324 typeTemplates.push(traceTemplate);
50325 });
50326
50327 walkStyleKeys(layout, template.layout, getLayoutInfo.bind(null, layout));
50328
50329 /*
50330 * Compose the new template with an existing one to the same effect
50331 *
50332 * NOTE: there's a possibility of slightly different behavior: if the plot
50333 * has an invalid value and the old template has a valid value for the same
50334 * attribute, the plot will use the old template value but this routine
50335 * will pull the invalid value (resulting in the original default).
50336 * In the general case it's not possible to solve this with a single value,
50337 * since valid options can be context-dependent. It could be solved with
50338 * a *list* of values, but that would be huge complexity for little gain.
50339 */
50340 delete template.layout.template;
50341 var oldTemplate = layout.template;
50342 if(isPlainObject(oldTemplate)) {
50343 var oldLayoutTemplate = oldTemplate.layout;
50344
50345 var i, traceType, oldTypeTemplates, oldTypeLen, typeTemplates, typeLen;
50346
50347 if(isPlainObject(oldLayoutTemplate)) {
50348 mergeTemplates(oldLayoutTemplate, template.layout);
50349 }
50350 var oldDataTemplate = oldTemplate.data;
50351 if(isPlainObject(oldDataTemplate)) {
50352 for(traceType in template.data) {
50353 oldTypeTemplates = oldDataTemplate[traceType];
50354 if(Array.isArray(oldTypeTemplates)) {
50355 typeTemplates = template.data[traceType];
50356 typeLen = typeTemplates.length;
50357 oldTypeLen = oldTypeTemplates.length;
50358 for(i = 0; i < typeLen; i++) {
50359 mergeTemplates(oldTypeTemplates[i % oldTypeLen], typeTemplates[i]);
50360 }
50361 for(i = typeLen; i < oldTypeLen; i++) {
50362 typeTemplates.push(Lib.extendDeep({}, oldTypeTemplates[i]));
50363 }
50364 }
50365 }
50366 for(traceType in oldDataTemplate) {
50367 if(!(traceType in template.data)) {
50368 template.data[traceType] = Lib.extendDeep([], oldDataTemplate[traceType]);
50369 }
50370 }
50371 }
50372 }
50373
50374 return template;
50375};
50376
50377function mergeTemplates(oldTemplate, newTemplate) {
50378 // we don't care about speed here, just make sure we have a totally
50379 // distinct object from the previous template
50380 oldTemplate = Lib.extendDeep({}, oldTemplate);
50381
50382 // sort keys so we always get annotationdefaults before annotations etc
50383 // so arrayTemplater will work right
50384 var oldKeys = Object.keys(oldTemplate).sort();
50385 var i, j;
50386
50387 function mergeOne(oldVal, newVal, key) {
50388 if(isPlainObject(newVal) && isPlainObject(oldVal)) {
50389 mergeTemplates(oldVal, newVal);
50390 } else if(Array.isArray(newVal) && Array.isArray(oldVal)) {
50391 // Note: omitted `inclusionAttr` from arrayTemplater here,
50392 // it's irrelevant as we only want the resulting `_template`.
50393 var templater = Template.arrayTemplater({_template: oldTemplate}, key);
50394 for(j = 0; j < newVal.length; j++) {
50395 var item = newVal[j];
50396 var oldItem = templater.newItem(item)._template;
50397 if(oldItem) mergeTemplates(oldItem, item);
50398 }
50399 var defaultItems = templater.defaultItems();
50400 for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template);
50401
50402 // templateitemname only applies to receiving plots
50403 for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname;
50404 }
50405 }
50406
50407 for(i = 0; i < oldKeys.length; i++) {
50408 var key = oldKeys[i];
50409 var oldVal = oldTemplate[key];
50410 if(key in newTemplate) {
50411 mergeOne(oldVal, newTemplate[key], key);
50412 } else newTemplate[key] = oldVal;
50413
50414 // if this is a base key from the old template (eg xaxis), look for
50415 // extended keys (eg xaxis2) in the new template to merge into
50416 if(getBaseKey(key) === key) {
50417 for(var key2 in newTemplate) {
50418 var baseKey2 = getBaseKey(key2);
50419 if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) {
50420 mergeOne(oldVal, newTemplate[key2], key);
50421 }
50422 }
50423 }
50424 }
50425}
50426
50427function getBaseKey(key) {
50428 return key.replace(/[0-9]+$/, '');
50429}
50430
50431function walkStyleKeys(parent, templateOut, getAttributeInfo, path, basePath) {
50432 var pathAttr = basePath && getAttributeInfo(basePath);
50433 for(var key in parent) {
50434 var child = parent[key];
50435 var nextPath = getNextPath(parent, key, path);
50436 var nextBasePath = getNextPath(parent, key, basePath);
50437 var attr = getAttributeInfo(nextBasePath);
50438 if(!attr) {
50439 var baseKey = getBaseKey(key);
50440 if(baseKey !== key) {
50441 nextBasePath = getNextPath(parent, baseKey, basePath);
50442 attr = getAttributeInfo(nextBasePath);
50443 }
50444 }
50445
50446 // we'll get an attr if path starts with a valid part, then has an
50447 // invalid ending. Make sure we got all the way to the end.
50448 if(pathAttr && (pathAttr === attr)) continue;
50449
50450 if(!attr || attr._noTemplating ||
50451 attr.valType === 'data_array' ||
50452 (attr.arrayOk && Array.isArray(child))
50453 ) {
50454 continue;
50455 }
50456
50457 if(!attr.valType && isPlainObject(child)) {
50458 walkStyleKeys(child, templateOut, getAttributeInfo, nextPath, nextBasePath);
50459 } else if(attr._isLinkedToArray && Array.isArray(child)) {
50460 var dfltDone = false;
50461 var namedIndex = 0;
50462 var usedNames = {};
50463 for(var i = 0; i < child.length; i++) {
50464 var item = child[i];
50465 if(isPlainObject(item)) {
50466 var name = item.name;
50467 if(name) {
50468 if(!usedNames[name]) {
50469 // named array items: allow all attributes except data arrays
50470 walkStyleKeys(item, templateOut, getAttributeInfo,
50471 getNextPath(child, namedIndex, nextPath),
50472 getNextPath(child, namedIndex, nextBasePath));
50473 namedIndex++;
50474 usedNames[name] = 1;
50475 }
50476 } else if(!dfltDone) {
50477 var dfltKey = Template.arrayDefaultKey(key);
50478 var dfltPath = getNextPath(parent, dfltKey, path);
50479
50480 // getAttributeInfo will fail if we try to use dfltKey directly.
50481 // Instead put this item into the next array element, then
50482 // pull it out and move it to dfltKey.
50483 var pathInArray = getNextPath(child, namedIndex, nextPath);
50484 walkStyleKeys(item, templateOut, getAttributeInfo, pathInArray,
50485 getNextPath(child, namedIndex, nextBasePath));
50486 var itemPropInArray = Lib.nestedProperty(templateOut, pathInArray);
50487 var dfltProp = Lib.nestedProperty(templateOut, dfltPath);
50488 dfltProp.set(itemPropInArray.get());
50489 itemPropInArray.set(null);
50490
50491 dfltDone = true;
50492 }
50493 }
50494 }
50495 } else {
50496 var templateProp = Lib.nestedProperty(templateOut, nextPath);
50497 templateProp.set(child);
50498 }
50499 }
50500}
50501
50502function getLayoutInfo(layout, path) {
50503 return PlotSchema.getLayoutValObject(
50504 layout, Lib.nestedProperty({}, path).parts
50505 );
50506}
50507
50508function getTraceInfo(trace, path) {
50509 return PlotSchema.getTraceValObject(
50510 trace, Lib.nestedProperty({}, path).parts
50511 );
50512}
50513
50514function getNextPath(parent, key, path) {
50515 var nextPath;
50516 if(!path) nextPath = key;
50517 else if(Array.isArray(parent)) nextPath = path + '[' + key + ']';
50518 else nextPath = path + '.' + key;
50519
50520 return nextPath;
50521}
50522
50523/**
50524 * validateTemplate: Test for consistency between the given figure and
50525 * a template, either already included in the figure or given separately.
50526 * Note that not every issue we identify here is necessarily a problem,
50527 * it depends on what you're using the template for.
50528 *
50529 * @param {object|DOM element} figure: the plot, with {data, layout} members,
50530 * to test the template against
50531 * @param {Optional(object)} template: the template, with its own {data, layout},
50532 * to test. If omitted, we will look for a template already attached as the
50533 * plot's `layout.template` attribute.
50534 *
50535 * @returns {array} array of error objects each containing:
50536 * - {string} code
50537 * error code ('missing', 'unused', 'reused', 'noLayout', 'noData')
50538 * - {string} msg
50539 * a full readable description of the issue.
50540 */
50541exports.validateTemplate = function(figureIn, template) {
50542 var figure = Lib.extendDeep({}, {
50543 _context: dfltConfig,
50544 data: figureIn.data,
50545 layout: figureIn.layout
50546 });
50547 var layout = figure.layout || {};
50548 if(!isPlainObject(template)) template = layout.template || {};
50549 var layoutTemplate = template.layout;
50550 var dataTemplate = template.data;
50551 var errorList = [];
50552
50553 figure.layout = layout;
50554 figure.layout.template = template;
50555 Plots.supplyDefaults(figure);
50556
50557 var fullLayout = figure._fullLayout;
50558 var fullData = figure._fullData;
50559
50560 var layoutPaths = {};
50561 function crawlLayoutForContainers(obj, paths) {
50562 for(var key in obj) {
50563 if(key.charAt(0) !== '_' && isPlainObject(obj[key])) {
50564 var baseKey = getBaseKey(key);
50565 var nextPaths = [];
50566 var i;
50567 for(i = 0; i < paths.length; i++) {
50568 nextPaths.push(getNextPath(obj, key, paths[i]));
50569 if(baseKey !== key) nextPaths.push(getNextPath(obj, baseKey, paths[i]));
50570 }
50571 for(i = 0; i < nextPaths.length; i++) {
50572 layoutPaths[nextPaths[i]] = 1;
50573 }
50574 crawlLayoutForContainers(obj[key], nextPaths);
50575 }
50576 }
50577 }
50578
50579 function crawlLayoutTemplateForContainers(obj, path) {
50580 for(var key in obj) {
50581 if(key.indexOf('defaults') === -1 && isPlainObject(obj[key])) {
50582 var nextPath = getNextPath(obj, key, path);
50583 if(layoutPaths[nextPath]) {
50584 crawlLayoutTemplateForContainers(obj[key], nextPath);
50585 } else {
50586 errorList.push({code: 'unused', path: nextPath});
50587 }
50588 }
50589 }
50590 }
50591
50592 if(!isPlainObject(layoutTemplate)) {
50593 errorList.push({code: 'layout'});
50594 } else {
50595 crawlLayoutForContainers(fullLayout, ['layout']);
50596 crawlLayoutTemplateForContainers(layoutTemplate, 'layout');
50597 }
50598
50599 if(!isPlainObject(dataTemplate)) {
50600 errorList.push({code: 'data'});
50601 } else {
50602 var typeCount = {};
50603 var traceType;
50604 for(var i = 0; i < fullData.length; i++) {
50605 var fullTrace = fullData[i];
50606 traceType = fullTrace.type;
50607 typeCount[traceType] = (typeCount[traceType] || 0) + 1;
50608 if(!fullTrace._fullInput._template) {
50609 // this takes care of the case of traceType in the data but not
50610 // the template
50611 errorList.push({
50612 code: 'missing',
50613 index: fullTrace._fullInput.index,
50614 traceType: traceType
50615 });
50616 }
50617 }
50618 for(traceType in dataTemplate) {
50619 var templateCount = dataTemplate[traceType].length;
50620 var dataCount = typeCount[traceType] || 0;
50621 if(templateCount > dataCount) {
50622 errorList.push({
50623 code: 'unused',
50624 traceType: traceType,
50625 templateCount: templateCount,
50626 dataCount: dataCount
50627 });
50628 } else if(dataCount > templateCount) {
50629 errorList.push({
50630 code: 'reused',
50631 traceType: traceType,
50632 templateCount: templateCount,
50633 dataCount: dataCount
50634 });
50635 }
50636 }
50637 }
50638
50639 // _template: false is when someone tried to modify an array item
50640 // but there was no template with matching name
50641 function crawlForMissingTemplates(obj, path) {
50642 for(var key in obj) {
50643 if(key.charAt(0) === '_') continue;
50644 var val = obj[key];
50645 var nextPath = getNextPath(obj, key, path);
50646 if(isPlainObject(val)) {
50647 if(Array.isArray(obj) && val._template === false && val.templateitemname) {
50648 errorList.push({
50649 code: 'missing',
50650 path: nextPath,
50651 templateitemname: val.templateitemname
50652 });
50653 }
50654 crawlForMissingTemplates(val, nextPath);
50655 } else if(Array.isArray(val) && hasPlainObject(val)) {
50656 crawlForMissingTemplates(val, nextPath);
50657 }
50658 }
50659 }
50660 crawlForMissingTemplates({data: fullData, layout: fullLayout}, '');
50661
50662 if(errorList.length) return errorList.map(format);
50663};
50664
50665function hasPlainObject(arr) {
50666 for(var i = 0; i < arr.length; i++) {
50667 if(isPlainObject(arr[i])) return true;
50668 }
50669}
50670
50671function format(opts) {
50672 var msg;
50673 switch(opts.code) {
50674 case 'data':
50675 msg = 'The template has no key data.';
50676 break;
50677 case 'layout':
50678 msg = 'The template has no key layout.';
50679 break;
50680 case 'missing':
50681 if(opts.path) {
50682 msg = 'There are no templates for item ' + opts.path +
50683 ' with name ' + opts.templateitemname;
50684 } else {
50685 msg = 'There are no templates for trace ' + opts.index +
50686 ', of type ' + opts.traceType + '.';
50687 }
50688 break;
50689 case 'unused':
50690 if(opts.path) {
50691 msg = 'The template item at ' + opts.path +
50692 ' was not used in constructing the plot.';
50693 } else if(opts.dataCount) {
50694 msg = 'Some of the templates of type ' + opts.traceType +
50695 ' were not used. The template has ' + opts.templateCount +
50696 ' traces, the data only has ' + opts.dataCount +
50697 ' of this type.';
50698 } else {
50699 msg = 'The template has ' + opts.templateCount +
50700 ' traces of type ' + opts.traceType +
50701 ' but there are none in the data.';
50702 }
50703 break;
50704 case 'reused':
50705 msg = 'Some of the templates of type ' + opts.traceType +
50706 ' were used more than once. The template has ' +
50707 opts.templateCount + ' traces, the data has ' +
50708 opts.dataCount + ' of this type.';
50709 break;
50710 }
50711 opts.msg = msg;
50712
50713 return opts;
50714}
50715
50716},{"../lib":178,"../plots/attributes":219,"../plots/plots":256,"./plot_config":210,"./plot_schema":211,"./plot_template":212}],215:[function(_dereq_,module,exports){
50717/**
50718* Copyright 2012-2020, Plotly, Inc.
50719* All rights reserved.
50720*
50721* This source code is licensed under the MIT license found in the
50722* LICENSE file in the root directory of this source tree.
50723*/
50724
50725'use strict';
50726
50727var isNumeric = _dereq_('fast-isnumeric');
50728
50729var plotApi = _dereq_('./plot_api');
50730var plots = _dereq_('../plots/plots');
50731var Lib = _dereq_('../lib');
50732
50733var helpers = _dereq_('../snapshot/helpers');
50734var toSVG = _dereq_('../snapshot/tosvg');
50735var svgToImg = _dereq_('../snapshot/svgtoimg');
50736var version = _dereq_('../version').version;
50737
50738var attrs = {
50739 format: {
50740 valType: 'enumerated',
50741 values: ['png', 'jpeg', 'webp', 'svg', 'full-json'],
50742 dflt: 'png',
50743
50744 },
50745 width: {
50746 valType: 'number',
50747 min: 1,
50748
50749 },
50750 height: {
50751 valType: 'number',
50752 min: 1,
50753
50754 },
50755 scale: {
50756 valType: 'number',
50757 min: 0,
50758 dflt: 1,
50759
50760 },
50761 setBackground: {
50762 valType: 'any',
50763 dflt: false,
50764
50765 },
50766 imageDataOnly: {
50767 valType: 'boolean',
50768 dflt: false,
50769
50770 }
50771};
50772
50773/** Plotly.toImage
50774 *
50775 * @param {object | string | HTML div} gd
50776 * can either be a data/layout/config object
50777 * or an existing graph <div>
50778 * or an id to an existing graph <div>
50779 * @param {object} opts (see above)
50780 * @return {promise}
50781 */
50782function toImage(gd, opts) {
50783 opts = opts || {};
50784
50785 var data;
50786 var layout;
50787 var config;
50788 var fullLayout;
50789
50790 if(Lib.isPlainObject(gd)) {
50791 data = gd.data || [];
50792 layout = gd.layout || {};
50793 config = gd.config || {};
50794 fullLayout = {};
50795 } else {
50796 gd = Lib.getGraphDiv(gd);
50797 data = Lib.extendDeep([], gd.data);
50798 layout = Lib.extendDeep({}, gd.layout);
50799 config = gd._context;
50800 fullLayout = gd._fullLayout || {};
50801 }
50802
50803 function isImpliedOrValid(attr) {
50804 return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
50805 }
50806
50807 if((!isImpliedOrValid('width') && opts.width !== null) ||
50808 (!isImpliedOrValid('height') && opts.height !== null)) {
50809 throw new Error('Height and width should be pixel values.');
50810 }
50811
50812 if(!isImpliedOrValid('format')) {
50813 throw new Error('Image format is not jpeg, png, svg or webp.');
50814 }
50815
50816 var fullOpts = {};
50817
50818 function coerce(attr, dflt) {
50819 return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
50820 }
50821
50822 var format = coerce('format');
50823 var width = coerce('width');
50824 var height = coerce('height');
50825 var scale = coerce('scale');
50826 var setBackground = coerce('setBackground');
50827 var imageDataOnly = coerce('imageDataOnly');
50828
50829 // put the cloned div somewhere off screen before attaching to DOM
50830 var clonedGd = document.createElement('div');
50831 clonedGd.style.position = 'absolute';
50832 clonedGd.style.left = '-5000px';
50833 document.body.appendChild(clonedGd);
50834
50835 // extend layout with image options
50836 var layoutImage = Lib.extendFlat({}, layout);
50837 if(width) {
50838 layoutImage.width = width;
50839 } else if(opts.width === null && isNumeric(fullLayout.width)) {
50840 layoutImage.width = fullLayout.width;
50841 }
50842 if(height) {
50843 layoutImage.height = height;
50844 } else if(opts.height === null && isNumeric(fullLayout.height)) {
50845 layoutImage.height = fullLayout.height;
50846 }
50847
50848 // extend config for static plot
50849 var configImage = Lib.extendFlat({}, config, {
50850 _exportedPlot: true,
50851 staticPlot: true,
50852 setBackground: setBackground
50853 });
50854
50855 var redrawFunc = helpers.getRedrawFunc(clonedGd);
50856
50857 function wait() {
50858 return new Promise(function(resolve) {
50859 setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
50860 });
50861 }
50862
50863 function convert() {
50864 return new Promise(function(resolve, reject) {
50865 var svg = toSVG(clonedGd, format, scale);
50866 var width = clonedGd._fullLayout.width;
50867 var height = clonedGd._fullLayout.height;
50868
50869 function cleanup() {
50870 plotApi.purge(clonedGd);
50871 document.body.removeChild(clonedGd);
50872 }
50873
50874 if(format === 'full-json') {
50875 var json = plots.graphJson(clonedGd, false, 'keepdata', 'object', true, true);
50876 json.version = version;
50877 json = JSON.stringify(json);
50878 cleanup();
50879 if(imageDataOnly) {
50880 return resolve(json);
50881 } else {
50882 return resolve(helpers.encodeJSON(json));
50883 }
50884 }
50885
50886 cleanup();
50887
50888 if(format === 'svg') {
50889 if(imageDataOnly) {
50890 return resolve(svg);
50891 } else {
50892 return resolve(helpers.encodeSVG(svg));
50893 }
50894 }
50895
50896 var canvas = document.createElement('canvas');
50897 canvas.id = Lib.randstr();
50898
50899 svgToImg({
50900 format: format,
50901 width: width,
50902 height: height,
50903 scale: scale,
50904 canvas: canvas,
50905 svg: svg,
50906 // ask svgToImg to return a Promise
50907 // rather than EventEmitter
50908 // leave EventEmitter for backward
50909 // compatibility
50910 promise: true
50911 })
50912 .then(resolve)
50913 .catch(reject);
50914 });
50915 }
50916
50917 function urlToImageData(url) {
50918 if(imageDataOnly) {
50919 return url.replace(helpers.IMAGE_URL_PREFIX, '');
50920 } else {
50921 return url;
50922 }
50923 }
50924
50925 return new Promise(function(resolve, reject) {
50926 plotApi.plot(clonedGd, data, layoutImage, configImage)
50927 .then(redrawFunc)
50928 .then(wait)
50929 .then(convert)
50930 .then(function(url) { resolve(urlToImageData(url)); })
50931 .catch(function(err) { reject(err); });
50932 });
50933}
50934
50935module.exports = toImage;
50936
50937},{"../lib":178,"../plots/plots":256,"../snapshot/helpers":273,"../snapshot/svgtoimg":275,"../snapshot/tosvg":277,"../version":435,"./plot_api":209,"fast-isnumeric":18}],216:[function(_dereq_,module,exports){
50938/**
50939* Copyright 2012-2020, Plotly, Inc.
50940* All rights reserved.
50941*
50942* This source code is licensed under the MIT license found in the
50943* LICENSE file in the root directory of this source tree.
50944*/
50945
50946'use strict';
50947
50948var Lib = _dereq_('../lib');
50949var Plots = _dereq_('../plots/plots');
50950var PlotSchema = _dereq_('./plot_schema');
50951var dfltConfig = _dereq_('./plot_config').dfltConfig;
50952
50953var isPlainObject = Lib.isPlainObject;
50954var isArray = Array.isArray;
50955var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
50956
50957/**
50958 * Validate a data array and layout object.
50959 *
50960 * @param {array} data
50961 * @param {object} layout
50962 *
50963 * @return {array} array of error objects each containing:
50964 * - {string} code
50965 * error code ('object', 'array', 'schema', 'unused', 'invisible' or 'value')
50966 * - {string} container
50967 * container where the error occurs ('data' or 'layout')
50968 * - {number} trace
50969 * trace index of the 'data' container where the error occurs
50970 * - {array} path
50971 * nested path to the key that causes the error
50972 * - {string} astr
50973 * attribute string variant of 'path' compatible with Plotly.restyle and
50974 * Plotly.relayout.
50975 * - {string} msg
50976 * error message (shown in console in logger config argument is enable)
50977 */
50978module.exports = function validate(data, layout) {
50979 var schema = PlotSchema.get();
50980 var errorList = [];
50981 var gd = {_context: Lib.extendFlat({}, dfltConfig)};
50982
50983 var dataIn, layoutIn;
50984
50985 if(isArray(data)) {
50986 gd.data = Lib.extendDeep([], data);
50987 dataIn = data;
50988 } else {
50989 gd.data = [];
50990 dataIn = [];
50991 errorList.push(format('array', 'data'));
50992 }
50993
50994 if(isPlainObject(layout)) {
50995 gd.layout = Lib.extendDeep({}, layout);
50996 layoutIn = layout;
50997 } else {
50998 gd.layout = {};
50999 layoutIn = {};
51000 if(arguments.length > 1) {
51001 errorList.push(format('object', 'layout'));
51002 }
51003 }
51004
51005 // N.B. dataIn and layoutIn are in general not the same as
51006 // gd.data and gd.layout after supplyDefaults as some attributes
51007 // in gd.data and gd.layout (still) get mutated during this step.
51008
51009 Plots.supplyDefaults(gd);
51010
51011 var dataOut = gd._fullData;
51012 var len = dataIn.length;
51013
51014 for(var i = 0; i < len; i++) {
51015 var traceIn = dataIn[i];
51016 var base = ['data', i];
51017
51018 if(!isPlainObject(traceIn)) {
51019 errorList.push(format('object', base));
51020 continue;
51021 }
51022
51023 var traceOut = dataOut[i];
51024 var traceType = traceOut.type;
51025 var traceSchema = schema.traces[traceType].attributes;
51026
51027 // PlotSchema does something fancy with trace 'type', reset it here
51028 // to make the trace schema compatible with Lib.validate.
51029 traceSchema.type = {
51030 valType: 'enumerated',
51031 values: [traceType]
51032 };
51033
51034 if(traceOut.visible === false && traceIn.visible !== false) {
51035 errorList.push(format('invisible', base));
51036 }
51037
51038 crawl(traceIn, traceOut, traceSchema, errorList, base);
51039
51040 var transformsIn = traceIn.transforms;
51041 var transformsOut = traceOut.transforms;
51042
51043 if(transformsIn) {
51044 if(!isArray(transformsIn)) {
51045 errorList.push(format('array', base, ['transforms']));
51046 }
51047
51048 base.push('transforms');
51049
51050 for(var j = 0; j < transformsIn.length; j++) {
51051 var path = ['transforms', j];
51052 var transformType = transformsIn[j].type;
51053
51054 if(!isPlainObject(transformsIn[j])) {
51055 errorList.push(format('object', base, path));
51056 continue;
51057 }
51058
51059 var transformSchema = schema.transforms[transformType] ?
51060 schema.transforms[transformType].attributes :
51061 {};
51062
51063 // add 'type' to transform schema to validate the transform type
51064 transformSchema.type = {
51065 valType: 'enumerated',
51066 values: Object.keys(schema.transforms)
51067 };
51068
51069 crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path);
51070 }
51071 }
51072 }
51073
51074 var layoutOut = gd._fullLayout;
51075 var layoutSchema = fillLayoutSchema(schema, dataOut);
51076
51077 crawl(layoutIn, layoutOut, layoutSchema, errorList, 'layout');
51078
51079 // return undefined if no validation errors were found
51080 return (errorList.length === 0) ? void(0) : errorList;
51081};
51082
51083function crawl(objIn, objOut, schema, list, base, path) {
51084 path = path || [];
51085
51086 var keys = Object.keys(objIn);
51087
51088 for(var i = 0; i < keys.length; i++) {
51089 var k = keys[i];
51090
51091 // transforms are handled separately
51092 if(k === 'transforms') continue;
51093
51094 var p = path.slice();
51095 p.push(k);
51096
51097 var valIn = objIn[k];
51098 var valOut = objOut[k];
51099
51100 var nestedSchema = getNestedSchema(schema, k);
51101 var nestedValType = (nestedSchema || {}).valType;
51102 var isInfoArray = nestedValType === 'info_array';
51103 var isColorscale = nestedValType === 'colorscale';
51104 var items = (nestedSchema || {}).items;
51105
51106 if(!isInSchema(schema, k)) {
51107 list.push(format('schema', base, p));
51108 } else if(isPlainObject(valIn) && isPlainObject(valOut) && nestedValType !== 'any') {
51109 crawl(valIn, valOut, nestedSchema, list, base, p);
51110 } else if(isInfoArray && isArray(valIn)) {
51111 if(valIn.length > valOut.length) {
51112 list.push(format('unused', base, p.concat(valOut.length)));
51113 }
51114 var len = valOut.length;
51115 var arrayItems = Array.isArray(items);
51116 if(arrayItems) len = Math.min(len, items.length);
51117 var m, n, item, valInPart, valOutPart;
51118 if(nestedSchema.dimensions === 2) {
51119 for(n = 0; n < len; n++) {
51120 if(isArray(valIn[n])) {
51121 if(valIn[n].length > valOut[n].length) {
51122 list.push(format('unused', base, p.concat(n, valOut[n].length)));
51123 }
51124 var len2 = valOut[n].length;
51125 for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) {
51126 item = arrayItems ? items[n][m] : items;
51127 valInPart = valIn[n][m];
51128 valOutPart = valOut[n][m];
51129 if(!Lib.validate(valInPart, item)) {
51130 list.push(format('value', base, p.concat(n, m), valInPart));
51131 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
51132 list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart));
51133 }
51134 }
51135 } else {
51136 list.push(format('array', base, p.concat(n), valIn[n]));
51137 }
51138 }
51139 } else {
51140 for(n = 0; n < len; n++) {
51141 item = arrayItems ? items[n] : items;
51142 valInPart = valIn[n];
51143 valOutPart = valOut[n];
51144 if(!Lib.validate(valInPart, item)) {
51145 list.push(format('value', base, p.concat(n), valInPart));
51146 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
51147 list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart));
51148 }
51149 }
51150 }
51151 } else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
51152 var _nestedSchema = items[Object.keys(items)[0]];
51153 var indexList = [];
51154
51155 var j, _p;
51156
51157 // loop over valOut items while keeping track of their
51158 // corresponding input container index (given by _index)
51159 for(j = 0; j < valOut.length; j++) {
51160 var _index = valOut[j]._index || j;
51161
51162 _p = p.slice();
51163 _p.push(_index);
51164
51165 if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) {
51166 indexList.push(_index);
51167 var valInj = valIn[_index];
51168 var valOutj = valOut[j];
51169 if(isPlainObject(valInj) && valInj.visible !== false && valOutj.visible === false) {
51170 list.push(format('invisible', base, _p));
51171 } else crawl(valInj, valOutj, _nestedSchema, list, base, _p);
51172 }
51173 }
51174
51175 // loop over valIn to determine where it went wrong for some items
51176 for(j = 0; j < valIn.length; j++) {
51177 _p = p.slice();
51178 _p.push(j);
51179
51180 if(!isPlainObject(valIn[j])) {
51181 list.push(format('object', base, _p, valIn[j]));
51182 } else if(indexList.indexOf(j) === -1) {
51183 list.push(format('unused', base, _p));
51184 }
51185 }
51186 } else if(!isPlainObject(valIn) && isPlainObject(valOut)) {
51187 list.push(format('object', base, p, valIn));
51188 } else if(!isArrayOrTypedArray(valIn) && isArrayOrTypedArray(valOut) && !isInfoArray && !isColorscale) {
51189 list.push(format('array', base, p, valIn));
51190 } else if(!(k in objOut)) {
51191 list.push(format('unused', base, p, valIn));
51192 } else if(!Lib.validate(valIn, nestedSchema)) {
51193 list.push(format('value', base, p, valIn));
51194 } else if(nestedSchema.valType === 'enumerated' &&
51195 ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut)
51196 ) {
51197 list.push(format('dynamic', base, p, valIn, valOut));
51198 }
51199 }
51200
51201 return list;
51202}
51203
51204// the 'full' layout schema depends on the traces types presents
51205function fillLayoutSchema(schema, dataOut) {
51206 var layoutSchema = schema.layout.layoutAttributes;
51207
51208 for(var i = 0; i < dataOut.length; i++) {
51209 var traceOut = dataOut[i];
51210 var traceSchema = schema.traces[traceOut.type];
51211 var traceLayoutAttr = traceSchema.layoutAttributes;
51212
51213 if(traceLayoutAttr) {
51214 if(traceOut.subplot) {
51215 Lib.extendFlat(layoutSchema[traceSchema.attributes.subplot.dflt], traceLayoutAttr);
51216 } else {
51217 Lib.extendFlat(layoutSchema, traceLayoutAttr);
51218 }
51219 }
51220 }
51221
51222 return layoutSchema;
51223}
51224
51225// validation error codes
51226var code2msgFunc = {
51227 object: function(base, astr) {
51228 var prefix;
51229
51230 if(base === 'layout' && astr === '') prefix = 'The layout argument';
51231 else if(base[0] === 'data' && astr === '') {
51232 prefix = 'Trace ' + base[1] + ' in the data argument';
51233 } else prefix = inBase(base) + 'key ' + astr;
51234
51235 return prefix + ' must be linked to an object container';
51236 },
51237 array: function(base, astr) {
51238 var prefix;
51239
51240 if(base === 'data') prefix = 'The data argument';
51241 else prefix = inBase(base) + 'key ' + astr;
51242
51243 return prefix + ' must be linked to an array container';
51244 },
51245 schema: function(base, astr) {
51246 return inBase(base) + 'key ' + astr + ' is not part of the schema';
51247 },
51248 unused: function(base, astr, valIn) {
51249 var target = isPlainObject(valIn) ? 'container' : 'key';
51250
51251 return inBase(base) + target + ' ' + astr + ' did not get coerced';
51252 },
51253 dynamic: function(base, astr, valIn, valOut) {
51254 return [
51255 inBase(base) + 'key',
51256 astr,
51257 '(set to \'' + valIn + '\')',
51258 'got reset to',
51259 '\'' + valOut + '\'',
51260 'during defaults.'
51261 ].join(' ');
51262 },
51263 invisible: function(base, astr) {
51264 return (
51265 astr ? (inBase(base) + 'item ' + astr) : ('Trace ' + base[1])
51266 ) + ' got defaulted to be not visible';
51267 },
51268 value: function(base, astr, valIn) {
51269 return [
51270 inBase(base) + 'key ' + astr,
51271 'is set to an invalid value (' + valIn + ')'
51272 ].join(' ');
51273 }
51274};
51275
51276function inBase(base) {
51277 if(isArray(base)) return 'In data trace ' + base[1] + ', ';
51278
51279 return 'In ' + base + ', ';
51280}
51281
51282function format(code, base, path, valIn, valOut) {
51283 path = path || '';
51284
51285 var container, trace;
51286
51287 // container is either 'data' or 'layout
51288 // trace is the trace index if 'data', null otherwise
51289
51290 if(isArray(base)) {
51291 container = base[0];
51292 trace = base[1];
51293 } else {
51294 container = base;
51295 trace = null;
51296 }
51297
51298 var astr = convertPathToAttributeString(path);
51299 var msg = code2msgFunc[code](base, astr, valIn, valOut);
51300
51301 // log to console if logger config option is enabled
51302 Lib.log(msg);
51303
51304 return {
51305 code: code,
51306 container: container,
51307 trace: trace,
51308 path: path,
51309 astr: astr,
51310 msg: msg
51311 };
51312}
51313
51314function isInSchema(schema, key) {
51315 var parts = splitKey(key);
51316 var keyMinusId = parts.keyMinusId;
51317 var id = parts.id;
51318
51319 if((keyMinusId in schema) && schema[keyMinusId]._isSubplotObj && id) {
51320 return true;
51321 }
51322
51323 return (key in schema);
51324}
51325
51326function getNestedSchema(schema, key) {
51327 if(key in schema) return schema[key];
51328
51329 var parts = splitKey(key);
51330
51331 return schema[parts.keyMinusId];
51332}
51333
51334var idRegex = Lib.counterRegex('([a-z]+)');
51335
51336function splitKey(key) {
51337 var idMatch = key.match(idRegex);
51338
51339 return {
51340 keyMinusId: idMatch && idMatch[1],
51341 id: idMatch && idMatch[2]
51342 };
51343}
51344
51345function convertPathToAttributeString(path) {
51346 if(!isArray(path)) return String(path);
51347
51348 var astr = '';
51349
51350 for(var i = 0; i < path.length; i++) {
51351 var p = path[i];
51352
51353 if(typeof p === 'number') {
51354 astr = astr.substr(0, astr.length - 1) + '[' + p + ']';
51355 } else {
51356 astr += p;
51357 }
51358
51359 if(i < path.length - 1) astr += '.';
51360 }
51361
51362 return astr;
51363}
51364
51365},{"../lib":178,"../plots/plots":256,"./plot_config":210,"./plot_schema":211}],217:[function(_dereq_,module,exports){
51366/**
51367* Copyright 2012-2020, Plotly, Inc.
51368* All rights reserved.
51369*
51370* This source code is licensed under the MIT license found in the
51371* LICENSE file in the root directory of this source tree.
51372*/
51373
51374'use strict';
51375
51376module.exports = {
51377 mode: {
51378 valType: 'enumerated',
51379 dflt: 'afterall',
51380
51381 values: ['immediate', 'next', 'afterall'],
51382
51383 },
51384 direction: {
51385 valType: 'enumerated',
51386
51387 values: ['forward', 'reverse'],
51388 dflt: 'forward',
51389
51390 },
51391 fromcurrent: {
51392 valType: 'boolean',
51393 dflt: false,
51394
51395
51396 },
51397 frame: {
51398 duration: {
51399 valType: 'number',
51400
51401 min: 0,
51402 dflt: 500,
51403
51404 },
51405 redraw: {
51406 valType: 'boolean',
51407
51408 dflt: true,
51409
51410 },
51411 },
51412 transition: {
51413 duration: {
51414 valType: 'number',
51415
51416 min: 0,
51417 dflt: 500,
51418 editType: 'none',
51419
51420 },
51421 easing: {
51422 valType: 'enumerated',
51423 dflt: 'cubic-in-out',
51424 values: [
51425 'linear',
51426 'quad',
51427 'cubic',
51428 'sin',
51429 'exp',
51430 'circle',
51431 'elastic',
51432 'back',
51433 'bounce',
51434 'linear-in',
51435 'quad-in',
51436 'cubic-in',
51437 'sin-in',
51438 'exp-in',
51439 'circle-in',
51440 'elastic-in',
51441 'back-in',
51442 'bounce-in',
51443 'linear-out',
51444 'quad-out',
51445 'cubic-out',
51446 'sin-out',
51447 'exp-out',
51448 'circle-out',
51449 'elastic-out',
51450 'back-out',
51451 'bounce-out',
51452 'linear-in-out',
51453 'quad-in-out',
51454 'cubic-in-out',
51455 'sin-in-out',
51456 'exp-in-out',
51457 'circle-in-out',
51458 'elastic-in-out',
51459 'back-in-out',
51460 'bounce-in-out'
51461 ],
51462
51463 editType: 'none',
51464
51465 },
51466 ordering: {
51467 valType: 'enumerated',
51468 values: ['layout first', 'traces first'],
51469 dflt: 'layout first',
51470
51471 editType: 'none',
51472
51473 }
51474 }
51475};
51476
51477},{}],218:[function(_dereq_,module,exports){
51478/**
51479* Copyright 2012-2020, Plotly, Inc.
51480* All rights reserved.
51481*
51482* This source code is licensed under the MIT license found in the
51483* LICENSE file in the root directory of this source tree.
51484*/
51485
51486'use strict';
51487
51488var Lib = _dereq_('../lib');
51489var Template = _dereq_('../plot_api/plot_template');
51490
51491/** Convenience wrapper for making array container logic DRY and consistent
51492 *
51493 * @param {object} parentObjIn
51494 * user input object where the container in question is linked
51495 * (i.e. either a user trace object or the user layout object)
51496 *
51497 * @param {object} parentObjOut
51498 * full object where the coerced container will be linked
51499 * (i.e. either a full trace object or the full layout object)
51500 *
51501 * @param {object} opts
51502 * options object:
51503 * - name {string}
51504 * name of the key linking the container in question
51505 * - inclusionAttr {string}
51506 * name of the item attribute for inclusion/exclusion. Default is 'visible'.
51507 * Since inclusion is true, use eg 'enabled' instead of 'disabled'.
51508 * - handleItemDefaults {function}
51509 * defaults method to be called on each item in the array container in question
51510 *
51511 * Its arguments are:
51512 * - itemIn {object} item in user layout
51513 * - itemOut {object} item in full layout
51514 * - parentObj {object} (as in closure)
51515 * - opts {object} (as in closure)
51516 * N.B.
51517 *
51518 * - opts is passed to handleItemDefaults so it can also store
51519 * links to supplementary data (e.g. fullData for layout components)
51520 *
51521 */
51522module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut, opts) {
51523 var name = opts.name;
51524 var inclusionAttr = opts.inclusionAttr || 'visible';
51525
51526 var previousContOut = parentObjOut[name];
51527
51528 var contIn = Lib.isArrayOrTypedArray(parentObjIn[name]) ? parentObjIn[name] : [];
51529 var contOut = parentObjOut[name] = [];
51530 var templater = Template.arrayTemplater(parentObjOut, name, inclusionAttr);
51531 var i, itemOut;
51532
51533 for(i = 0; i < contIn.length; i++) {
51534 var itemIn = contIn[i];
51535
51536 if(!Lib.isPlainObject(itemIn)) {
51537 itemOut = templater.newItem({});
51538 itemOut[inclusionAttr] = false;
51539 } else {
51540 itemOut = templater.newItem(itemIn);
51541 }
51542
51543 itemOut._index = i;
51544
51545 if(itemOut[inclusionAttr] !== false) {
51546 opts.handleItemDefaults(itemIn, itemOut, parentObjOut, opts);
51547 }
51548
51549 contOut.push(itemOut);
51550 }
51551
51552 var defaultItems = templater.defaultItems();
51553 for(i = 0; i < defaultItems.length; i++) {
51554 itemOut = defaultItems[i];
51555 itemOut._index = contOut.length;
51556 opts.handleItemDefaults({}, itemOut, parentObjOut, opts, {});
51557 contOut.push(itemOut);
51558 }
51559
51560 // in case this array gets its defaults rebuilt independent of the whole layout,
51561 // relink the private keys just for this array.
51562 if(Lib.isArrayOrTypedArray(previousContOut)) {
51563 var len = Math.min(previousContOut.length, contOut.length);
51564 for(i = 0; i < len; i++) {
51565 Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
51566 }
51567 }
51568
51569 return contOut;
51570};
51571
51572},{"../lib":178,"../plot_api/plot_template":212}],219:[function(_dereq_,module,exports){
51573/**
51574* Copyright 2012-2020, Plotly, Inc.
51575* All rights reserved.
51576*
51577* This source code is licensed under the MIT license found in the
51578* LICENSE file in the root directory of this source tree.
51579*/
51580
51581'use strict';
51582
51583var fxAttrs = _dereq_('../components/fx/attributes');
51584
51585module.exports = {
51586 type: {
51587 valType: 'enumerated',
51588
51589 values: [], // listed dynamically
51590 dflt: 'scatter',
51591 editType: 'calc+clearAxisTypes',
51592 _noTemplating: true // we handle this at a higher level
51593 },
51594 visible: {
51595 valType: 'enumerated',
51596 values: [true, false, 'legendonly'],
51597
51598 dflt: true,
51599 editType: 'calc',
51600
51601 },
51602 showlegend: {
51603 valType: 'boolean',
51604
51605 dflt: true,
51606 editType: 'style',
51607
51608 },
51609 legendgroup: {
51610 valType: 'string',
51611
51612 dflt: '',
51613 editType: 'style',
51614
51615 },
51616 opacity: {
51617 valType: 'number',
51618
51619 min: 0,
51620 max: 1,
51621 dflt: 1,
51622 editType: 'style',
51623
51624 },
51625 name: {
51626 valType: 'string',
51627
51628 editType: 'style',
51629
51630 },
51631 uid: {
51632 valType: 'string',
51633
51634 editType: 'plot',
51635 anim: true,
51636
51637 },
51638 ids: {
51639 valType: 'data_array',
51640 editType: 'calc',
51641 anim: true,
51642
51643 },
51644 customdata: {
51645 valType: 'data_array',
51646 editType: 'calc',
51647
51648 },
51649 meta: {
51650 valType: 'any',
51651 arrayOk: true,
51652
51653 editType: 'plot',
51654
51655 },
51656
51657 // N.B. these cannot be 'data_array' as they do not have the same length as
51658 // other data arrays and arrayOk attributes in general
51659 //
51660 // Maybe add another valType:
51661 // https://github.com/plotly/plotly.js/issues/1894
51662 selectedpoints: {
51663 valType: 'any',
51664
51665 editType: 'calc',
51666
51667 },
51668
51669 hoverinfo: {
51670 valType: 'flaglist',
51671
51672 flags: ['x', 'y', 'z', 'text', 'name'],
51673 extras: ['all', 'none', 'skip'],
51674 arrayOk: true,
51675 dflt: 'all',
51676 editType: 'none',
51677
51678 },
51679 hoverlabel: fxAttrs.hoverlabel,
51680 stream: {
51681 token: {
51682 valType: 'string',
51683 noBlank: true,
51684 strict: true,
51685
51686 editType: 'calc',
51687
51688 },
51689 maxpoints: {
51690 valType: 'number',
51691 min: 0,
51692 max: 10000,
51693 dflt: 500,
51694
51695 editType: 'calc',
51696
51697 },
51698 editType: 'calc'
51699 },
51700 transforms: {
51701 _isLinkedToArray: 'transform',
51702 editType: 'calc',
51703
51704 },
51705 uirevision: {
51706 valType: 'any',
51707
51708 editType: 'none',
51709
51710 }
51711};
51712
51713},{"../components/fx/attributes":83}],220:[function(_dereq_,module,exports){
51714/**
51715* Copyright 2012-2020, Plotly, Inc.
51716* All rights reserved.
51717*
51718* This source code is licensed under the MIT license found in the
51719* LICENSE file in the root directory of this source tree.
51720*/
51721
51722'use strict';
51723
51724
51725module.exports = {
51726 xaxis: {
51727 valType: 'subplotid',
51728
51729 dflt: 'x',
51730 editType: 'calc+clearAxisTypes',
51731
51732 },
51733 yaxis: {
51734 valType: 'subplotid',
51735
51736 dflt: 'y',
51737 editType: 'calc+clearAxisTypes',
51738
51739 }
51740};
51741
51742},{}],221:[function(_dereq_,module,exports){
51743/**
51744* Copyright 2012-2020, Plotly, Inc.
51745* All rights reserved.
51746*
51747* This source code is licensed under the MIT license found in the
51748* LICENSE file in the root directory of this source tree.
51749*/
51750
51751'use strict';
51752
51753var isNumeric = _dereq_('fast-isnumeric');
51754
51755var Lib = _dereq_('../../lib');
51756var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE;
51757var Registry = _dereq_('../../registry');
51758
51759module.exports = {
51760 getAutoRange: getAutoRange,
51761 makePadFn: makePadFn,
51762 doAutoRange: doAutoRange,
51763 findExtremes: findExtremes,
51764 concatExtremes: concatExtremes
51765};
51766
51767/**
51768 * getAutoRange
51769 *
51770 * Collects all _extremes values corresponding to a given axis
51771 * and computes its auto range.
51772 *
51773 * Note that getAutoRange uses return values from findExtremes.
51774 *
51775 * @param {object} gd:
51776 * graph div object with filled-in fullData and fullLayout, in particular
51777 * with filled-in '_extremes' containers:
51778 * {
51779 * val: calcdata value,
51780 * pad: extra pixels beyond this value,
51781 * extrapad: bool, does this point want 5% extra padding
51782 * }
51783 * @param {object} ax:
51784 * full axis object, in particular with filled-in '_traceIndices'
51785 * and '_annIndices' / '_shapeIndices' if applicable
51786 * @return {array}
51787 * an array of [min, max]. These are calcdata for log and category axes
51788 * and data for linear and date axes.
51789 *
51790 * TODO: we want to change log to data as well, but it's hard to do this
51791 * maintaining backward compatibility. category will always have to use calcdata
51792 * though, because otherwise values between categories (or outside all categories)
51793 * would be impossible.
51794 */
51795function getAutoRange(gd, ax) {
51796 var i, j;
51797 var newRange = [];
51798
51799 var getPad = makePadFn(ax);
51800 var extremes = concatExtremes(gd, ax);
51801 var minArray = extremes.min;
51802 var maxArray = extremes.max;
51803
51804 if(minArray.length === 0 || maxArray.length === 0) {
51805 return Lib.simpleMap(ax.range, ax.r2l);
51806 }
51807
51808 var minmin = minArray[0].val;
51809 var maxmax = maxArray[0].val;
51810
51811 for(i = 1; i < minArray.length; i++) {
51812 if(minmin !== maxmax) break;
51813 minmin = Math.min(minmin, minArray[i].val);
51814 }
51815 for(i = 1; i < maxArray.length; i++) {
51816 if(minmin !== maxmax) break;
51817 maxmax = Math.max(maxmax, maxArray[i].val);
51818 }
51819
51820 var axReverse = false;
51821
51822 if(ax.range) {
51823 var rng = Lib.simpleMap(ax.range, ax.r2l);
51824 axReverse = rng[1] < rng[0];
51825 }
51826 // one-time setting to easily reverse the axis
51827 // when plotting from code
51828 if(ax.autorange === 'reversed') {
51829 axReverse = true;
51830 ax.autorange = true;
51831 }
51832
51833 var rangeMode = ax.rangemode;
51834 var toZero = rangeMode === 'tozero';
51835 var nonNegative = rangeMode === 'nonnegative';
51836 var axLen = ax._length;
51837 // don't allow padding to reduce the data to < 10% of the length
51838 var minSpan = axLen / 10;
51839
51840 // find axis rangebreaks in [v0,v1] and compute its length in value space
51841 var calcBreaksLength = function(v0, v1) {
51842 var lBreaks = 0;
51843 if(ax.rangebreaks) {
51844 var rangebreaksOut = ax.locateBreaks(v0, v1);
51845 for(var i = 0; i < rangebreaksOut.length; i++) {
51846 var brk = rangebreaksOut[i];
51847 lBreaks += brk.max - brk.min;
51848 }
51849 }
51850 return lBreaks;
51851 };
51852
51853 var mbest = 0;
51854 var minpt, maxpt, minbest, maxbest, dp, dv;
51855
51856 for(i = 0; i < minArray.length; i++) {
51857 minpt = minArray[i];
51858 for(j = 0; j < maxArray.length; j++) {
51859 maxpt = maxArray[j];
51860 dv = maxpt.val - minpt.val - calcBreaksLength(minpt.val, maxpt.val);
51861 if(dv > 0) {
51862 dp = axLen - getPad(minpt) - getPad(maxpt);
51863 if(dp > minSpan) {
51864 if(dv / dp > mbest) {
51865 minbest = minpt;
51866 maxbest = maxpt;
51867 mbest = dv / dp;
51868 }
51869 } else if(dv / axLen > mbest) {
51870 // in case of padding longer than the axis
51871 // at least include the unpadded data values.
51872 minbest = {val: minpt.val, pad: 0};
51873 maxbest = {val: maxpt.val, pad: 0};
51874 mbest = dv / axLen;
51875 }
51876 }
51877 }
51878 }
51879
51880 function getMaxPad(prev, pt) {
51881 return Math.max(prev, getPad(pt));
51882 }
51883
51884 if(minmin === maxmax) {
51885 var lower = minmin - 1;
51886 var upper = minmin + 1;
51887 if(toZero) {
51888 if(minmin === 0) {
51889 // The only value we have on this axis is 0, and we want to
51890 // autorange so zero is one end.
51891 // In principle this could be [0, 1] or [-1, 0] but usually
51892 // 'tozero' pins 0 to the low end, so follow that.
51893 newRange = [0, 1];
51894 } else {
51895 var maxPad = (minmin > 0 ? maxArray : minArray).reduce(getMaxPad, 0);
51896 // we're pushing a single value away from the edge due to its
51897 // padding, with the other end clamped at zero
51898 // 0.5 means don't push it farther than the center.
51899 var rangeEnd = minmin / (1 - Math.min(0.5, maxPad / axLen));
51900 newRange = minmin > 0 ? [0, rangeEnd] : [rangeEnd, 0];
51901 }
51902 } else if(nonNegative) {
51903 newRange = [Math.max(0, lower), Math.max(1, upper)];
51904 } else {
51905 newRange = [lower, upper];
51906 }
51907 } else {
51908 if(toZero) {
51909 if(minbest.val >= 0) {
51910 minbest = {val: 0, pad: 0};
51911 }
51912 if(maxbest.val <= 0) {
51913 maxbest = {val: 0, pad: 0};
51914 }
51915 } else if(nonNegative) {
51916 if(minbest.val - mbest * getPad(minbest) < 0) {
51917 minbest = {val: 0, pad: 0};
51918 }
51919 if(maxbest.val <= 0) {
51920 maxbest = {val: 1, pad: 0};
51921 }
51922 }
51923
51924 // in case it changed again...
51925 mbest = (maxbest.val - minbest.val - calcBreaksLength(minpt.val, maxpt.val)) /
51926 (axLen - getPad(minbest) - getPad(maxbest));
51927
51928 newRange = [
51929 minbest.val - mbest * getPad(minbest),
51930 maxbest.val + mbest * getPad(maxbest)
51931 ];
51932 }
51933
51934 // maintain reversal
51935 if(axReverse) newRange.reverse();
51936
51937 return Lib.simpleMap(newRange, ax.l2r || Number);
51938}
51939
51940/*
51941 * calculate the pixel padding for ax._min and ax._max entries with
51942 * optional extrapad as 5% of the total axis length
51943 */
51944function makePadFn(ax) {
51945 // 5% padding for points that specify extrapad: true
51946 var extrappad = ax._length / 20;
51947
51948 // domain-constrained axes: base extrappad on the unconstrained
51949 // domain so it's consistent as the domain changes
51950 if((ax.constrain === 'domain') && ax._inputDomain) {
51951 extrappad *= (ax._inputDomain[1] - ax._inputDomain[0]) /
51952 (ax.domain[1] - ax.domain[0]);
51953 }
51954
51955 return function getPad(pt) { return pt.pad + (pt.extrapad ? extrappad : 0); };
51956}
51957
51958function concatExtremes(gd, ax) {
51959 var axId = ax._id;
51960 var fullData = gd._fullData;
51961 var fullLayout = gd._fullLayout;
51962 var minArray = [];
51963 var maxArray = [];
51964 var i, j, d;
51965
51966 function _concat(cont, indices) {
51967 for(i = 0; i < indices.length; i++) {
51968 var item = cont[indices[i]];
51969 var extremes = (item._extremes || {})[axId];
51970 if(item.visible === true && extremes) {
51971 for(j = 0; j < extremes.min.length; j++) {
51972 d = extremes.min[j];
51973 collapseMinArray(minArray, d.val, d.pad, {extrapad: d.extrapad});
51974 }
51975 for(j = 0; j < extremes.max.length; j++) {
51976 d = extremes.max[j];
51977 collapseMaxArray(maxArray, d.val, d.pad, {extrapad: d.extrapad});
51978 }
51979 }
51980 }
51981 }
51982
51983 _concat(fullData, ax._traceIndices);
51984 _concat(fullLayout.annotations || [], ax._annIndices || []);
51985 _concat(fullLayout.shapes || [], ax._shapeIndices || []);
51986
51987 return {min: minArray, max: maxArray};
51988}
51989
51990function doAutoRange(gd, ax) {
51991 ax.setScale();
51992
51993 if(ax.autorange) {
51994 ax.range = getAutoRange(gd, ax);
51995
51996 ax._r = ax.range.slice();
51997 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
51998
51999 // doAutoRange will get called on fullLayout,
52000 // but we want to report its results back to layout
52001
52002 var axIn = ax._input;
52003
52004 // before we edit _input, store preGUI values
52005 var edits = {};
52006 edits[ax._attr + '.range'] = ax.range;
52007 edits[ax._attr + '.autorange'] = ax.autorange;
52008 Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, edits);
52009
52010 axIn.range = ax.range.slice();
52011 axIn.autorange = ax.autorange;
52012 }
52013
52014 var anchorAx = ax._anchorAxis;
52015
52016 if(anchorAx && anchorAx.rangeslider) {
52017 var axeRangeOpts = anchorAx.rangeslider[ax._name];
52018 if(axeRangeOpts) {
52019 if(axeRangeOpts.rangemode === 'auto') {
52020 axeRangeOpts.range = getAutoRange(gd, ax);
52021 }
52022 }
52023 anchorAx._input.rangeslider[ax._name] = Lib.extendFlat({}, axeRangeOpts);
52024 }
52025}
52026
52027/**
52028 * findExtremes
52029 *
52030 * Find min/max extremes of an array of coordinates on a given axis.
52031 *
52032 * Note that findExtremes is called during `calc`, when we don't yet know the axis
52033 * length; all the inputs should be based solely on the trace data, nothing
52034 * about the axis layout.
52035 *
52036 * Note that `ppad` and `vpad` as well as their asymmetric variants refer to
52037 * the before and after padding of the passed `data` array, not to the whole axis.
52038 *
52039 * @param {object} ax: full axis object
52040 * relies on
52041 * - ax.type
52042 * - ax._m (just its sign)
52043 * - ax.d2l
52044 * @param {array} data:
52045 * array of numbers (i.e. already run though ax.d2c)
52046 * @param {object} opts:
52047 * available keys are:
52048 * vpad: (number or number array) pad values (data value +-vpad)
52049 * ppad: (number or number array) pad pixels (pixel location +-ppad)
52050 * ppadplus, ppadminus, vpadplus, vpadminus:
52051 * separate padding for each side, overrides symmetric
52052 * padded: (boolean) add 5% padding to both ends
52053 * (unless one end is overridden by tozero)
52054 * tozero: (boolean) make sure to include zero if axis is linear,
52055 * and make it a tight bound if possible
52056 * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus)
52057 * is linearized (for log scale axes)
52058 *
52059 * @return {object}
52060 * - min {array of objects}
52061 * - max {array of objects}
52062 * each object item has fields:
52063 * - val {number}
52064 * - pad {number}
52065 * - extrappad {number}
52066 * - opts {object}: a ref to the passed "options" object
52067 */
52068function findExtremes(ax, data, opts) {
52069 if(!opts) opts = {};
52070 if(!ax._m) ax.setScale();
52071
52072 var minArray = [];
52073 var maxArray = [];
52074
52075 var len = data.length;
52076 var extrapad = opts.padded || false;
52077 var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-');
52078 var isLog = ax.type === 'log';
52079 var hasArrayOption = false;
52080 var vpadLinearized = opts.vpadLinearized || false;
52081 var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax;
52082
52083 function makePadAccessor(item) {
52084 if(Array.isArray(item)) {
52085 hasArrayOption = true;
52086 return function(i) { return Math.max(Number(item[i]||0), 0); };
52087 } else {
52088 var v = Math.max(Number(item||0), 0);
52089 return function() { return v; };
52090 }
52091 }
52092
52093 var ppadplus = makePadAccessor((ax._m > 0 ?
52094 opts.ppadplus : opts.ppadminus) || opts.ppad || 0);
52095 var ppadminus = makePadAccessor((ax._m > 0 ?
52096 opts.ppadminus : opts.ppadplus) || opts.ppad || 0);
52097 var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad);
52098 var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad);
52099
52100 if(!hasArrayOption) {
52101 // with no arrays other than `data` we don't need to consider
52102 // every point, only the extreme data points
52103 vmin = Infinity;
52104 vmax = -Infinity;
52105
52106 if(isLog) {
52107 for(i = 0; i < len; i++) {
52108 v = data[i];
52109 // data is not linearized yet so we still have to filter out negative logs
52110 if(v < vmin && v > 0) vmin = v;
52111 if(v > vmax && v < FP_SAFE) vmax = v;
52112 }
52113 } else {
52114 for(i = 0; i < len; i++) {
52115 v = data[i];
52116 if(v < vmin && v > -FP_SAFE) vmin = v;
52117 if(v > vmax && v < FP_SAFE) vmax = v;
52118 }
52119 }
52120
52121 data = [vmin, vmax];
52122 len = 2;
52123 }
52124
52125 var collapseOpts = {tozero: tozero, extrapad: extrapad};
52126
52127 function addItem(i) {
52128 di = data[i];
52129 if(!isNumeric(di)) return;
52130 ppadiplus = ppadplus(i);
52131 ppadiminus = ppadminus(i);
52132
52133 if(vpadLinearized) {
52134 dmin = ax.c2l(di) - vpadminus(i);
52135 dmax = ax.c2l(di) + vpadplus(i);
52136 } else {
52137 vmin = di - vpadminus(i);
52138 vmax = di + vpadplus(i);
52139 // special case for log axes: if vpad makes this object span
52140 // more than an order of mag, clip it to one order. This is so
52141 // we don't have non-positive errors or absurdly large lower
52142 // range due to rounding errors
52143 if(isLog && vmin < vmax / 10) vmin = vmax / 10;
52144
52145 dmin = ax.c2l(vmin);
52146 dmax = ax.c2l(vmax);
52147 }
52148
52149 if(tozero) {
52150 dmin = Math.min(0, dmin);
52151 dmax = Math.max(0, dmax);
52152 }
52153 if(goodNumber(dmin)) {
52154 collapseMinArray(minArray, dmin, ppadiminus, collapseOpts);
52155 }
52156 if(goodNumber(dmax)) {
52157 collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts);
52158 }
52159 }
52160
52161 // For efficiency covering monotonic or near-monotonic data,
52162 // check a few points at both ends first and then sweep
52163 // through the middle
52164 var iMax = Math.min(6, len);
52165 for(i = 0; i < iMax; i++) addItem(i);
52166 for(i = len - 1; i >= iMax; i--) addItem(i);
52167
52168 return {
52169 min: minArray,
52170 max: maxArray,
52171 opts: opts
52172 };
52173}
52174
52175function collapseMinArray(array, newVal, newPad, opts) {
52176 collapseArray(array, newVal, newPad, opts, lessOrEqual);
52177}
52178
52179function collapseMaxArray(array, newVal, newPad, opts) {
52180 collapseArray(array, newVal, newPad, opts, greaterOrEqual);
52181}
52182
52183/**
52184 * collapseArray
52185 *
52186 * Takes items from 'array' and compares them to 'newVal', 'newPad'.
52187 *
52188 * @param {array} array:
52189 * current set of min or max extremes
52190 * @param {number} newVal:
52191 * new value to compare against
52192 * @param {number} newPad:
52193 * pad value associated with 'newVal'
52194 * @param {object} opts:
52195 * - tozero {boolean}
52196 * - extrapad {number}
52197 * @param {function} atLeastAsExtreme:
52198 * comparison function, use
52199 * - lessOrEqual for min 'array' and
52200 * - greaterOrEqual for max 'array'
52201 *
52202 * In practice, 'array' is either
52203 * - 'extremes[ax._id].min' or
52204 * - 'extremes[ax._id].max
52205 * found in traces and layout items that affect autorange.
52206 *
52207 * Since we don't yet know the relationship between pixels and values
52208 * (that's what we're trying to figure out!) AND we don't yet know how
52209 * many pixels `extrapad` represents (it's going to be 5% of the length,
52210 * but we don't want to have to redo calc just because length changed)
52211 * two point must satisfy three criteria simultaneously for one to supersede the other:
52212 * - at least as extreme a `val`
52213 * - at least as big a `pad`
52214 * - an unpadded point cannot supersede a padded point, but any other combination can
52215 *
52216 * Then:
52217 * - If the item supersedes the new point, set includeThis false
52218 * - If the new pt supersedes the item, delete it from 'array'
52219 */
52220function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) {
52221 var tozero = opts.tozero;
52222 var extrapad = opts.extrapad;
52223 var includeThis = true;
52224
52225 for(var j = 0; j < array.length && includeThis; j++) {
52226 var v = array[j];
52227 if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) {
52228 includeThis = false;
52229 break;
52230 } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) {
52231 array.splice(j, 1);
52232 j--;
52233 }
52234 }
52235 if(includeThis) {
52236 var clipAtZero = (tozero && newVal === 0);
52237 array.push({
52238 val: newVal,
52239 pad: clipAtZero ? 0 : newPad,
52240 extrapad: clipAtZero ? false : extrapad
52241 });
52242 }
52243}
52244
52245// In order to stop overflow errors, don't consider points
52246// too close to the limits of js floating point
52247function goodNumber(v) {
52248 return isNumeric(v) && Math.abs(v) < FP_SAFE;
52249}
52250
52251function lessOrEqual(v0, v1) { return v0 <= v1; }
52252function greaterOrEqual(v0, v1) { return v0 >= v1; }
52253
52254},{"../../constants/numerical":158,"../../lib":178,"../../registry":269,"fast-isnumeric":18}],222:[function(_dereq_,module,exports){
52255/**
52256* Copyright 2012-2020, Plotly, Inc.
52257* All rights reserved.
52258*
52259* This source code is licensed under the MIT license found in the
52260* LICENSE file in the root directory of this source tree.
52261*/
52262
52263'use strict';
52264
52265var d3 = _dereq_('d3');
52266var isNumeric = _dereq_('fast-isnumeric');
52267var Plots = _dereq_('../../plots/plots');
52268
52269var Registry = _dereq_('../../registry');
52270var Lib = _dereq_('../../lib');
52271var svgTextUtils = _dereq_('../../lib/svg_text_utils');
52272var Titles = _dereq_('../../components/titles');
52273var Color = _dereq_('../../components/color');
52274var Drawing = _dereq_('../../components/drawing');
52275
52276var axAttrs = _dereq_('./layout_attributes');
52277var cleanTicks = _dereq_('./clean_ticks');
52278
52279var constants = _dereq_('../../constants/numerical');
52280var ONEAVGYEAR = constants.ONEAVGYEAR;
52281var ONEAVGMONTH = constants.ONEAVGMONTH;
52282var ONEDAY = constants.ONEDAY;
52283var ONEHOUR = constants.ONEHOUR;
52284var ONEMIN = constants.ONEMIN;
52285var ONESEC = constants.ONESEC;
52286var MINUS_SIGN = constants.MINUS_SIGN;
52287var BADNUM = constants.BADNUM;
52288
52289var alignmentConstants = _dereq_('../../constants/alignment');
52290var MID_SHIFT = alignmentConstants.MID_SHIFT;
52291var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
52292var LINE_SPACING = alignmentConstants.LINE_SPACING;
52293var OPPOSITE_SIDE = alignmentConstants.OPPOSITE_SIDE;
52294
52295var axes = module.exports = {};
52296
52297axes.setConvert = _dereq_('./set_convert');
52298var autoType = _dereq_('./axis_autotype');
52299
52300var axisIds = _dereq_('./axis_ids');
52301axes.id2name = axisIds.id2name;
52302axes.name2id = axisIds.name2id;
52303axes.cleanId = axisIds.cleanId;
52304axes.list = axisIds.list;
52305axes.listIds = axisIds.listIds;
52306axes.getFromId = axisIds.getFromId;
52307axes.getFromTrace = axisIds.getFromTrace;
52308
52309var autorange = _dereq_('./autorange');
52310axes.getAutoRange = autorange.getAutoRange;
52311axes.findExtremes = autorange.findExtremes;
52312
52313/*
52314 * find the list of possible axes to reference with an xref or yref attribute
52315 * and coerce it to that list
52316 *
52317 * attr: the attribute we're generating a reference for. Should end in 'x' or 'y'
52318 * but can be prefixed, like 'ax' for annotation's arrow x
52319 * dflt: the default to coerce to, or blank to use the first axis (falling back on
52320 * extraOption if there is no axis)
52321 * extraOption: aside from existing axes with this letter, what non-axis value is allowed?
52322 * Only required if it's different from `dflt`
52323 */
52324axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
52325 var axLetter = attr.charAt(attr.length - 1);
52326 var axlist = gd._fullLayout._subplots[axLetter + 'axis'];
52327 var refAttr = attr + 'ref';
52328 var attrDef = {};
52329
52330 if(!dflt) dflt = axlist[0] || extraOption;
52331 if(!extraOption) extraOption = dflt;
52332
52333 // data-ref annotations are not supported in gl2d yet
52334
52335 attrDef[refAttr] = {
52336 valType: 'enumerated',
52337 values: axlist.concat(extraOption ? [extraOption] : []),
52338 dflt: dflt
52339 };
52340
52341 // xref, yref
52342 return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
52343};
52344
52345/*
52346 * coerce position attributes (range-type) that can be either on axes or absolute
52347 * (paper or pixel) referenced. The biggest complication here is that we don't know
52348 * before looking at the axis whether the value must be a number or not (it may be
52349 * a date string), so we can't use the regular valType='number' machinery
52350 *
52351 * axRef (string): the axis this position is referenced to, or:
52352 * paper: fraction of the plot area
52353 * pixel: pixels relative to some starting position
52354 * attr (string): the attribute in containerOut we are coercing
52355 * dflt (number): the default position, as a fraction or pixels. If the attribute
52356 * is to be axis-referenced, this will be converted to an axis data value
52357 *
52358 * Also cleans the values, since the attribute definition itself has to say
52359 * valType: 'any' to handle date axes. This allows us to accept:
52360 * - for category axes: category names, and convert them here into serial numbers.
52361 * Note that this will NOT work for axis range endpoints, because we don't know
52362 * the category list yet (it's set by ax.makeCalcdata during calc)
52363 * but it works for component (note, shape, images) positions.
52364 * - for date axes: JS Dates or milliseconds, and convert to date strings
52365 * - for other types: coerce them to numbers
52366 */
52367axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) {
52368 var cleanPos, pos;
52369
52370 if(axRef === 'paper' || axRef === 'pixel') {
52371 cleanPos = Lib.ensureNumber;
52372 pos = coerce(attr, dflt);
52373 } else {
52374 var ax = axes.getFromId(gd, axRef);
52375 dflt = ax.fraction2r(dflt);
52376 pos = coerce(attr, dflt);
52377 cleanPos = ax.cleanPos;
52378 }
52379
52380 containerOut[attr] = cleanPos(pos);
52381};
52382
52383axes.cleanPosition = function(pos, gd, axRef) {
52384 var cleanPos = (axRef === 'paper' || axRef === 'pixel') ?
52385 Lib.ensureNumber :
52386 axes.getFromId(gd, axRef).cleanPos;
52387
52388 return cleanPos(pos);
52389};
52390
52391axes.redrawComponents = function(gd, axIds) {
52392 axIds = axIds ? axIds : axes.listIds(gd);
52393
52394 var fullLayout = gd._fullLayout;
52395
52396 function _redrawOneComp(moduleName, methodName, stashName, shortCircuit) {
52397 var method = Registry.getComponentMethod(moduleName, methodName);
52398 var stash = {};
52399
52400 for(var i = 0; i < axIds.length; i++) {
52401 var ax = fullLayout[axes.id2name(axIds[i])];
52402 var indices = ax[stashName];
52403
52404 for(var j = 0; j < indices.length; j++) {
52405 var ind = indices[j];
52406
52407 if(!stash[ind]) {
52408 method(gd, ind);
52409 stash[ind] = 1;
52410 // once is enough for images (which doesn't use the `i` arg anyway)
52411 if(shortCircuit) return;
52412 }
52413 }
52414 }
52415 }
52416
52417 // annotations and shapes 'draw' method is slow,
52418 // use the finer-grained 'drawOne' method instead
52419 _redrawOneComp('annotations', 'drawOne', '_annIndices');
52420 _redrawOneComp('shapes', 'drawOne', '_shapeIndices');
52421 _redrawOneComp('images', 'draw', '_imgIndices', true);
52422};
52423
52424var getDataConversions = axes.getDataConversions = function(gd, trace, target, targetArray) {
52425 var ax;
52426
52427 // If target points to an axis, use the type we already have for that
52428 // axis to find the data type. Otherwise use the values to autotype.
52429 var d2cTarget = (target === 'x' || target === 'y' || target === 'z') ?
52430 target :
52431 targetArray;
52432
52433 // In the case of an array target, make a mock data array
52434 // and call supplyDefaults to the data type and
52435 // setup the data-to-calc method.
52436 if(Array.isArray(d2cTarget)) {
52437 ax = {
52438 type: autoType(targetArray),
52439 _categories: []
52440 };
52441 axes.setConvert(ax);
52442
52443 // build up ax._categories (usually done during ax.makeCalcdata()
52444 if(ax.type === 'category') {
52445 for(var i = 0; i < targetArray.length; i++) {
52446 ax.d2c(targetArray[i]);
52447 }
52448 }
52449 // TODO what to do for transforms?
52450 } else {
52451 ax = axes.getFromTrace(gd, trace, d2cTarget);
52452 }
52453
52454 // if 'target' has corresponding axis
52455 // -> use setConvert method
52456 if(ax) return {d2c: ax.d2c, c2d: ax.c2d};
52457
52458 // special case for 'ids'
52459 // -> cast to String
52460 if(d2cTarget === 'ids') return {d2c: toString, c2d: toString};
52461
52462 // otherwise (e.g. numeric-array of 'marker.color' or 'marker.size')
52463 // -> cast to Number
52464
52465 return {d2c: toNum, c2d: toNum};
52466};
52467
52468function toNum(v) { return +v; }
52469function toString(v) { return String(v); }
52470
52471axes.getDataToCoordFunc = function(gd, trace, target, targetArray) {
52472 return getDataConversions(gd, trace, target, targetArray).d2c;
52473};
52474
52475// get counteraxis letter for this axis (name or id)
52476// this can also be used as the id for default counter axis
52477axes.counterLetter = function(id) {
52478 var axLetter = id.charAt(0);
52479 if(axLetter === 'x') return 'y';
52480 if(axLetter === 'y') return 'x';
52481};
52482
52483// incorporate a new minimum difference and first tick into
52484// forced
52485// note that _forceTick0 is linearized, so needs to be turned into
52486// a range value for setting tick0
52487axes.minDtick = function(ax, newDiff, newFirst, allow) {
52488 // doesn't make sense to do forced min dTick on log or category axes,
52489 // and the plot itself may decide to cancel (ie non-grouped bars)
52490 if(['log', 'category', 'multicategory'].indexOf(ax.type) !== -1 || !allow) {
52491 ax._minDtick = 0;
52492 } else if(ax._minDtick === undefined) {
52493 // undefined means there's nothing there yet
52494
52495 ax._minDtick = newDiff;
52496 ax._forceTick0 = newFirst;
52497 } else if(ax._minDtick) {
52498 if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 &&
52499 // existing minDtick is an integer multiple of newDiff
52500 // (within rounding err)
52501 // and forceTick0 can be shifted to newFirst
52502
52503 (((newFirst - ax._forceTick0) / newDiff % 1) +
52504 1.000001) % 1 < 2e-6) {
52505 ax._minDtick = newDiff;
52506 ax._forceTick0 = newFirst;
52507 } else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 ||
52508 // if the converse is true (newDiff is a multiple of minDtick and
52509 // newFirst can be shifted to forceTick0) then do nothing - same
52510 // forcing stands. Otherwise, cancel forced minimum
52511
52512 (((newFirst - ax._forceTick0) / ax._minDtick % 1) +
52513 1.000001) % 1 > 2e-6) {
52514 ax._minDtick = 0;
52515 }
52516 }
52517};
52518
52519// save a copy of the initial axis ranges in fullLayout
52520// use them in mode bar and dblclick events
52521axes.saveRangeInitial = function(gd, overwrite) {
52522 var axList = axes.list(gd, '', true);
52523 var hasOneAxisChanged = false;
52524
52525 for(var i = 0; i < axList.length; i++) {
52526 var ax = axList[i];
52527 var isNew = (ax._rangeInitial === undefined);
52528 var hasChanged = isNew || !(
52529 ax.range[0] === ax._rangeInitial[0] &&
52530 ax.range[1] === ax._rangeInitial[1]
52531 );
52532
52533 if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
52534 ax._rangeInitial = ax.range.slice();
52535 hasOneAxisChanged = true;
52536 }
52537 }
52538
52539 return hasOneAxisChanged;
52540};
52541
52542// save a copy of the initial spike visibility
52543axes.saveShowSpikeInitial = function(gd, overwrite) {
52544 var axList = axes.list(gd, '', true);
52545 var hasOneAxisChanged = false;
52546 var allSpikesEnabled = 'on';
52547
52548 for(var i = 0; i < axList.length; i++) {
52549 var ax = axList[i];
52550 var isNew = (ax._showSpikeInitial === undefined);
52551 var hasChanged = isNew || !(ax.showspikes === ax._showspikes);
52552
52553 if(isNew || (overwrite && hasChanged)) {
52554 ax._showSpikeInitial = ax.showspikes;
52555 hasOneAxisChanged = true;
52556 }
52557
52558 if(allSpikesEnabled === 'on' && !ax.showspikes) {
52559 allSpikesEnabled = 'off';
52560 }
52561 }
52562 gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
52563 return hasOneAxisChanged;
52564};
52565
52566axes.autoBin = function(data, ax, nbins, is2d, calendar, size) {
52567 var dataMin = Lib.aggNums(Math.min, null, data);
52568 var dataMax = Lib.aggNums(Math.max, null, data);
52569
52570 if(ax.type === 'category' || ax.type === 'multicategory') {
52571 return {
52572 start: dataMin - 0.5,
52573 end: dataMax + 0.5,
52574 size: Math.max(1, Math.round(size) || 1),
52575 _dataSpan: dataMax - dataMin,
52576 };
52577 }
52578
52579 if(!calendar) calendar = ax.calendar;
52580
52581 // piggyback off tick code to make "nice" bin sizes and edges
52582 var dummyAx;
52583 if(ax.type === 'log') {
52584 dummyAx = {
52585 type: 'linear',
52586 range: [dataMin, dataMax]
52587 };
52588 } else {
52589 dummyAx = {
52590 type: ax.type,
52591 range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
52592 calendar: calendar
52593 };
52594 }
52595 axes.setConvert(dummyAx);
52596
52597 size = size && cleanTicks.dtick(size, dummyAx.type);
52598
52599 if(size) {
52600 dummyAx.dtick = size;
52601 dummyAx.tick0 = cleanTicks.tick0(undefined, dummyAx.type, calendar);
52602 } else {
52603 var size0;
52604 if(nbins) size0 = ((dataMax - dataMin) / nbins);
52605 else {
52606 // totally auto: scale off std deviation so the highest bin is
52607 // somewhat taller than the total number of bins, but don't let
52608 // the size get smaller than the 'nice' rounded down minimum
52609 // difference between values
52610 var distinctData = Lib.distinctVals(data);
52611 var msexp = Math.pow(10, Math.floor(
52612 Math.log(distinctData.minDiff) / Math.LN10));
52613 var minSize = msexp * Lib.roundUp(
52614 distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
52615 size0 = Math.max(minSize, 2 * Lib.stdev(data) /
52616 Math.pow(data.length, is2d ? 0.25 : 0.4));
52617
52618 // fallback if ax.d2c output BADNUMs
52619 // e.g. when user try to plot categorical bins
52620 // on a layout.xaxis.type: 'linear'
52621 if(!isNumeric(size0)) size0 = 1;
52622 }
52623
52624 axes.autoTicks(dummyAx, size0);
52625 }
52626
52627 var finalSize = dummyAx.dtick;
52628 var binStart = axes.tickIncrement(
52629 axes.tickFirst(dummyAx), finalSize, 'reverse', calendar);
52630 var binEnd, bincount;
52631
52632 // check for too many data points right at the edges of bins
52633 // (>50% within 1% of bin edges) or all data points integral
52634 // and offset the bins accordingly
52635 if(typeof finalSize === 'number') {
52636 binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
52637
52638 bincount = 1 + Math.floor((dataMax - binStart) / finalSize);
52639 binEnd = binStart + bincount * finalSize;
52640 } else {
52641 // month ticks - should be the only nonlinear kind we have at this point.
52642 // dtick (as supplied by axes.autoTick) only has nonlinear values on
52643 // date and log axes, but even if you display a histogram on a log axis
52644 // we bin it on a linear axis (which one could argue against, but that's
52645 // a separate issue)
52646 if(dummyAx.dtick.charAt(0) === 'M') {
52647 binStart = autoShiftMonthBins(binStart, data, finalSize, dataMin, calendar);
52648 }
52649
52650 // calculate the endpoint for nonlinear ticks - you have to
52651 // just increment until you're done
52652 binEnd = binStart;
52653 bincount = 0;
52654 while(binEnd <= dataMax) {
52655 binEnd = axes.tickIncrement(binEnd, finalSize, false, calendar);
52656 bincount++;
52657 }
52658 }
52659
52660 return {
52661 start: ax.c2r(binStart, 0, calendar),
52662 end: ax.c2r(binEnd, 0, calendar),
52663 size: finalSize,
52664 _dataSpan: dataMax - dataMin
52665 };
52666};
52667
52668
52669function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) {
52670 var edgecount = 0;
52671 var midcount = 0;
52672 var intcount = 0;
52673 var blankCount = 0;
52674
52675 function nearEdge(v) {
52676 // is a value within 1% of a bin edge?
52677 return (1 + (v - binStart) * 100 / ax.dtick) % 100 < 2;
52678 }
52679
52680 for(var i = 0; i < data.length; i++) {
52681 if(data[i] % 1 === 0) intcount++;
52682 else if(!isNumeric(data[i])) blankCount++;
52683
52684 if(nearEdge(data[i])) edgecount++;
52685 if(nearEdge(data[i] + ax.dtick / 2)) midcount++;
52686 }
52687 var dataCount = data.length - blankCount;
52688
52689 if(intcount === dataCount && ax.type !== 'date') {
52690 if(ax.dtick < 1) {
52691 // all integers: if bin size is <1, it's because
52692 // that was specifically requested (large nbins)
52693 // so respect that... but center the bins containing
52694 // integers on those integers
52695
52696 binStart = dataMin - 0.5 * ax.dtick;
52697 } else {
52698 // otherwise start half an integer down regardless of
52699 // the bin size, just enough to clear up endpoint
52700 // ambiguity about which integers are in which bins.
52701
52702 binStart -= 0.5;
52703 if(binStart + ax.dtick < dataMin) binStart += ax.dtick;
52704 }
52705 } else if(midcount < dataCount * 0.1) {
52706 if(edgecount > dataCount * 0.3 ||
52707 nearEdge(dataMin) || nearEdge(dataMax)) {
52708 // lots of points at the edge, not many in the middle
52709 // shift half a bin
52710 var binshift = ax.dtick / 2;
52711 binStart += (binStart + binshift < dataMin) ? binshift : -binshift;
52712 }
52713 }
52714 return binStart;
52715}
52716
52717
52718function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
52719 var stats = Lib.findExactDates(data, calendar);
52720 // number of data points that needs to be an exact value
52721 // to shift that increment to (near) the bin center
52722 var threshold = 0.8;
52723
52724 if(stats.exactDays > threshold) {
52725 var numMonths = Number(dtick.substr(1));
52726
52727 if((stats.exactYears > threshold) && (numMonths % 12 === 0)) {
52728 // The exact middle of a non-leap-year is 1.5 days into July
52729 // so if we start the bins here, all but leap years will
52730 // get hover-labeled as exact years.
52731 binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5;
52732 } else if(stats.exactMonths > threshold) {
52733 // Months are not as clean, but if we shift half the *longest*
52734 // month (31/2 days) then 31-day months will get labeled exactly
52735 // and shorter months will get labeled with the correct month
52736 // but shifted 12-36 hours into it.
52737 binStart = axes.tickIncrement(binStart, 'M1', 'reverse') + ONEDAY * 15.5;
52738 } else {
52739 // Shifting half a day is exact, but since these are month bins it
52740 // will always give a somewhat odd-looking label, until we do something
52741 // smarter like showing the bin boundaries (or the bounds of the actual
52742 // data in each bin)
52743 binStart -= ONEDAY / 2;
52744 }
52745 var nextBinStart = axes.tickIncrement(binStart, dtick);
52746
52747 if(nextBinStart <= dataMin) return nextBinStart;
52748 }
52749 return binStart;
52750}
52751
52752// ----------------------------------------------------
52753// Ticks and grids
52754// ----------------------------------------------------
52755
52756// ensure we have tick0, dtick, and tick rounding calculated
52757axes.prepTicks = function(ax) {
52758 var rng = Lib.simpleMap(ax.range, ax.r2l);
52759
52760 // calculate max number of (auto) ticks to display based on plot size
52761 if(ax.tickmode === 'auto' || !ax.dtick) {
52762 var nt = ax.nticks;
52763 var minPx;
52764
52765 if(!nt) {
52766 if(ax.type === 'category' || ax.type === 'multicategory') {
52767 minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15;
52768 nt = ax._length / minPx;
52769 } else {
52770 minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
52771 nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
52772 }
52773
52774 // radial axes span half their domain,
52775 // multiply nticks value by two to get correct number of auto ticks.
52776 if(ax._name === 'radialaxis') nt *= 2;
52777 }
52778
52779 // add a couple of extra digits for filling in ticks when we
52780 // have explicit tickvals without tick text
52781 if(ax.tickmode === 'array') nt *= 100;
52782
52783
52784 ax._roughDTick = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / nt;
52785 axes.autoTicks(ax, ax._roughDTick);
52786
52787 // check for a forced minimum dtick
52788 if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) {
52789 ax.dtick = ax._minDtick;
52790 ax.tick0 = ax.l2r(ax._forceTick0);
52791 }
52792 }
52793
52794 // check for missing tick0
52795 if(!ax.tick0) {
52796 ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0;
52797 }
52798
52799 // ensure we don't try to make ticks below our minimum precision
52800 // see https://github.com/plotly/plotly.js/issues/2892
52801 if(ax.type === 'date' && ax.dtick < 0.1) ax.dtick = 0.1;
52802
52803 // now figure out rounding of tick values
52804 autoTickRound(ax);
52805};
52806
52807// calculate the ticks: text, values, positioning
52808// if ticks are set to automatic, determine the right values (tick0,dtick)
52809// in any case, set tickround to # of digits to round tick labels to,
52810// or codes to this effect for log and date scales
52811axes.calcTicks = function calcTicks(ax) {
52812 axes.prepTicks(ax);
52813 var rng = Lib.simpleMap(ax.range, ax.r2l);
52814
52815 // now that we've figured out the auto values for formatting
52816 // in case we're missing some ticktext, we can break out for array ticks
52817 if(ax.tickmode === 'array') return arrayTicks(ax);
52818
52819 // find the first tick
52820 ax._tmin = axes.tickFirst(ax);
52821
52822 // add a tiny bit so we get ticks which may have rounded out
52823 var startTick = rng[0] * 1.0001 - rng[1] * 0.0001;
52824 var endTick = rng[1] * 1.0001 - rng[0] * 0.0001;
52825 // check for reversed axis
52826 var axrev = (rng[1] < rng[0]);
52827
52828 // No visible ticks? Quit.
52829 // I've only seen this on category axes with all categories off the edge.
52830 if((ax._tmin < startTick) !== axrev) return [];
52831
52832 // return the full set of tick vals
52833 if(ax.type === 'category' || ax.type === 'multicategory') {
52834 endTick = (axrev) ? Math.max(-0.5, endTick) :
52835 Math.min(ax._categories.length - 0.5, endTick);
52836 }
52837
52838 var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L');
52839
52840 var tickVals;
52841 function generateTicks() {
52842 var xPrevious = null;
52843 var maxTicks = Math.max(1000, ax._length || 0);
52844 tickVals = [];
52845 for(var x = ax._tmin;
52846 (axrev) ? (x >= endTick) : (x <= endTick);
52847 x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) {
52848 // prevent infinite loops - no more than one tick per pixel,
52849 // and make sure each value is different from the previous
52850 if(tickVals.length > maxTicks || x === xPrevious) break;
52851 xPrevious = x;
52852
52853 var minor = false;
52854 if(isDLog && (x !== (x | 0))) {
52855 minor = true;
52856 }
52857
52858 tickVals.push({
52859 minor: minor,
52860 value: x
52861 });
52862 }
52863 }
52864
52865 generateTicks();
52866
52867 if(ax.rangebreaks) {
52868 // replace ticks inside breaks that would get a tick
52869 if(ax.tickmode === 'auto') {
52870 for(var t = 0; t < tickVals.length; t++) {
52871 var value = tickVals[t].value;
52872 if(ax.maskBreaks(value) === BADNUM) {
52873 // find which break we are in
52874 for(var k = 0; k < ax._rangebreaks.length; k++) {
52875 var brk = ax._rangebreaks[k];
52876 if(value >= brk.min && value < brk.max) {
52877 tickVals[t].value = brk.max; // replace with break end
52878 break;
52879 }
52880 }
52881 }
52882 }
52883 }
52884
52885 // reduce ticks
52886 var len = tickVals.length;
52887 if(len > 2) {
52888 var tf2 = 2 * (ax.tickfont ? ax.tickfont.size : 12);
52889
52890 var newTickVals = [];
52891 var prevPos;
52892
52893 var dir = axrev ? 1 : -1;
52894 var first = axrev ? 0 : len - 1;
52895 var last = axrev ? len - 1 : 0;
52896 for(var q = first; dir * q <= dir * last; q += dir) { // apply reverse loop to pick greater values in breaks first
52897 var pos = ax.c2p(tickVals[q].value);
52898
52899 if(prevPos === undefined || Math.abs(pos - prevPos) > tf2) {
52900 prevPos = pos;
52901 newTickVals.push(tickVals[q]);
52902 }
52903 }
52904 tickVals = newTickVals.reverse();
52905 }
52906 }
52907
52908 // If same angle over a full circle, the last tick vals is a duplicate.
52909 // TODO must do something similar for angular date axes.
52910 if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
52911 tickVals.pop();
52912 }
52913
52914 // save the last tick as well as first, so we can
52915 // show the exponent only on the last one
52916 ax._tmax = (tickVals[tickVals.length - 1] || {}).value;
52917
52918 // for showing the rest of a date when the main tick label is only the
52919 // latter part: ax._prevDateHead holds what we showed most recently.
52920 // Start with it cleared and mark that we're in calcTicks (ie calculating a
52921 // whole string of these so we should care what the previous date head was!)
52922 ax._prevDateHead = '';
52923 ax._inCalcTicks = true;
52924
52925 var ticksOut = new Array(tickVals.length);
52926 for(var i = 0; i < tickVals.length; i++) {
52927 var _minor = tickVals[i].minor;
52928 var _value = tickVals[i].value;
52929
52930 ticksOut[i] = axes.tickText(
52931 ax,
52932 _value,
52933 false, // hover
52934 _minor // noSuffixPrefix
52935 );
52936 }
52937
52938 ax._inCalcTicks = false;
52939
52940 return ticksOut;
52941};
52942
52943function arrayTicks(ax) {
52944 var vals = ax.tickvals;
52945 var text = ax.ticktext;
52946 var ticksOut = new Array(vals.length);
52947 var rng = Lib.simpleMap(ax.range, ax.r2l);
52948 var r0expanded = rng[0] * 1.0001 - rng[1] * 0.0001;
52949 var r1expanded = rng[1] * 1.0001 - rng[0] * 0.0001;
52950 var tickMin = Math.min(r0expanded, r1expanded);
52951 var tickMax = Math.max(r0expanded, r1expanded);
52952 var j = 0;
52953
52954 // without a text array, just format the given values as any other ticks
52955 // except with more precision to the numbers
52956 if(!Array.isArray(text)) text = [];
52957
52958 // make sure showing ticks doesn't accidentally add new categories
52959 // TODO multicategory, if we allow ticktext / tickvals
52960 var tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l;
52961
52962 // array ticks on log axes always show the full number
52963 // (if no explicit ticktext overrides it)
52964 if(ax.type === 'log' && String(ax.dtick).charAt(0) !== 'L') {
52965 ax.dtick = 'L' + Math.pow(10, Math.floor(Math.min(ax.range[0], ax.range[1])) - 1);
52966 }
52967
52968 for(var i = 0; i < vals.length; i++) {
52969 var vali = tickVal2l(vals[i]);
52970 if(vali > tickMin && vali < tickMax) {
52971 if(text[i] === undefined) ticksOut[j] = axes.tickText(ax, vali);
52972 else ticksOut[j] = tickTextObj(ax, vali, String(text[i]));
52973 j++;
52974 }
52975 }
52976
52977 if(j < vals.length) ticksOut.splice(j, vals.length - j);
52978
52979 if(ax.rangebreaks) {
52980 // remove ticks falling inside rangebreaks
52981 ticksOut = ticksOut.filter(function(d) {
52982 return ax.maskBreaks(d.x) !== BADNUM;
52983 });
52984 }
52985
52986 return ticksOut;
52987}
52988
52989var roundBase10 = [2, 5, 10];
52990var roundBase24 = [1, 2, 3, 6, 12];
52991var roundBase60 = [1, 2, 5, 10, 15, 30];
52992// 2&3 day ticks are weird, but need something btwn 1&7
52993var roundDays = [1, 2, 3, 7, 14];
52994// approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2)
52995// these don't have to be exact, just close enough to round to the right value
52996var roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1];
52997var roundLog2 = [-0.301, 0, 0.301, 0.699, 1];
52998// N.B. `thetaunit; 'radians' angular axes must be converted to degrees
52999var roundAngles = [15, 30, 45, 90, 180];
53000
53001function roundDTick(roughDTick, base, roundingSet) {
53002 return base * Lib.roundUp(roughDTick / base, roundingSet);
53003}
53004
53005// autoTicks: calculate best guess at pleasant ticks for this axis
53006// inputs:
53007// ax - an axis object
53008// roughDTick - rough tick spacing (to be turned into a nice round number)
53009// outputs (into ax):
53010// tick0: starting point for ticks (not necessarily on the graph)
53011// usually 0 for numeric (=10^0=1 for log) or jan 1, 2000 for dates
53012// dtick: the actual, nice round tick spacing, usually a little larger than roughDTick
53013// if the ticks are spaced linearly (linear scale, categories,
53014// log with only full powers, date ticks < month),
53015// this will just be a number
53016// months: M#
53017// years: M# where # is 12*number of years
53018// log with linear ticks: L# where # is the linear tick spacing
53019// log showing powers plus some intermediates:
53020// D1 shows all digits, D2 shows 2 and 5
53021axes.autoTicks = function(ax, roughDTick) {
53022 var base;
53023
53024 function getBase(v) {
53025 return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10));
53026 }
53027
53028 if(ax.type === 'date') {
53029 ax.tick0 = Lib.dateTick0(ax.calendar);
53030 // the criteria below are all based on the rough spacing we calculate
53031 // being > half of the final unit - so precalculate twice the rough val
53032 var roughX2 = 2 * roughDTick;
53033
53034 if(roughX2 > ONEAVGYEAR) {
53035 roughDTick /= ONEAVGYEAR;
53036 base = getBase(10);
53037 ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10));
53038 } else if(roughX2 > ONEAVGMONTH) {
53039 roughDTick /= ONEAVGMONTH;
53040 ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24);
53041 } else if(roughX2 > ONEDAY) {
53042 ax.dtick = roundDTick(roughDTick, ONEDAY, ax._hasDayOfWeekBreaks ? [1, 7, 14] : roundDays);
53043
53044 // get week ticks on sunday
53045 // this will also move the base tick off 2000-01-01 if dtick is
53046 // 2 or 3 days... but that's a weird enough case that we'll ignore it.
53047 ax.tick0 = Lib.dateTick0(ax.calendar, true);
53048 } else if(roughX2 > ONEHOUR) {
53049 ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24);
53050 } else if(roughX2 > ONEMIN) {
53051 ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60);
53052 } else if(roughX2 > ONESEC) {
53053 ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60);
53054 } else {
53055 // milliseconds
53056 base = getBase(10);
53057 ax.dtick = roundDTick(roughDTick, base, roundBase10);
53058 }
53059 } else if(ax.type === 'log') {
53060 ax.tick0 = 0;
53061 var rng = Lib.simpleMap(ax.range, ax.r2l);
53062
53063 if(roughDTick > 0.7) {
53064 // only show powers of 10
53065 ax.dtick = Math.ceil(roughDTick);
53066 } else if(Math.abs(rng[1] - rng[0]) < 1) {
53067 // span is less than one power of 10
53068 var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick);
53069
53070 // ticks on a linear scale, labeled fully
53071 roughDTick = Math.abs(Math.pow(10, rng[1]) -
53072 Math.pow(10, rng[0])) / nt;
53073 base = getBase(10);
53074 ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
53075 } else {
53076 // include intermediates between powers of 10,
53077 // labeled with small digits
53078 // ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
53079 ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
53080 }
53081 } else if(ax.type === 'category' || ax.type === 'multicategory') {
53082 ax.tick0 = 0;
53083 ax.dtick = Math.ceil(Math.max(roughDTick, 1));
53084 } else if(isAngular(ax)) {
53085 ax.tick0 = 0;
53086 base = 1;
53087 ax.dtick = roundDTick(roughDTick, base, roundAngles);
53088 } else {
53089 // auto ticks always start at 0
53090 ax.tick0 = 0;
53091 base = getBase(10);
53092 ax.dtick = roundDTick(roughDTick, base, roundBase10);
53093 }
53094
53095 // prevent infinite loops
53096 if(ax.dtick === 0) ax.dtick = 1;
53097
53098 // TODO: this is from log axis histograms with autorange off
53099 if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') {
53100 var olddtick = ax.dtick;
53101 ax.dtick = 1;
53102 throw 'ax.dtick error: ' + String(olddtick);
53103 }
53104};
53105
53106// after dtick is already known, find tickround = precision
53107// to display in tick labels
53108// for numeric ticks, integer # digits after . to round to
53109// for date ticks, the last date part to show (y,m,d,H,M,S)
53110// or an integer # digits past seconds
53111function autoTickRound(ax) {
53112 var dtick = ax.dtick;
53113
53114 ax._tickexponent = 0;
53115 if(!isNumeric(dtick) && typeof dtick !== 'string') {
53116 dtick = 1;
53117 }
53118
53119 if(ax.type === 'category' || ax.type === 'multicategory') {
53120 ax._tickround = null;
53121 }
53122 if(ax.type === 'date') {
53123 // If tick0 is unusual, give tickround a bit more information
53124 // not necessarily *all* the information in tick0 though, if it's really odd
53125 // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
53126 // take off a leading minus (year < 0) and i (intercalary month) so length is consistent
53127 var tick0ms = ax.r2l(ax.tick0);
53128 var tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, '');
53129 var tick0len = tick0str.length;
53130
53131 if(String(dtick).charAt(0) === 'M') {
53132 // any tick0 more specific than a year: alway show the full date
53133 if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd';
53134 // show the month unless ticks are full multiples of a year
53135 else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm';
53136 } else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd';
53137 else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M';
53138 else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S';
53139 else {
53140 // tickround is a number of digits of fractional seconds
53141 // of any two adjacent ticks, at least one will have the maximum fractional digits
53142 // of all possible ticks - so take the max. length of tick0 and the next one
53143 var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length;
53144 ax._tickround = Math.max(tick0len, tick1len) - 20;
53145
53146 // We shouldn't get here... but in case there's a situation I'm
53147 // not thinking of where tick0str and tick1str are identical or
53148 // something, fall back on maximum precision
53149 if(ax._tickround < 0) ax._tickround = 4;
53150 }
53151 } else if(isNumeric(dtick) || dtick.charAt(0) === 'L') {
53152 // linear or log (except D1, D2)
53153 var rng = ax.range.map(ax.r2d || Number);
53154 if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
53155 // 2 digits past largest digit of dtick
53156 ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
53157
53158 var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
53159 var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
53160 if(Math.abs(rangeexp) > 3) {
53161 if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
53162 ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
53163 } else ax._tickexponent = rangeexp;
53164 }
53165 } else {
53166 // D1 or D2 (log)
53167 ax._tickround = null;
53168 }
53169}
53170
53171// months and years don't have constant millisecond values
53172// (but a year is always 12 months so we only need months)
53173// log-scale ticks are also not consistently spaced, except
53174// for pure powers of 10
53175// numeric ticks always have constant differences, other datetime ticks
53176// can all be calculated as constant number of milliseconds
53177axes.tickIncrement = function(x, dtick, axrev, calendar) {
53178 var axSign = axrev ? -1 : 1;
53179
53180 // includes linear, all dates smaller than month, and pure 10^n in log
53181 if(isNumeric(dtick)) return x + axSign * dtick;
53182
53183 // everything else is a string, one character plus a number
53184 var tType = dtick.charAt(0);
53185 var dtSigned = axSign * Number(dtick.substr(1));
53186
53187 // Dates: months (or years - see Lib.incrementMonth)
53188 if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
53189
53190 // Log scales: Linear, Digits
53191 else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
53192
53193 // log10 of 2,5,10, or all digits (logs just have to be
53194 // close enough to round)
53195 else if(tType === 'D') {
53196 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
53197 var x2 = x + axSign * 0.01;
53198 var frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
53199
53200 return Math.floor(x2) +
53201 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
53202 } else throw 'unrecognized dtick ' + String(dtick);
53203};
53204
53205// calculate the first tick on an axis
53206axes.tickFirst = function(ax) {
53207 var r2l = ax.r2l || Number;
53208 var rng = Lib.simpleMap(ax.range, r2l);
53209 var axrev = rng[1] < rng[0];
53210 var sRound = axrev ? Math.floor : Math.ceil;
53211 // add a tiny extra bit to make sure we get ticks
53212 // that may have been rounded out
53213 var r0 = rng[0] * 1.0001 - rng[1] * 0.0001;
53214 var dtick = ax.dtick;
53215 var tick0 = r2l(ax.tick0);
53216
53217 if(isNumeric(dtick)) {
53218 var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0;
53219
53220 // make sure no ticks outside the category list
53221 if(ax.type === 'category' || ax.type === 'multicategory') {
53222 tmin = Lib.constrain(tmin, 0, ax._categories.length - 1);
53223 }
53224 return tmin;
53225 }
53226
53227 var tType = dtick.charAt(0);
53228 var dtNum = Number(dtick.substr(1));
53229
53230 // Dates: months (or years)
53231 if(tType === 'M') {
53232 var cnt = 0;
53233 var t0 = tick0;
53234 var t1, mult, newDTick;
53235
53236 // This algorithm should work for *any* nonlinear (but close to linear!)
53237 // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3.
53238 while(cnt < 10) {
53239 t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar);
53240 if((t1 - r0) * (t0 - r0) <= 0) {
53241 // t1 and t0 are on opposite sides of r0! we've succeeded!
53242 if(axrev) return Math.min(t0, t1);
53243 return Math.max(t0, t1);
53244 }
53245 mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0);
53246 newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum);
53247 t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar);
53248 cnt++;
53249 }
53250 Lib.error('tickFirst did not converge', ax);
53251 return t0;
53252 } else if(tType === 'L') {
53253 // Log scales: Linear, Digits
53254
53255 return Math.log(sRound(
53256 (Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10;
53257 } else if(tType === 'D') {
53258 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
53259 var frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev);
53260
53261 return Math.floor(r0) +
53262 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
53263 } else throw 'unrecognized dtick ' + String(dtick);
53264};
53265
53266// draw the text for one tick.
53267// px,py are the location on gd.paper
53268// prefix is there so the x axis ticks can be dropped a line
53269// ax is the axis layout, x is the tick value
53270// hover is a (truthy) flag for whether to show numbers with a bit
53271// more precision for hovertext
53272axes.tickText = function(ax, x, hover, noSuffixPrefix) {
53273 var out = tickTextObj(ax, x);
53274 var arrayMode = ax.tickmode === 'array';
53275 var extraPrecision = hover || arrayMode;
53276 var axType = ax.type;
53277 // TODO multicategory, if we allow ticktext / tickvals
53278 var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l;
53279 var i;
53280
53281 if(arrayMode && Array.isArray(ax.ticktext)) {
53282 var rng = Lib.simpleMap(ax.range, ax.r2l);
53283 var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000;
53284
53285 for(i = 0; i < ax.ticktext.length; i++) {
53286 if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
53287 }
53288 if(i < ax.ticktext.length) {
53289 out.text = String(ax.ticktext[i]);
53290 return out;
53291 }
53292 }
53293
53294 function isHidden(showAttr) {
53295 if(showAttr === undefined) return true;
53296 if(hover) return showAttr === 'none';
53297
53298 var firstOrLast = {
53299 first: ax._tmin,
53300 last: ax._tmax
53301 }[showAttr];
53302
53303 return showAttr !== 'all' && x !== firstOrLast;
53304 }
53305
53306 var hideexp = hover ?
53307 'never' :
53308 ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : '';
53309
53310 if(axType === 'date') formatDate(ax, out, hover, extraPrecision);
53311 else if(axType === 'log') formatLog(ax, out, hover, extraPrecision, hideexp);
53312 else if(axType === 'category') formatCategory(ax, out);
53313 else if(axType === 'multicategory') formatMultiCategory(ax, out, hover);
53314 else if(isAngular(ax)) formatAngle(ax, out, hover, extraPrecision, hideexp);
53315 else formatLinear(ax, out, hover, extraPrecision, hideexp);
53316
53317 // add prefix and suffix
53318 if(!noSuffixPrefix) {
53319 if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text;
53320 if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix;
53321 }
53322
53323 // Setup ticks and grid lines boundaries
53324 // at 1/2 a 'category' to the left/bottom
53325 if(ax.tickson === 'boundaries' || ax.showdividers) {
53326 var inbounds = function(v) {
53327 var p = ax.l2p(v);
53328 return p >= 0 && p <= ax._length ? v : null;
53329 };
53330
53331 out.xbnd = [
53332 inbounds(out.x - 0.5),
53333 inbounds(out.x + ax.dtick - 0.5)
53334 ];
53335 }
53336
53337 return out;
53338};
53339
53340/**
53341 * create text for a hover label on this axis, with special handling of
53342 * log axes (where negative values can't be displayed but can appear in hover text)
53343 *
53344 * @param {object} ax: the axis to format text for
53345 * @param {number} val: calcdata value to format
53346 * @param {Optional(number)} val2: a second value to display
53347 *
53348 * @returns {string} `val` formatted as a string appropriate to this axis, or
53349 * `val` and `val2` as a range (ie '<val> - <val2>') if `val2` is provided and
53350 * it's different from `val`.
53351 */
53352axes.hoverLabelText = function(ax, val, val2) {
53353 if(val2 !== BADNUM && val2 !== val) {
53354 return axes.hoverLabelText(ax, val) + ' - ' + axes.hoverLabelText(ax, val2);
53355 }
53356
53357 var logOffScale = (ax.type === 'log' && val <= 0);
53358 var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text;
53359
53360 if(logOffScale) {
53361 return val === 0 ? '0' : MINUS_SIGN + tx;
53362 }
53363
53364 // TODO: should we do something special if the axis calendar and
53365 // the data calendar are different? Somehow display both dates with
53366 // their system names? Right now it will just display in the axis calendar
53367 // but users could add the other one as text.
53368 return tx;
53369};
53370
53371function tickTextObj(ax, x, text) {
53372 var tf = ax.tickfont || {};
53373
53374 return {
53375 x: x,
53376 dx: 0,
53377 dy: 0,
53378 text: text || '',
53379 fontSize: tf.size,
53380 font: tf.family,
53381 fontColor: tf.color
53382 };
53383}
53384
53385function formatDate(ax, out, hover, extraPrecision) {
53386 var tr = ax._tickround;
53387 var fmt = (hover && ax.hoverformat) || axes.getTickFormat(ax);
53388
53389 if(extraPrecision) {
53390 // second or sub-second precision: extra always shows max digits.
53391 // for other fields, extra precision just adds one field.
53392 if(isNumeric(tr)) tr = 4;
53393 else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr];
53394 }
53395
53396 var dateStr = Lib.formatDate(out.x, fmt, tr, ax._dateFormat, ax.calendar, ax._extraFormat);
53397 var headStr;
53398
53399 var splitIndex = dateStr.indexOf('\n');
53400 if(splitIndex !== -1) {
53401 headStr = dateStr.substr(splitIndex + 1);
53402 dateStr = dateStr.substr(0, splitIndex);
53403 }
53404
53405 if(extraPrecision) {
53406 // if extraPrecision led to trailing zeros, strip them off
53407 // actually, this can lead to removing even more zeros than
53408 // in the original rounding, but that's fine because in these
53409 // contexts uniformity is not so important (if there's even
53410 // anything to be uniform with!)
53411
53412 // can we remove the whole time part?
53413 if(dateStr === '00:00:00' || dateStr === '00:00') {
53414 dateStr = headStr;
53415 headStr = '';
53416 } else if(dateStr.length === 8) {
53417 // strip off seconds if they're zero (zero fractional seconds
53418 // are already omitted)
53419 // but we never remove minutes and leave just hours
53420 dateStr = dateStr.replace(/:00$/, '');
53421 }
53422 }
53423
53424 if(headStr) {
53425 if(hover) {
53426 // hover puts it all on one line, so headPart works best up front
53427 // except for year headPart: turn this into "Jan 1, 2000" etc.
53428 if(tr === 'd') dateStr += ', ' + headStr;
53429 else dateStr = headStr + (dateStr ? ', ' + dateStr : '');
53430 } else if(!ax._inCalcTicks || (headStr !== ax._prevDateHead)) {
53431 dateStr += '<br>' + headStr;
53432 ax._prevDateHead = headStr;
53433 }
53434 }
53435
53436 out.text = dateStr;
53437}
53438
53439function formatLog(ax, out, hover, extraPrecision, hideexp) {
53440 var dtick = ax.dtick;
53441 var x = out.x;
53442 var tickformat = ax.tickformat;
53443 var dtChar0 = typeof dtick === 'string' && dtick.charAt(0);
53444
53445 if(hideexp === 'never') {
53446 // If this is a hover label, then we must *never* hide the exponent
53447 // for the sake of display, which could give the wrong value by
53448 // potentially many orders of magnitude. If hideexp was 'never', then
53449 // it's now succeeded by preventing the other condition from automating
53450 // this choice. Thus we can unset it so that the axis formatting takes
53451 // precedence.
53452 hideexp = '';
53453 }
53454
53455 if(extraPrecision && (dtChar0 !== 'L')) {
53456 dtick = 'L3';
53457 dtChar0 = 'L';
53458 }
53459
53460 if(tickformat || (dtChar0 === 'L')) {
53461 out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
53462 } else if(isNumeric(dtick) || ((dtChar0 === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
53463 var p = Math.round(x);
53464 var absP = Math.abs(p);
53465 var exponentFormat = ax.exponentformat;
53466 if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
53467 if(p === 0) out.text = 1;
53468 else if(p === 1) out.text = '10';
53469 else out.text = '10<sup>' + (p > 1 ? '' : MINUS_SIGN) + absP + '</sup>';
53470
53471 out.fontSize *= 1.25;
53472 } else if((exponentFormat === 'e' || exponentFormat === 'E') && absP > 2) {
53473 out.text = '1' + exponentFormat + (p > 0 ? '+' : MINUS_SIGN) + absP;
53474 } else {
53475 out.text = numFormat(Math.pow(10, x), ax, '', 'fakehover');
53476 if(dtick === 'D1' && ax._id.charAt(0) === 'y') {
53477 out.dy -= out.fontSize / 6;
53478 }
53479 }
53480 } else if(dtChar0 === 'D') {
53481 out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1))));
53482 out.fontSize *= 0.75;
53483 } else throw 'unrecognized dtick ' + String(dtick);
53484
53485 // if 9's are printed on log scale, move the 10's away a bit
53486 if(ax.dtick === 'D1') {
53487 var firstChar = String(out.text).charAt(0);
53488 if(firstChar === '0' || firstChar === '1') {
53489 if(ax._id.charAt(0) === 'y') {
53490 out.dx -= out.fontSize / 4;
53491 } else {
53492 out.dy += out.fontSize / 2;
53493 out.dx += (ax.range[1] > ax.range[0] ? 1 : -1) *
53494 out.fontSize * (x < 0 ? 0.5 : 0.25);
53495 }
53496 }
53497 }
53498}
53499
53500function formatCategory(ax, out) {
53501 var tt = ax._categories[Math.round(out.x)];
53502 if(tt === undefined) tt = '';
53503 out.text = String(tt);
53504}
53505
53506function formatMultiCategory(ax, out, hover) {
53507 var v = Math.round(out.x);
53508 var cats = ax._categories[v] || [];
53509 var tt = cats[1] === undefined ? '' : String(cats[1]);
53510 var tt2 = cats[0] === undefined ? '' : String(cats[0]);
53511
53512 if(hover) {
53513 // TODO is this what we want?
53514 out.text = tt2 + ' - ' + tt;
53515 } else {
53516 // setup for secondary labels
53517 out.text = tt;
53518 out.text2 = tt2;
53519 }
53520}
53521
53522function formatLinear(ax, out, hover, extraPrecision, hideexp) {
53523 if(hideexp === 'never') {
53524 // If this is a hover label, then we must *never* hide the exponent
53525 // for the sake of display, which could give the wrong value by
53526 // potentially many orders of magnitude. If hideexp was 'never', then
53527 // it's now succeeded by preventing the other condition from automating
53528 // this choice. Thus we can unset it so that the axis formatting takes
53529 // precedence.
53530 hideexp = '';
53531 } else if(ax.showexponent === 'all' && Math.abs(out.x / ax.dtick) < 1e-6) {
53532 // don't add an exponent to zero if we're showing all exponents
53533 // so the only reason you'd show an exponent on zero is if it's the
53534 // ONLY tick to get an exponent (first or last)
53535 hideexp = 'hide';
53536 }
53537 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
53538}
53539
53540function formatAngle(ax, out, hover, extraPrecision, hideexp) {
53541 if(ax.thetaunit === 'radians' && !hover) {
53542 var num = out.x / 180;
53543
53544 if(num === 0) {
53545 out.text = '0';
53546 } else {
53547 var frac = num2frac(num);
53548
53549 if(frac[1] >= 100) {
53550 out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision);
53551 } else {
53552 var isNeg = out.x < 0;
53553
53554 if(frac[1] === 1) {
53555 if(frac[0] === 1) out.text = 'π';
53556 else out.text = frac[0] + 'π';
53557 } else {
53558 out.text = [
53559 '<sup>', frac[0], '</sup>',
53560 '⁄',
53561 '<sub>', frac[1], '</sub>',
53562 'π'
53563 ].join('');
53564 }
53565
53566 if(isNeg) out.text = MINUS_SIGN + out.text;
53567 }
53568 }
53569 } else {
53570 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
53571 }
53572}
53573
53574// inspired by
53575// https://github.com/yisibl/num2fraction/blob/master/index.js
53576function num2frac(num) {
53577 function almostEq(a, b) {
53578 return Math.abs(a - b) <= 1e-6;
53579 }
53580
53581 function findGCD(a, b) {
53582 return almostEq(b, 0) ? a : findGCD(b, a % b);
53583 }
53584
53585 function findPrecision(n) {
53586 var e = 1;
53587 while(!almostEq(Math.round(n * e) / e, n)) {
53588 e *= 10;
53589 }
53590 return e;
53591 }
53592
53593 var precision = findPrecision(num);
53594 var number = num * precision;
53595 var gcd = Math.abs(findGCD(number, precision));
53596
53597 return [
53598 // numerator
53599 Math.round(number / gcd),
53600 // denominator
53601 Math.round(precision / gcd)
53602 ];
53603}
53604
53605// format a number (tick value) according to the axis settings
53606// new, more reliable procedure than d3.round or similar:
53607// add half the rounding increment, then stringify and truncate
53608// also automatically switch to sci. notation
53609var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
53610
53611function isSIFormat(exponentFormat) {
53612 return exponentFormat === 'SI' || exponentFormat === 'B';
53613}
53614
53615// are we beyond the range of common SI prefixes?
53616// 10^-16 -> 1x10^-16
53617// 10^-15 -> 1f
53618// ...
53619// 10^14 -> 100T
53620// 10^15 -> 1x10^15
53621// 10^16 -> 1x10^16
53622function beyondSI(exponent) {
53623 return exponent > 14 || exponent < -15;
53624}
53625
53626function numFormat(v, ax, fmtoverride, hover) {
53627 var isNeg = v < 0;
53628 // max number of digits past decimal point to show
53629 var tickRound = ax._tickround;
53630 var exponentFormat = fmtoverride || ax.exponentformat || 'B';
53631 var exponent = ax._tickexponent;
53632 var tickformat = axes.getTickFormat(ax);
53633 var separatethousands = ax.separatethousands;
53634
53635 // special case for hover: set exponent just for this value, and
53636 // add a couple more digits of precision over tick labels
53637 if(hover) {
53638 // make a dummy axis obj to get the auto rounding and exponent
53639 var ah = {
53640 exponentformat: exponentFormat,
53641 dtick: ax.showexponent === 'none' ? ax.dtick :
53642 (isNumeric(v) ? Math.abs(v) || 1 : 1),
53643 // if not showing any exponents, don't change the exponent
53644 // from what we calculate
53645 range: ax.showexponent === 'none' ? ax.range.map(ax.r2d) : [0, v || 1]
53646 };
53647 autoTickRound(ah);
53648 tickRound = (Number(ah._tickround) || 0) + 4;
53649 exponent = ah._tickexponent;
53650 if(ax.hoverformat) tickformat = ax.hoverformat;
53651 }
53652
53653 if(tickformat) return ax._numFormat(tickformat)(v).replace(/-/g, MINUS_SIGN);
53654
53655 // 'epsilon' - rounding increment
53656 var e = Math.pow(10, -tickRound) / 2;
53657
53658 // exponentFormat codes:
53659 // 'e' (1.2e+6, default)
53660 // 'E' (1.2E+6)
53661 // 'SI' (1.2M)
53662 // 'B' (same as SI except 10^9=B not G)
53663 // 'none' (1200000)
53664 // 'power' (1.2x10^6)
53665 // 'hide' (1.2, use 3rd argument=='hide' to eg
53666 // only show exponent on last tick)
53667 if(exponentFormat === 'none') exponent = 0;
53668
53669 // take the sign out, put it back manually at the end
53670 // - makes cases easier
53671 v = Math.abs(v);
53672 if(v < e) {
53673 // 0 is just 0, but may get exponent if it's the last tick
53674 v = '0';
53675 isNeg = false;
53676 } else {
53677 v += e;
53678 // take out a common exponent, if any
53679 if(exponent) {
53680 v *= Math.pow(10, -exponent);
53681 tickRound += exponent;
53682 }
53683 // round the mantissa
53684 if(tickRound === 0) v = String(Math.floor(v));
53685 else if(tickRound < 0) {
53686 v = String(Math.round(v));
53687 v = v.substr(0, v.length + tickRound);
53688 for(var i = tickRound; i < 0; i++) v += '0';
53689 } else {
53690 v = String(v);
53691 var dp = v.indexOf('.') + 1;
53692 if(dp) v = v.substr(0, dp + tickRound).replace(/\.?0+$/, '');
53693 }
53694 // insert appropriate decimal point and thousands separator
53695 v = Lib.numSeparate(v, ax._separators, separatethousands);
53696 }
53697
53698 // add exponent
53699 if(exponent && exponentFormat !== 'hide') {
53700 if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
53701
53702 var signedExponent;
53703 if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
53704 else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
53705 else signedExponent = String(exponent);
53706
53707 if(exponentFormat === 'e' || exponentFormat === 'E') {
53708 v += exponentFormat + signedExponent;
53709 } else if(exponentFormat === 'power') {
53710 v += '×10<sup>' + signedExponent + '</sup>';
53711 } else if(exponentFormat === 'B' && exponent === 9) {
53712 v += 'B';
53713 } else if(isSIFormat(exponentFormat)) {
53714 v += SIPREFIXES[exponent / 3 + 5];
53715 }
53716 }
53717
53718 // put sign back in and return
53719 // replace standard minus character (which is technically a hyphen)
53720 // with a true minus sign
53721 if(isNeg) return MINUS_SIGN + v;
53722 return v;
53723}
53724
53725axes.getTickFormat = function(ax) {
53726 var i;
53727
53728 function convertToMs(dtick) {
53729 return typeof dtick !== 'string' ? dtick : Number(dtick.replace('M', '')) * ONEAVGMONTH;
53730 }
53731
53732 function compareLogTicks(left, right) {
53733 var priority = ['L', 'D'];
53734 if(typeof left === typeof right) {
53735 if(typeof left === 'number') {
53736 return left - right;
53737 } else {
53738 var leftPriority = priority.indexOf(left.charAt(0));
53739 var rightPriority = priority.indexOf(right.charAt(0));
53740 if(leftPriority === rightPriority) {
53741 return Number(left.replace(/(L|D)/g, '')) - Number(right.replace(/(L|D)/g, ''));
53742 } else {
53743 return leftPriority - rightPriority;
53744 }
53745 }
53746 } else {
53747 return typeof left === 'number' ? 1 : -1;
53748 }
53749 }
53750
53751 function isProperStop(dtick, range, convert) {
53752 var convertFn = convert || function(x) { return x;};
53753 var leftDtick = range[0];
53754 var rightDtick = range[1];
53755 return ((!leftDtick && typeof leftDtick !== 'number') || convertFn(leftDtick) <= convertFn(dtick)) &&
53756 ((!rightDtick && typeof rightDtick !== 'number') || convertFn(rightDtick) >= convertFn(dtick));
53757 }
53758
53759 function isProperLogStop(dtick, range) {
53760 var isLeftDtickNull = range[0] === null;
53761 var isRightDtickNull = range[1] === null;
53762 var isDtickInRangeLeft = compareLogTicks(dtick, range[0]) >= 0;
53763 var isDtickInRangeRight = compareLogTicks(dtick, range[1]) <= 0;
53764 return (isLeftDtickNull || isDtickInRangeLeft) && (isRightDtickNull || isDtickInRangeRight);
53765 }
53766
53767 var tickstop, stopi;
53768 if(ax.tickformatstops && ax.tickformatstops.length > 0) {
53769 switch(ax.type) {
53770 case 'date':
53771 case 'linear': {
53772 for(i = 0; i < ax.tickformatstops.length; i++) {
53773 stopi = ax.tickformatstops[i];
53774 if(stopi.enabled && isProperStop(ax.dtick, stopi.dtickrange, convertToMs)) {
53775 tickstop = stopi;
53776 break;
53777 }
53778 }
53779 break;
53780 }
53781 case 'log': {
53782 for(i = 0; i < ax.tickformatstops.length; i++) {
53783 stopi = ax.tickformatstops[i];
53784 if(stopi.enabled && isProperLogStop(ax.dtick, stopi.dtickrange)) {
53785 tickstop = stopi;
53786 break;
53787 }
53788 }
53789 break;
53790 }
53791 default:
53792 }
53793 }
53794 return tickstop ? tickstop.value : ax.tickformat;
53795};
53796
53797// getSubplots - extract all subplot IDs we need
53798// as an array of items like 'xy', 'x2y', 'x2y2'...
53799// sorted by x (x,x2,x3...) then y
53800// optionally restrict to only subplots containing axis object ax
53801//
53802// NOTE: this is currently only used OUTSIDE plotly.js (toolpanel, webapp)
53803// ideally we get rid of it there (or just copy this there) and remove it here
53804axes.getSubplots = function(gd, ax) {
53805 var subplotObj = gd._fullLayout._subplots;
53806 var allSubplots = subplotObj.cartesian.concat(subplotObj.gl2d || []);
53807
53808 var out = ax ? axes.findSubplotsWithAxis(allSubplots, ax) : allSubplots;
53809
53810 out.sort(function(a, b) {
53811 var aParts = a.substr(1).split('y');
53812 var bParts = b.substr(1).split('y');
53813
53814 if(aParts[0] === bParts[0]) return +aParts[1] - +bParts[1];
53815 return +aParts[0] - +bParts[0];
53816 });
53817
53818 return out;
53819};
53820
53821// find all subplots with axis 'ax'
53822// NOTE: this is only used in axes.getSubplots (only used outside plotly.js) and
53823// gl2d/convert (where it restricts axis subplots to only those with gl2d)
53824axes.findSubplotsWithAxis = function(subplots, ax) {
53825 var axMatch = new RegExp(
53826 (ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
53827 );
53828 var subplotsWithAx = [];
53829
53830 for(var i = 0; i < subplots.length; i++) {
53831 var sp = subplots[i];
53832 if(axMatch.test(sp)) subplotsWithAx.push(sp);
53833 }
53834
53835 return subplotsWithAx;
53836};
53837
53838// makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings
53839axes.makeClipPaths = function(gd) {
53840 var fullLayout = gd._fullLayout;
53841
53842 // for more info: https://github.com/plotly/plotly.js/issues/2595
53843 if(fullLayout._hasOnlyLargeSploms) return;
53844
53845 var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''};
53846 var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''};
53847 var xaList = axes.list(gd, 'x', true);
53848 var yaList = axes.list(gd, 'y', true);
53849 var clipList = [];
53850 var i, j;
53851
53852 for(i = 0; i < xaList.length; i++) {
53853 clipList.push({x: xaList[i], y: fullHeight});
53854 for(j = 0; j < yaList.length; j++) {
53855 if(i === 0) clipList.push({x: fullWidth, y: yaList[j]});
53856 clipList.push({x: xaList[i], y: yaList[j]});
53857 }
53858 }
53859
53860 // selectors don't work right with camelCase tags,
53861 // have to use class instead
53862 // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
53863 var axClips = fullLayout._clips.selectAll('.axesclip')
53864 .data(clipList, function(d) { return d.x._id + d.y._id; });
53865
53866 axClips.enter().append('clipPath')
53867 .classed('axesclip', true)
53868 .attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; })
53869 .append('rect');
53870
53871 axClips.exit().remove();
53872
53873 axClips.each(function(d) {
53874 d3.select(this).select('rect').attr({
53875 x: d.x._offset || 0,
53876 y: d.y._offset || 0,
53877 width: d.x._length || 1,
53878 height: d.y._length || 1
53879 });
53880 });
53881};
53882
53883/**
53884 * Main multi-axis drawing routine!
53885 *
53886 * @param {DOM element} gd : graph div
53887 * @param {string or array of strings} arg : polymorphic argument
53888 * @param {object} opts:
53889 * - @param {boolean} skipTitle : optional flag to skip axis title draw/update
53890 *
53891 * Signature 1: Axes.draw(gd, 'redraw')
53892 * use this to clear and redraw all axes on graph
53893 *
53894 * Signature 2: Axes.draw(gd, '')
53895 * use this to draw all axes on graph w/o the selectAll().remove()
53896 * of the 'redraw' signature
53897 *
53898 * Signature 3: Axes.draw(gd, [axId, axId2, ...])
53899 * where the items are axis id string,
53900 * use this to update multiple axes in one call
53901 *
53902 * N.B draw updates:
53903 * - ax._r (stored range for use by zoom/pan)
53904 * - ax._rl (stored linearized range for use by zoom/pan)
53905 */
53906axes.draw = function(gd, arg, opts) {
53907 var fullLayout = gd._fullLayout;
53908
53909 if(arg === 'redraw') {
53910 fullLayout._paper.selectAll('g.subplot').each(function(d) {
53911 var id = d[0];
53912 var plotinfo = fullLayout._plots[id];
53913 var xa = plotinfo.xaxis;
53914 var ya = plotinfo.yaxis;
53915
53916 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
53917 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
53918 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove();
53919 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick2').remove();
53920 plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove();
53921 plotinfo.yaxislayer.selectAll('.' + ya._id + 'divider').remove();
53922
53923 if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
53924 if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
53925
53926 fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
53927 fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
53928 });
53929 }
53930
53931 var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;
53932
53933 return Lib.syncOrAsync(axList.map(function(axId) {
53934 return function() {
53935 if(!axId) return;
53936
53937 var ax = axes.getFromId(gd, axId);
53938 var axDone = axes.drawOne(gd, ax, opts);
53939
53940 ax._r = ax.range.slice();
53941 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
53942
53943 return axDone;
53944 };
53945 }));
53946};
53947
53948/**
53949 * Draw one cartesian axis
53950 *
53951 * @param {DOM element} gd
53952 * @param {object} ax (full) axis object
53953 * @param {object} opts
53954 * - @param {boolean} skipTitle (set to true to skip axis title draw call)
53955 *
53956 * Depends on:
53957 * - ax._mainSubplot (from linkSubplots)
53958 * - ax._mainAxis
53959 * - ax._anchorAxis
53960 * - ax._subplotsWith
53961 * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots)
53962 * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults)
53963 * - ax._mainLinePosition (from lsInner)
53964 * - ax._mainMirrorPosition
53965 * - ax._linepositions
53966 *
53967 * Fills in:
53968 * - ax._vals:
53969 * - ax._gridVals:
53970 * - ax._selections:
53971 * - ax._tickAngles:
53972 * - ax._depth (when required only):
53973 * - and calls ax.setScale
53974 */
53975axes.drawOne = function(gd, ax, opts) {
53976 opts = opts || {};
53977
53978 var i, sp, plotinfo;
53979
53980 ax.setScale();
53981
53982 var fullLayout = gd._fullLayout;
53983 var axId = ax._id;
53984 var axLetter = axId.charAt(0);
53985 var counterLetter = axes.counterLetter(axId);
53986 var mainPlotinfo = fullLayout._plots[ax._mainSubplot];
53987
53988 // this happens when updating matched group with 'missing' axes
53989 if(!mainPlotinfo) return;
53990
53991 var mainAxLayer = mainPlotinfo[axLetter + 'axislayer'];
53992 var mainLinePosition = ax._mainLinePosition;
53993 var mainMirrorPosition = ax._mainMirrorPosition;
53994
53995 var vals = ax._vals = axes.calcTicks(ax);
53996
53997 // Add a couple of axis properties that should cause us to recreate
53998 // elements. Used in d3 data function.
53999 var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_');
54000 for(i = 0; i < vals.length; i++) {
54001 vals[i].axInfo = axInfo;
54002 }
54003
54004 // stash selections to avoid DOM queries e.g.
54005 // - stash tickLabels selection, so that drawTitle can use it to scoot title
54006 ax._selections = {};
54007 // stash tick angle (including the computed 'auto' values) per tick-label class
54008 // linkup 'previous' tick angles on redraws
54009 if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles;
54010 ax._tickAngles = {};
54011 // measure [in px] between axis position and outward-most part of bounding box
54012 // (touching either the tick label or ticks)
54013 // depth can be expansive to compute, so we only do so when required
54014 ax._depth = null;
54015
54016 // calcLabelLevelBbox can be expensive,
54017 // so make sure to not call it twice during the same Axes.drawOne call
54018 // by stashing label-level bounding boxes per tick-label class
54019 var llbboxes = {};
54020 function getLabelLevelBbox(suffix) {
54021 var cls = axId + (suffix || 'tick');
54022 if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls);
54023 return llbboxes[cls];
54024 }
54025
54026 if(!ax.visible) return;
54027
54028 var transFn = axes.makeTransFn(ax);
54029 var tickVals;
54030 // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
54031 // The key case here is removing zero lines when the axis bound is zero
54032 var valsClipped;
54033
54034 if(ax.tickson === 'boundaries') {
54035 var boundaryVals = getBoundaryVals(ax, vals);
54036 valsClipped = axes.clipEnds(ax, boundaryVals);
54037 tickVals = ax.ticks === 'inside' ? valsClipped : boundaryVals;
54038 } else {
54039 valsClipped = axes.clipEnds(ax, vals);
54040 tickVals = ax.ticks === 'inside' ? valsClipped : vals;
54041 }
54042
54043 var gridVals = ax._gridVals = valsClipped;
54044 var dividerVals = getDividerVals(ax, vals);
54045
54046 if(!fullLayout._hasOnlyLargeSploms) {
54047 var subplotsWithAx = ax._subplotsWith;
54048
54049 // keep track of which subplots (by main counter axis) we've already
54050 // drawn grids for, so we don't overdraw overlaying subplots
54051 var finishedGrids = {};
54052
54053 for(i = 0; i < subplotsWithAx.length; i++) {
54054 sp = subplotsWithAx[i];
54055 plotinfo = fullLayout._plots[sp];
54056
54057 var counterAxis = plotinfo[counterLetter + 'axis'];
54058 var mainCounterID = counterAxis._mainAxis._id;
54059 if(finishedGrids[mainCounterID]) continue;
54060 finishedGrids[mainCounterID] = 1;
54061
54062 var gridPath = axLetter === 'x' ?
54063 'M0,' + counterAxis._offset + 'v' + counterAxis._length :
54064 'M' + counterAxis._offset + ',0h' + counterAxis._length;
54065
54066 axes.drawGrid(gd, ax, {
54067 vals: gridVals,
54068 counterAxis: counterAxis,
54069 layer: plotinfo.gridlayer.select('.' + axId),
54070 path: gridPath,
54071 transFn: transFn
54072 });
54073 axes.drawZeroLine(gd, ax, {
54074 counterAxis: counterAxis,
54075 layer: plotinfo.zerolinelayer,
54076 path: gridPath,
54077 transFn: transFn
54078 });
54079 }
54080 }
54081
54082 var tickSigns = axes.getTickSigns(ax);
54083 var tickSubplots = [];
54084
54085 if(ax.ticks) {
54086 var mainTickPath = axes.makeTickPath(ax, mainLinePosition, tickSigns[2]);
54087 var mirrorTickPath;
54088 var fullTickPath;
54089 if(ax._anchorAxis && ax.mirror && ax.mirror !== true) {
54090 mirrorTickPath = axes.makeTickPath(ax, mainMirrorPosition, tickSigns[3]);
54091 fullTickPath = mainTickPath + mirrorTickPath;
54092 } else {
54093 mirrorTickPath = '';
54094 fullTickPath = mainTickPath;
54095 }
54096
54097 var tickPath;
54098 if(ax.showdividers && ax.ticks === 'outside' && ax.tickson === 'boundaries') {
54099 var dividerLookup = {};
54100 for(i = 0; i < dividerVals.length; i++) {
54101 dividerLookup[dividerVals[i].x] = 1;
54102 }
54103 tickPath = function(d) {
54104 return dividerLookup[d.x] ? mirrorTickPath : fullTickPath;
54105 };
54106 } else {
54107 tickPath = fullTickPath;
54108 }
54109
54110 axes.drawTicks(gd, ax, {
54111 vals: tickVals,
54112 layer: mainAxLayer,
54113 path: tickPath,
54114 transFn: transFn
54115 });
54116
54117 if(ax.mirror === 'allticks') {
54118 tickSubplots = Object.keys(ax._linepositions || {});
54119 }
54120 }
54121
54122 for(i = 0; i < tickSubplots.length; i++) {
54123 sp = tickSubplots[i];
54124 plotinfo = fullLayout._plots[sp];
54125 // [bottom or left, top or right], free and main are handled above
54126 var linepositions = ax._linepositions[sp] || [];
54127 var spTickPath = axes.makeTickPath(ax, linepositions[0], tickSigns[0]) +
54128 axes.makeTickPath(ax, linepositions[1], tickSigns[1]);
54129
54130 axes.drawTicks(gd, ax, {
54131 vals: tickVals,
54132 layer: plotinfo[axLetter + 'axislayer'],
54133 path: spTickPath,
54134 transFn: transFn
54135 });
54136 }
54137
54138 var seq = [];
54139
54140 // tick labels - for now just the main labels.
54141 // TODO: mirror labels, esp for subplots
54142
54143 seq.push(function() {
54144 return axes.drawLabels(gd, ax, {
54145 vals: vals,
54146 layer: mainAxLayer,
54147 transFn: transFn,
54148 labelFns: axes.makeLabelFns(ax, mainLinePosition)
54149 });
54150 });
54151
54152 if(ax.type === 'multicategory') {
54153 var pad = {x: 2, y: 10}[axLetter];
54154
54155 seq.push(function() {
54156 var bboxKey = {x: 'height', y: 'width'}[axLetter];
54157 var standoff = getLabelLevelBbox()[bboxKey] + pad +
54158 (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0);
54159
54160 return axes.drawLabels(gd, ax, {
54161 vals: getSecondaryLabelVals(ax, vals),
54162 layer: mainAxLayer,
54163 cls: axId + 'tick2',
54164 repositionOnUpdate: true,
54165 secondary: true,
54166 transFn: transFn,
54167 labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4])
54168 });
54169 });
54170
54171 seq.push(function() {
54172 ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition);
54173
54174 return drawDividers(gd, ax, {
54175 vals: dividerVals,
54176 layer: mainAxLayer,
54177 path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth),
54178 transFn: transFn
54179 });
54180 });
54181 } else if(ax.title.hasOwnProperty('standoff')) {
54182 seq.push(function() {
54183 ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition);
54184 });
54185 }
54186
54187 var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax);
54188
54189 seq.push(function() {
54190 var s = ax.side.charAt(0);
54191 var sMirror = OPPOSITE_SIDE[ax.side].charAt(0);
54192 var pos = axes.getPxPosition(gd, ax);
54193 var outsideTickLen = ax.ticks === 'outside' ? ax.ticklen : 0;
54194 var llbbox;
54195
54196 var push;
54197 var mirrorPush;
54198 var rangeSliderPush;
54199
54200 if(ax.automargin || hasRangeSlider) {
54201 if(ax.type === 'multicategory') {
54202 llbbox = getLabelLevelBbox('tick2');
54203 } else {
54204 llbbox = getLabelLevelBbox();
54205 if(axLetter === 'x' && s === 'b') {
54206 ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen);
54207 }
54208 }
54209 }
54210
54211 if(ax.automargin) {
54212 push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
54213 var domainIndices = [0, 1];
54214
54215 if(axLetter === 'x') {
54216 if(s === 'b') {
54217 push[s] = ax._depth;
54218 } else {
54219 push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen);
54220 domainIndices.reverse();
54221 }
54222
54223 if(llbbox.width > 0) {
54224 var rExtra = llbbox.right - (ax._offset + ax._length);
54225 if(rExtra > 0) {
54226 push.xr = 1;
54227 push.r = rExtra;
54228 }
54229 var lExtra = ax._offset - llbbox.left;
54230 if(lExtra > 0) {
54231 push.xl = 0;
54232 push.l = lExtra;
54233 }
54234 }
54235 } else {
54236 if(s === 'l') {
54237 push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen);
54238 } else {
54239 push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen);
54240 domainIndices.reverse();
54241 }
54242
54243 if(llbbox.height > 0) {
54244 var bExtra = llbbox.bottom - (ax._offset + ax._length);
54245 if(bExtra > 0) {
54246 push.yb = 0;
54247 push.b = bExtra;
54248 }
54249 var tExtra = ax._offset - llbbox.top;
54250 if(tExtra > 0) {
54251 push.yt = 1;
54252 push.t = tExtra;
54253 }
54254 }
54255 }
54256
54257 push[counterLetter] = ax.anchor === 'free' ?
54258 ax.position :
54259 ax._anchorAxis.domain[domainIndices[0]];
54260
54261 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
54262 push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0);
54263 }
54264
54265 if(ax.mirror && ax.anchor !== 'free') {
54266 mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
54267
54268 mirrorPush[sMirror] = ax.linewidth;
54269 if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen;
54270
54271 if(ax.mirror === true || ax.mirror === 'ticks') {
54272 mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]];
54273 } else if(ax.mirror === 'all' || ax.mirror === 'allticks') {
54274 mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]];
54275 }
54276 }
54277 }
54278
54279 if(hasRangeSlider) {
54280 rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
54281 }
54282
54283 Plots.autoMargin(gd, axAutoMarginID(ax), push);
54284 Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush);
54285 Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush);
54286 });
54287
54288 if(!opts.skipTitle &&
54289 !(hasRangeSlider && ax.side === 'bottom')
54290 ) {
54291 seq.push(function() { return drawTitle(gd, ax); });
54292 }
54293
54294 return Lib.syncOrAsync(seq);
54295};
54296
54297function getBoundaryVals(ax, vals) {
54298 var out = [];
54299 var i;
54300
54301 // boundaryVals are never used for labels;
54302 // no need to worry about the other tickTextObj keys
54303 var _push = function(d, bndIndex) {
54304 var xb = d.xbnd[bndIndex];
54305 if(xb !== null) {
54306 out.push(Lib.extendFlat({}, d, {x: xb}));
54307 }
54308 };
54309
54310 if(vals.length) {
54311 for(i = 0; i < vals.length; i++) {
54312 _push(vals[i], 0);
54313 }
54314 _push(vals[i - 1], 1);
54315 }
54316
54317 return out;
54318}
54319
54320function getSecondaryLabelVals(ax, vals) {
54321 var out = [];
54322 var lookup = {};
54323
54324 for(var i = 0; i < vals.length; i++) {
54325 var d = vals[i];
54326 if(lookup[d.text2]) {
54327 lookup[d.text2].push(d.x);
54328 } else {
54329 lookup[d.text2] = [d.x];
54330 }
54331 }
54332
54333 for(var k in lookup) {
54334 out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k));
54335 }
54336
54337 return out;
54338}
54339
54340function getDividerVals(ax, vals) {
54341 var out = [];
54342 var i, current;
54343
54344 // never used for labels;
54345 // no need to worry about the other tickTextObj keys
54346 var _push = function(d, bndIndex) {
54347 var xb = d.xbnd[bndIndex];
54348 if(xb !== null) {
54349 out.push(Lib.extendFlat({}, d, {x: xb}));
54350 }
54351 };
54352
54353 if(ax.showdividers && vals.length) {
54354 for(i = 0; i < vals.length; i++) {
54355 var d = vals[i];
54356 if(d.text2 !== current) {
54357 _push(d, 0);
54358 }
54359 current = d.text2;
54360 }
54361 _push(vals[i - 1], 1);
54362 }
54363
54364 return out;
54365}
54366
54367function calcLabelLevelBbox(ax, cls) {
54368 var top, bottom;
54369 var left, right;
54370
54371 if(ax._selections[cls].size()) {
54372 top = Infinity;
54373 bottom = -Infinity;
54374 left = Infinity;
54375 right = -Infinity;
54376 ax._selections[cls].each(function() {
54377 var thisLabel = selectTickLabel(this);
54378 // Use parent node <g.(x|y)tick>, to make Drawing.bBox
54379 // retrieve a bbox computed with transform info
54380 //
54381 // To improve perf, it would be nice to use `thisLabel.node()`
54382 // (like in fixLabelOverlaps) instead and use Axes.getPxPosition
54383 // together with the makeLabelFns outputs and `tickangle`
54384 // to compute one bbox per (tick value x tick style)
54385 var bb = Drawing.bBox(thisLabel.node().parentNode);
54386 top = Math.min(top, bb.top);
54387 bottom = Math.max(bottom, bb.bottom);
54388 left = Math.min(left, bb.left);
54389 right = Math.max(right, bb.right);
54390 });
54391 } else {
54392 top = 0;
54393 bottom = 0;
54394 left = 0;
54395 right = 0;
54396 }
54397
54398 return {
54399 top: top,
54400 bottom: bottom,
54401 left: left,
54402 right: right,
54403 height: bottom - top,
54404 width: right - left
54405 };
54406}
54407
54408/**
54409 * Which direction do the 'ax.side' values, and free ticks go?
54410 *
54411 * @param {object} ax (full) axis object
54412 * - {string} _id (starting with 'x' or 'y')
54413 * - {string} side
54414 * - {string} ticks
54415 * @return {array} all entries are either -1 or 1
54416 * - [0]: sign for top/right ticks (i.e. negative SVG direction)
54417 * - [1]: sign for bottom/left ticks (i.e. positive SVG direction)
54418 * - [2]: sign for ticks corresponding to 'ax.side'
54419 * - [3]: sign for ticks mirroring 'ax.side'
54420 * - [4]: sign of arrow starting at axis pointing towards margin
54421 */
54422axes.getTickSigns = function(ax) {
54423 var axLetter = ax._id.charAt(0);
54424 var sideOpposite = {x: 'top', y: 'right'}[axLetter];
54425 var main = ax.side === sideOpposite ? 1 : -1;
54426 var out = [-1, 1, main, -main];
54427 // then we flip if outside XOR y axis
54428 if((ax.ticks !== 'inside') === (axLetter === 'x')) {
54429 out = out.map(function(v) { return -v; });
54430 }
54431 // independent of `ticks`; do not flip this one
54432 if(ax.side) {
54433 out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]);
54434 }
54435 return out;
54436};
54437
54438/**
54439 * Make axis translate transform function
54440 *
54441 * @param {object} ax (full) axis object
54442 * - {string} _id
54443 * - {number} _offset
54444 * - {fn} l2p
54445 * @return {fn} function of calcTicks items
54446 */
54447axes.makeTransFn = function(ax) {
54448 var axLetter = ax._id.charAt(0);
54449 var offset = ax._offset;
54450 return axLetter === 'x' ?
54451 function(d) { return 'translate(' + (offset + ax.l2p(d.x)) + ',0)'; } :
54452 function(d) { return 'translate(0,' + (offset + ax.l2p(d.x)) + ')'; };
54453};
54454
54455/**
54456 * Make axis tick path string
54457 *
54458 * @param {object} ax (full) axis object
54459 * - {string} _id
54460 * - {number} ticklen
54461 * - {number} linewidth
54462 * @param {number} shift along direction of ticklen
54463 * @param {1 or -1} sgn tick sign
54464 * @param {number (optional)} len tick length
54465 * @return {string}
54466 */
54467axes.makeTickPath = function(ax, shift, sgn, len) {
54468 len = len !== undefined ? len : ax.ticklen;
54469
54470 var axLetter = ax._id.charAt(0);
54471 var pad = (ax.linewidth || 1) / 2;
54472
54473 return axLetter === 'x' ?
54474 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) :
54475 'M' + (shift + pad * sgn) + ',0h' + (len * sgn);
54476};
54477
54478/**
54479 * Make axis tick label x, y and anchor functions
54480 *
54481 * @param {object} ax (full) axis object
54482 * - {string} _id
54483 * - {string} ticks
54484 * - {number} ticklen
54485 * - {string} side
54486 * - {number} linewidth
54487 * - {number} tickfont.size
54488 * - {boolean} showline
54489 * @param {number} shift
54490 * @param {number} angle [in degrees] ...
54491 * @return {object}
54492 * - {fn} xFn
54493 * - {fn} yFn
54494 * - {fn} anchorFn
54495 * - {fn} heightFn
54496 * - {number} labelStandoff (gap parallel to ticks)
54497 * - {number} labelShift (gap perpendicular to ticks)
54498 */
54499axes.makeLabelFns = function(ax, shift, angle) {
54500 var axLetter = ax._id.charAt(0);
54501 var ticksOnOutsideLabels = ax.tickson !== 'boundaries' && ax.ticks === 'outside';
54502
54503 var labelStandoff = 0;
54504 var labelShift = 0;
54505
54506 if(ticksOnOutsideLabels) {
54507 labelStandoff += ax.ticklen;
54508 }
54509 if(angle && ax.ticks === 'outside') {
54510 var rad = Lib.deg2rad(angle);
54511 labelStandoff = ax.ticklen * Math.cos(rad) + 1;
54512 labelShift = ax.ticklen * Math.sin(rad);
54513 }
54514 if(ax.showticklabels && (ticksOnOutsideLabels || ax.showline)) {
54515 labelStandoff += 0.2 * ax.tickfont.size;
54516 }
54517 labelStandoff += (ax.linewidth || 1) / 2;
54518
54519 var out = {
54520 labelStandoff: labelStandoff,
54521 labelShift: labelShift
54522 };
54523
54524 var x0, y0, ff, flipIt;
54525
54526 if(axLetter === 'x') {
54527 flipIt = ax.side === 'bottom' ? 1 : -1;
54528 x0 = labelShift * flipIt;
54529 y0 = shift + labelStandoff * flipIt;
54530 ff = ax.side === 'bottom' ? 1 : -0.2;
54531
54532 out.xFn = function(d) { return d.dx + x0; };
54533 out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; };
54534 out.anchorFn = function(d, a) {
54535 if(!isNumeric(a) || a === 0 || a === 180) {
54536 return 'middle';
54537 }
54538 return (a * flipIt < 0) ? 'end' : 'start';
54539 };
54540 out.heightFn = function(d, a, h) {
54541 return (a < -60 || a > 60) ? -0.5 * h :
54542 ax.side === 'top' ? -h :
54543 0;
54544 };
54545 } else if(axLetter === 'y') {
54546 flipIt = ax.side === 'right' ? 1 : -1;
54547 x0 = labelStandoff;
54548 y0 = -labelShift * flipIt;
54549 ff = Math.abs(ax.tickangle) === 90 ? 0.5 : 0;
54550
54551 out.xFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; };
54552 out.yFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; };
54553 out.anchorFn = function(d, a) {
54554 if(isNumeric(a) && Math.abs(a) === 90) {
54555 return 'middle';
54556 }
54557 return ax.side === 'right' ? 'start' : 'end';
54558 };
54559 out.heightFn = function(d, a, h) {
54560 a *= ax.side === 'left' ? 1 : -1;
54561 return a < -30 ? -h :
54562 a < 30 ? -0.5 * h :
54563 0;
54564 };
54565 }
54566
54567 return out;
54568};
54569
54570function tickDataFn(d) {
54571 return [d.text, d.x, d.axInfo, d.font, d.fontSize, d.fontColor].join('_');
54572}
54573
54574/**
54575 * Draw axis ticks
54576 *
54577 * @param {DOM element} gd
54578 * @param {object} ax (full) axis object
54579 * - {string} _id
54580 * - {string} ticks
54581 * - {number} linewidth
54582 * - {string} tickcolor
54583 * @param {object} opts
54584 * - {array of object} vals (calcTicks output-like)
54585 * - {d3 selection} layer
54586 * - {string or fn} path
54587 * - {fn} transFn
54588 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
54589 */
54590axes.drawTicks = function(gd, ax, opts) {
54591 opts = opts || {};
54592
54593 var cls = ax._id + 'tick';
54594
54595 var ticks = opts.layer.selectAll('path.' + cls)
54596 .data(ax.ticks ? opts.vals : [], tickDataFn);
54597
54598 ticks.exit().remove();
54599
54600 ticks.enter().append('path')
54601 .classed(cls, 1)
54602 .classed('ticks', 1)
54603 .classed('crisp', opts.crisp !== false)
54604 .call(Color.stroke, ax.tickcolor)
54605 .style('stroke-width', Drawing.crispRound(gd, ax.tickwidth, 1) + 'px')
54606 .attr('d', opts.path);
54607
54608 ticks.attr('transform', opts.transFn);
54609};
54610
54611/**
54612 * Draw axis grid
54613 *
54614 * @param {DOM element} gd
54615 * @param {object} ax (full) axis object
54616 * - {string} _id
54617 * - {boolean} showgrid
54618 * - {string} gridcolor
54619 * - {string} gridwidth
54620 * - {boolean} zeroline
54621 * - {string} type
54622 * - {string} dtick
54623 * @param {object} opts
54624 * - {array of object} vals (calcTicks output-like)
54625 * - {d3 selection} layer
54626 * - {object} counterAxis (full axis object corresponding to counter axis)
54627 * optional - only required if this axis supports zero lines
54628 * - {string or fn} path
54629 * - {fn} transFn
54630 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
54631 */
54632axes.drawGrid = function(gd, ax, opts) {
54633 opts = opts || {};
54634
54635 var cls = ax._id + 'grid';
54636 var vals = opts.vals;
54637 var counterAx = opts.counterAxis;
54638 if(ax.showgrid === false) {
54639 vals = [];
54640 } else if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
54641 var isArrayMode = ax.tickmode === 'array';
54642 for(var i = 0; i < vals.length; i++) {
54643 var xi = vals[i].x;
54644 if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
54645 vals = vals.slice(0, i).concat(vals.slice(i + 1));
54646 // In array mode you can in principle have multiple
54647 // ticks at 0, so test them all. Otherwise once we found
54648 // one we can stop.
54649 if(isArrayMode) i--;
54650 else break;
54651 }
54652 }
54653 }
54654
54655 var grid = opts.layer.selectAll('path.' + cls)
54656 .data(vals, tickDataFn);
54657
54658 grid.exit().remove();
54659
54660 grid.enter().append('path')
54661 .classed(cls, 1)
54662 .classed('crisp', opts.crisp !== false);
54663
54664 ax._gw = Drawing.crispRound(gd, ax.gridwidth, 1);
54665
54666 grid.attr('transform', opts.transFn)
54667 .attr('d', opts.path)
54668 .call(Color.stroke, ax.gridcolor || '#ddd')
54669 .style('stroke-width', ax._gw + 'px');
54670
54671 if(typeof opts.path === 'function') grid.attr('d', opts.path);
54672};
54673
54674/**
54675 * Draw axis zero-line
54676 *
54677 * @param {DOM element} gd
54678 * @param {object} ax (full) axis object
54679 * - {string} _id
54680 * - {boolean} zeroline
54681 * - {number} zerolinewidth
54682 * - {string} zerolinecolor
54683 * - {number (optional)} _gridWidthCrispRound
54684 * @param {object} opts
54685 * - {d3 selection} layer
54686 * - {object} counterAxis (full axis object corresponding to counter axis)
54687 * - {string or fn} path
54688 * - {fn} transFn
54689 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
54690 */
54691axes.drawZeroLine = function(gd, ax, opts) {
54692 opts = opts || opts;
54693
54694 var cls = ax._id + 'zl';
54695 var show = axes.shouldShowZeroLine(gd, ax, opts.counterAxis);
54696
54697 var zl = opts.layer.selectAll('path.' + cls)
54698 .data(show ? [{x: 0, id: ax._id}] : []);
54699
54700 zl.exit().remove();
54701
54702 zl.enter().append('path')
54703 .classed(cls, 1)
54704 .classed('zl', 1)
54705 .classed('crisp', opts.crisp !== false)
54706 .each(function() {
54707 // use the fact that only one element can enter to trigger a sort.
54708 // If several zerolines enter at the same time we will sort once per,
54709 // but generally this should be a minimal overhead.
54710 opts.layer.selectAll('path').sort(function(da, db) {
54711 return axisIds.idSort(da.id, db.id);
54712 });
54713 });
54714
54715 zl.attr('transform', opts.transFn)
54716 .attr('d', opts.path)
54717 .call(Color.stroke, ax.zerolinecolor || Color.defaultLine)
54718 .style('stroke-width', Drawing.crispRound(gd, ax.zerolinewidth, ax._gw || 1) + 'px');
54719};
54720
54721/**
54722 * Draw axis tick labels
54723 *
54724 * @param {DOM element} gd
54725 * @param {object} ax (full) axis object
54726 * - {string} _id
54727 * - {boolean} showticklabels
54728 * - {number} tickangle
54729 * - {object (optional)} _selections
54730 * - {object} (optional)} _tickAngles
54731 * - {object} (optional)} _prevTickAngles
54732 * @param {object} opts
54733 * - {array of object} vals (calcTicks output-like)
54734 * - {d3 selection} layer
54735 * - {string (optional)} cls (node className)
54736 * - {boolean} repositionOnUpdate (set to true to reposition update selection)
54737 * - {boolean} secondary
54738 * - {fn} transFn
54739 * - {object} labelFns
54740 * + {fn} xFn
54741 * + {fn} yFn
54742 * + {fn} anchorFn
54743 * + {fn} heightFn
54744 */
54745axes.drawLabels = function(gd, ax, opts) {
54746 opts = opts || {};
54747
54748 var fullLayout = gd._fullLayout;
54749 var axId = ax._id;
54750 var axLetter = axId.charAt(0);
54751 var cls = opts.cls || axId + 'tick';
54752 var vals = opts.vals;
54753 var labelFns = opts.labelFns;
54754 var tickAngle = opts.secondary ? 0 : ax.tickangle;
54755 var prevAngle = (ax._prevTickAngles || {})[cls];
54756
54757 var tickLabels = opts.layer.selectAll('g.' + cls)
54758 .data(ax.showticklabels ? vals : [], tickDataFn);
54759
54760 var labelsReady = [];
54761
54762 tickLabels.enter().append('g')
54763 .classed(cls, 1)
54764 .append('text')
54765 // only so tex has predictable alignment that we can
54766 // alter later
54767 .attr('text-anchor', 'middle')
54768 .each(function(d) {
54769 var thisLabel = d3.select(this);
54770 var newPromise = gd._promises.length;
54771
54772 thisLabel
54773 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d))
54774 .call(Drawing.font, d.font, d.fontSize, d.fontColor)
54775 .text(d.text)
54776 .call(svgTextUtils.convertToTspans, gd);
54777
54778 if(gd._promises[newPromise]) {
54779 // if we have an async label, we'll deal with that
54780 // all here so take it out of gd._promises and
54781 // instead position the label and promise this in
54782 // labelsReady
54783 labelsReady.push(gd._promises.pop().then(function() {
54784 positionLabels(thisLabel, tickAngle);
54785 }));
54786 } else {
54787 // sync label: just position it now.
54788 positionLabels(thisLabel, tickAngle);
54789 }
54790 });
54791
54792 tickLabels.exit().remove();
54793
54794 if(opts.repositionOnUpdate) {
54795 tickLabels.each(function(d) {
54796 d3.select(this).select('text')
54797 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d));
54798 });
54799 }
54800
54801 function positionLabels(s, angle) {
54802 s.each(function(d) {
54803 var thisLabel = d3.select(this);
54804 var mathjaxGroup = thisLabel.select('.text-math-group');
54805 var anchor = labelFns.anchorFn(d, angle);
54806
54807 var transform = opts.transFn.call(thisLabel.node(), d) +
54808 ((isNumeric(angle) && +angle !== 0) ?
54809 (' rotate(' + angle + ',' + labelFns.xFn(d) + ',' +
54810 (labelFns.yFn(d) - d.fontSize / 2) + ')') :
54811 '');
54812
54813 // how much to shift a multi-line label to center it vertically.
54814 var nLines = svgTextUtils.lineCount(thisLabel);
54815 var lineHeight = LINE_SPACING * d.fontSize;
54816 var anchorHeight = labelFns.heightFn(d, isNumeric(angle) ? +angle : 0, (nLines - 1) * lineHeight);
54817
54818 if(anchorHeight) {
54819 transform += ' translate(0, ' + anchorHeight + ')';
54820 }
54821
54822 if(mathjaxGroup.empty()) {
54823 thisLabel.select('text').attr({
54824 transform: transform,
54825 'text-anchor': anchor
54826 });
54827 } else {
54828 var mjWidth = Drawing.bBox(mathjaxGroup.node()).width;
54829 var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor];
54830 mathjaxGroup.attr('transform', transform + (mjShift ? 'translate(' + mjShift + ',0)' : ''));
54831 }
54832 });
54833 }
54834
54835 // make sure all labels are correctly positioned at their base angle
54836 // the positionLabels call above is only for newly drawn labels.
54837 // do this without waiting, using the last calculated angle to
54838 // minimize flicker, then do it again when we know all labels are
54839 // there, putting back the prescribed angle to check for overlaps.
54840 positionLabels(tickLabels, (prevAngle + 1) ? prevAngle : tickAngle);
54841
54842 function allLabelsReady() {
54843 return labelsReady.length && Promise.all(labelsReady);
54844 }
54845
54846 var autoangle = null;
54847
54848 function fixLabelOverlaps() {
54849 positionLabels(tickLabels, tickAngle);
54850
54851 // check for auto-angling if x labels overlap
54852 // don't auto-angle at all for log axes with
54853 // base and digit format
54854 if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) &&
54855 (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D')
54856 ) {
54857 autoangle = 0;
54858
54859 var maxFontSize = 0;
54860 var lbbArray = [];
54861 var i;
54862
54863 tickLabels.each(function(d) {
54864 maxFontSize = Math.max(maxFontSize, d.fontSize);
54865
54866 var x = ax.l2p(d.x);
54867 var thisLabel = selectTickLabel(this);
54868 var bb = Drawing.bBox(thisLabel.node());
54869
54870 lbbArray.push({
54871 // ignore about y, just deal with x overlaps
54872 top: 0,
54873 bottom: 10,
54874 height: 10,
54875 left: x - bb.width / 2,
54876 // impose a 2px gap
54877 right: x + bb.width / 2 + 2,
54878 width: bb.width + 2
54879 });
54880 });
54881
54882 if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
54883 var gap = 2;
54884 if(ax.ticks) gap += ax.tickwidth / 2;
54885
54886 // TODO should secondary labels also fall into this fix-overlap regime?
54887
54888 for(i = 0; i < lbbArray.length; i++) {
54889 var xbnd = vals[i].xbnd;
54890 var lbb = lbbArray[i];
54891 if(
54892 (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
54893 (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
54894 ) {
54895 autoangle = 90;
54896 break;
54897 }
54898 }
54899 } else {
54900 var vLen = vals.length;
54901 var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
54902 var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory';
54903
54904 // any overlap at all - set 30 degrees or 90 degrees
54905 for(i = 0; i < lbbArray.length - 1; i++) {
54906 if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1])) {
54907 autoangle = rotate90 ? 90 : 30;
54908 break;
54909 }
54910 }
54911 }
54912
54913 if(autoangle) {
54914 positionLabels(tickLabels, autoangle);
54915 }
54916 }
54917 }
54918
54919 if(ax._selections) {
54920 ax._selections[cls] = tickLabels;
54921 }
54922
54923 var seq = [allLabelsReady];
54924
54925 // N.B. during auto-margin redraws, if the axis fixed its label overlaps
54926 // by rotating 90 degrees, do not attempt to re-fix its label overlaps
54927 // as this can lead to infinite redraw loops!
54928 if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) {
54929 autoangle = 90;
54930 seq.push(function() {
54931 positionLabels(tickLabels, prevAngle);
54932 });
54933 } else {
54934 seq.push(fixLabelOverlaps);
54935 }
54936
54937 // save current tick angle for future redraws
54938 if(ax._tickAngles) {
54939 seq.push(function() {
54940 ax._tickAngles[cls] = autoangle === null ?
54941 (isNumeric(tickAngle) ? tickAngle : 0) :
54942 autoangle;
54943 });
54944 }
54945
54946 var done = Lib.syncOrAsync(seq);
54947 if(done && done.then) gd._promises.push(done);
54948 return done;
54949};
54950
54951/**
54952 * Draw axis dividers
54953 *
54954 * @param {DOM element} gd
54955 * @param {object} ax (full) axis object
54956 * - {string} _id
54957 * - {string} showdividers
54958 * - {number} dividerwidth
54959 * - {string} dividercolor
54960 * @param {object} opts
54961 * - {array of object} vals (calcTicks output-like)
54962 * - {d3 selection} layer
54963 * - {fn} path
54964 * - {fn} transFn
54965 */
54966function drawDividers(gd, ax, opts) {
54967 var cls = ax._id + 'divider';
54968 var vals = opts.vals;
54969
54970 var dividers = opts.layer.selectAll('path.' + cls)
54971 .data(vals, tickDataFn);
54972
54973 dividers.exit().remove();
54974
54975 dividers.enter().insert('path', ':first-child')
54976 .classed(cls, 1)
54977 .classed('crisp', 1)
54978 .call(Color.stroke, ax.dividercolor)
54979 .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px');
54980
54981 dividers
54982 .attr('transform', opts.transFn)
54983 .attr('d', opts.path);
54984}
54985
54986/**
54987 * Get axis position in px, that is the distance for the graph's
54988 * top (left) edge for x (y) axes.
54989 *
54990 * @param {DOM element} gd
54991 * @param {object} ax (full) axis object
54992 * - {string} _id
54993 * - {string} side
54994 * if anchored:
54995 * - {object} _anchorAxis
54996 * Otherwise:
54997 * - {number} position
54998 * @return {number}
54999 */
55000axes.getPxPosition = function(gd, ax) {
55001 var gs = gd._fullLayout._size;
55002 var axLetter = ax._id.charAt(0);
55003 var side = ax.side;
55004 var anchorAxis;
55005
55006 if(ax.anchor !== 'free') {
55007 anchorAxis = ax._anchorAxis;
55008 } else if(axLetter === 'x') {
55009 anchorAxis = {
55010 _offset: gs.t + (1 - (ax.position || 0)) * gs.h,
55011 _length: 0
55012 };
55013 } else if(axLetter === 'y') {
55014 anchorAxis = {
55015 _offset: gs.l + (ax.position || 0) * gs.w,
55016 _length: 0
55017 };
55018 }
55019
55020 if(side === 'top' || side === 'left') {
55021 return anchorAxis._offset;
55022 } else if(side === 'bottom' || side === 'right') {
55023 return anchorAxis._offset + anchorAxis._length;
55024 }
55025};
55026
55027/**
55028 * Approximate axis title depth (w/o computing its bounding box)
55029 *
55030 * @param {object} ax (full) axis object
55031 * - {string} title.text
55032 * - {number} title.font.size
55033 * - {number} title.standoff
55034 * @return {number} (in px)
55035 */
55036function approxTitleDepth(ax) {
55037 var fontSize = ax.title.font.size;
55038 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
55039 if(ax.title.hasOwnProperty('standoff')) {
55040 return extraLines ?
55041 fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) :
55042 fontSize * CAP_SHIFT;
55043 } else {
55044 return extraLines ?
55045 fontSize * (extraLines + 1) * LINE_SPACING :
55046 fontSize;
55047 }
55048}
55049
55050/**
55051 * Draw axis title, compute default standoff if necessary
55052 *
55053 * @param {DOM element} gd
55054 * @param {object} ax (full) axis object
55055 * - {string} _id
55056 * - {string} _name
55057 * - {string} side
55058 * - {number} title.font.size
55059 * - {object} _selections
55060 *
55061 * - {number} _depth
55062 * - {number} title.standoff
55063 * OR
55064 * - {number} linewidth
55065 * - {boolean} showticklabels
55066 */
55067function drawTitle(gd, ax) {
55068 var fullLayout = gd._fullLayout;
55069 var axId = ax._id;
55070 var axLetter = axId.charAt(0);
55071 var fontSize = ax.title.font.size;
55072
55073 var titleStandoff;
55074
55075 if(ax.title.hasOwnProperty('standoff')) {
55076 titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax);
55077 } else {
55078 if(ax.type === 'multicategory') {
55079 titleStandoff = ax._depth;
55080 } else {
55081 var offsetBase = 1.5;
55082 titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0);
55083 }
55084
55085 if(axLetter === 'x') {
55086 titleStandoff += ax.side === 'top' ?
55087 fontSize * (ax.showticklabels ? 1 : 0) :
55088 fontSize * (ax.showticklabels ? 1.5 : 0.5);
55089 } else {
55090 titleStandoff += ax.side === 'right' ?
55091 fontSize * (ax.showticklabels ? 1 : 0.5) :
55092 fontSize * (ax.showticklabels ? 0.5 : 0);
55093 }
55094 }
55095
55096 var pos = axes.getPxPosition(gd, ax);
55097 var transform, x, y;
55098
55099 if(axLetter === 'x') {
55100 x = ax._offset + ax._length / 2;
55101 y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff;
55102 } else {
55103 y = ax._offset + ax._length / 2;
55104 x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff;
55105 transform = {rotate: '-90', offset: 0};
55106 }
55107
55108 var avoid;
55109
55110 if(ax.type !== 'multicategory') {
55111 var tickLabels = ax._selections[ax._id + 'tick'];
55112
55113 avoid = {
55114 selection: tickLabels,
55115 side: ax.side
55116 };
55117
55118 if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) {
55119 var translation = Drawing.getTranslate(tickLabels.node().parentNode);
55120 avoid.offsetLeft = translation.x;
55121 avoid.offsetTop = translation.y;
55122 }
55123
55124 if(ax.title.hasOwnProperty('standoff')) {
55125 avoid.pad = 0;
55126 }
55127 }
55128
55129 return Titles.draw(gd, axId + 'title', {
55130 propContainer: ax,
55131 propName: ax._name + '.title.text',
55132 placeholder: fullLayout._dfltTitle[axLetter],
55133 avoid: avoid,
55134 transform: transform,
55135 attributes: {x: x, y: y, 'text-anchor': 'middle'}
55136 });
55137}
55138
55139axes.shouldShowZeroLine = function(gd, ax, counterAxis) {
55140 var rng = Lib.simpleMap(ax.range, ax.r2l);
55141 return (
55142 (rng[0] * rng[1] <= 0) &&
55143 ax.zeroline &&
55144 (ax.type === 'linear' || ax.type === '-') &&
55145 !(ax.rangebreaks && ax.maskBreaks(0) === BADNUM) &&
55146 (
55147 clipEnds(ax, 0) ||
55148 !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) ||
55149 hasBarsOrFill(gd, ax)
55150 )
55151 );
55152};
55153
55154axes.clipEnds = function(ax, vals) {
55155 return vals.filter(function(d) { return clipEnds(ax, d.x); });
55156};
55157
55158function clipEnds(ax, l) {
55159 var p = ax.l2p(l);
55160 return (p > 1 && p < ax._length - 1);
55161}
55162
55163function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) {
55164 var mainCounterAxis = counterAxis._mainAxis;
55165 if(!mainCounterAxis) return;
55166
55167 var fullLayout = gd._fullLayout;
55168 var axLetter = ax._id.charAt(0);
55169 var counterLetter = axes.counterLetter(ax._id);
55170
55171 var zeroPosition = ax._offset + (
55172 ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ?
55173 0 : ax._length
55174 );
55175
55176 function lineNearZero(ax2) {
55177 if(!ax2.showline || !ax2.linewidth) return false;
55178 var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1);
55179
55180 function closeEnough(pos2) {
55181 return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance;
55182 }
55183
55184 if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) {
55185 return true;
55186 }
55187 var linePositions = ax2._linepositions || {};
55188 for(var k in linePositions) {
55189 if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) {
55190 return true;
55191 }
55192 }
55193 }
55194
55195 var plotinfo = fullLayout._plots[counterAxis._mainSubplot];
55196 if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) {
55197 return lineNearZero(counterAxis, zeroPosition);
55198 }
55199
55200 var counterLetterAxes = axes.list(gd, counterLetter);
55201 for(var i = 0; i < counterLetterAxes.length; i++) {
55202 var counterAxis2 = counterLetterAxes[i];
55203 if(
55204 counterAxis2._mainAxis === mainCounterAxis &&
55205 lineNearZero(counterAxis2, zeroPosition)
55206 ) {
55207 return true;
55208 }
55209 }
55210}
55211
55212function hasBarsOrFill(gd, ax) {
55213 var fullData = gd._fullData;
55214 var subplot = ax._mainSubplot;
55215 var axLetter = ax._id.charAt(0);
55216
55217 for(var i = 0; i < fullData.length; i++) {
55218 var trace = fullData[i];
55219
55220 if(trace.visible === true && (trace.xaxis + trace.yaxis) === subplot) {
55221 if(
55222 Registry.traceIs(trace, 'bar-like') &&
55223 trace.orientation === {x: 'h', y: 'v'}[axLetter]
55224 ) return true;
55225
55226 if(
55227 trace.fill &&
55228 trace.fill.charAt(trace.fill.length - 1) === axLetter
55229 ) return true;
55230 }
55231 }
55232 return false;
55233}
55234
55235function selectTickLabel(gTick) {
55236 var s = d3.select(gTick);
55237 var mj = s.select('.text-math-group');
55238 return mj.empty() ? s.select('text') : mj;
55239}
55240
55241/**
55242 * Find all margin pushers for 2D axes and reserve them for later use
55243 * Both label and rangeslider automargin calculations happen later so
55244 * we need to explicitly allow their ids in order to not delete them.
55245 *
55246 * TODO: can we pull the actual automargin calls forward to avoid this hack?
55247 * We're probably also doing multiple redraws in this case, would be faster
55248 * if we can just do the whole calculation ahead of time and draw once.
55249 */
55250axes.allowAutoMargin = function(gd) {
55251 var axList = axes.list(gd, '', true);
55252 for(var i = 0; i < axList.length; i++) {
55253 var ax = axList[i];
55254 if(ax.automargin) {
55255 Plots.allowAutoMargin(gd, axAutoMarginID(ax));
55256 if(ax.mirror) {
55257 Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax));
55258 }
55259 }
55260 if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) {
55261 Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax));
55262 }
55263 }
55264};
55265
55266function axAutoMarginID(ax) { return ax._id + '.automargin'; }
55267function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; }
55268function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; }
55269
55270// swap all the presentation attributes of the axes showing these traces
55271axes.swap = function(gd, traces) {
55272 var axGroups = makeAxisGroups(gd, traces);
55273
55274 for(var i = 0; i < axGroups.length; i++) {
55275 swapAxisGroup(gd, axGroups[i].x, axGroups[i].y);
55276 }
55277};
55278
55279function makeAxisGroups(gd, traces) {
55280 var groups = [];
55281 var i, j;
55282
55283 for(i = 0; i < traces.length; i++) {
55284 var groupsi = [];
55285 var xi = gd._fullData[traces[i]].xaxis;
55286 var yi = gd._fullData[traces[i]].yaxis;
55287 if(!xi || !yi) continue; // not a 2D cartesian trace?
55288
55289 for(j = 0; j < groups.length; j++) {
55290 if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) {
55291 groupsi.push(j);
55292 }
55293 }
55294
55295 if(!groupsi.length) {
55296 groups.push({x: [xi], y: [yi]});
55297 continue;
55298 }
55299
55300 var group0 = groups[groupsi[0]];
55301 var groupj;
55302
55303 if(groupsi.length > 1) {
55304 for(j = 1; j < groupsi.length; j++) {
55305 groupj = groups[groupsi[j]];
55306 mergeAxisGroups(group0.x, groupj.x);
55307 mergeAxisGroups(group0.y, groupj.y);
55308 }
55309 }
55310 mergeAxisGroups(group0.x, [xi]);
55311 mergeAxisGroups(group0.y, [yi]);
55312 }
55313
55314 return groups;
55315}
55316
55317function mergeAxisGroups(intoSet, fromSet) {
55318 for(var i = 0; i < fromSet.length; i++) {
55319 if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]);
55320 }
55321}
55322
55323function swapAxisGroup(gd, xIds, yIds) {
55324 var xFullAxes = [];
55325 var yFullAxes = [];
55326 var layout = gd.layout;
55327 var i, j;
55328
55329 for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i]));
55330 for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i]));
55331
55332 var allAxKeys = Object.keys(axAttrs);
55333
55334 var noSwapAttrs = [
55335 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType'
55336 ];
55337 var numericTypes = ['linear', 'log'];
55338
55339 for(i = 0; i < allAxKeys.length; i++) {
55340 var keyi = allAxKeys[i];
55341 var xVal = xFullAxes[0][keyi];
55342 var yVal = yFullAxes[0][keyi];
55343 var allEqual = true;
55344 var coerceLinearX = false;
55345 var coerceLinearY = false;
55346 if(keyi.charAt(0) === '_' || typeof xVal === 'function' ||
55347 noSwapAttrs.indexOf(keyi) !== -1) {
55348 continue;
55349 }
55350 for(j = 1; j < xFullAxes.length && allEqual; j++) {
55351 var xVali = xFullAxes[j][keyi];
55352 if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 &&
55353 numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) {
55354 // type is special - if we find a mixture of linear and log,
55355 // coerce them all to linear on flipping
55356 coerceLinearX = true;
55357 } else if(xVali !== xVal) allEqual = false;
55358 }
55359 for(j = 1; j < yFullAxes.length && allEqual; j++) {
55360 var yVali = yFullAxes[j][keyi];
55361 if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 &&
55362 numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) {
55363 // type is special - if we find a mixture of linear and log,
55364 // coerce them all to linear on flipping
55365 coerceLinearY = true;
55366 } else if(yFullAxes[j][keyi] !== yVal) allEqual = false;
55367 }
55368 if(allEqual) {
55369 if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear';
55370 if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear';
55371 swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle);
55372 }
55373 }
55374
55375 // now swap x&y for any annotations anchored to these x & y
55376 for(i = 0; i < gd._fullLayout.annotations.length; i++) {
55377 var ann = gd._fullLayout.annotations[i];
55378 if(xIds.indexOf(ann.xref) !== -1 &&
55379 yIds.indexOf(ann.yref) !== -1) {
55380 Lib.swapAttrs(layout.annotations[i], ['?']);
55381 }
55382 }
55383}
55384
55385function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) {
55386 // in case the value is the default for either axis,
55387 // look at the first axis in each list and see if
55388 // this key's value is undefined
55389 var np = Lib.nestedProperty;
55390 var xVal = np(layout[xFullAxes[0]._name], key).get();
55391 var yVal = np(layout[yFullAxes[0]._name], key).get();
55392 var i;
55393
55394 if(key === 'title') {
55395 // special handling of placeholder titles
55396 if(xVal && xVal.text === dfltTitle.x) {
55397 xVal.text = dfltTitle.y;
55398 }
55399 if(yVal && yVal.text === dfltTitle.y) {
55400 yVal.text = dfltTitle.x;
55401 }
55402 }
55403
55404 for(i = 0; i < xFullAxes.length; i++) {
55405 np(layout, xFullAxes[i]._name + '.' + key).set(yVal);
55406 }
55407 for(i = 0; i < yFullAxes.length; i++) {
55408 np(layout, yFullAxes[i]._name + '.' + key).set(xVal);
55409 }
55410}
55411
55412function isAngular(ax) {
55413 return ax._id === 'angularaxis';
55414}
55415
55416},{"../../components/color":52,"../../components/drawing":74,"../../components/titles":147,"../../constants/alignment":154,"../../constants/numerical":158,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/plots":256,"../../registry":269,"./autorange":221,"./axis_autotype":223,"./axis_ids":225,"./clean_ticks":227,"./layout_attributes":236,"./set_convert":242,"d3":16,"fast-isnumeric":18}],223:[function(_dereq_,module,exports){
55417/**
55418* Copyright 2012-2020, Plotly, Inc.
55419* All rights reserved.
55420*
55421* This source code is licensed under the MIT license found in the
55422* LICENSE file in the root directory of this source tree.
55423*/
55424
55425
55426'use strict';
55427
55428var isNumeric = _dereq_('fast-isnumeric');
55429
55430var Lib = _dereq_('../../lib');
55431var BADNUM = _dereq_('../../constants/numerical').BADNUM;
55432
55433module.exports = function autoType(array, calendar, opts) {
55434 opts = opts || {};
55435
55436 if(!opts.noMultiCategory && multiCategory(array)) return 'multicategory';
55437 if(moreDates(array, calendar)) return 'date';
55438 if(category(array)) return 'category';
55439 if(linearOK(array)) return 'linear';
55440 else return '-';
55441};
55442
55443// is there at least one number in array? If not, we should leave
55444// ax.type empty so it can be autoset later
55445function linearOK(array) {
55446 if(!array) return false;
55447
55448 for(var i = 0; i < array.length; i++) {
55449 if(isNumeric(array[i])) return true;
55450 }
55451
55452 return false;
55453}
55454
55455// does the array a have mostly dates rather than numbers?
55456// note: some values can be neither (such as blanks, text)
55457// 2- or 4-digit integers can be both, so require twice as many
55458// dates as non-dates, to exclude cases with mostly 2 & 4 digit
55459// numbers and a few dates
55460// as with categories, consider DISTINCT values only.
55461function moreDates(a, calendar) {
55462 // test at most 1000 points, evenly spaced
55463 var inc = Math.max(1, (a.length - 1) / 1000);
55464 var dcnt = 0;
55465 var ncnt = 0;
55466 var seen = {};
55467
55468 for(var i = 0; i < a.length; i += inc) {
55469 var ai = a[Math.round(i)];
55470 var stri = String(ai);
55471 if(seen[stri]) continue;
55472 seen[stri] = 1;
55473
55474 if(Lib.isDateTime(ai, calendar)) dcnt += 1;
55475 if(isNumeric(ai)) ncnt += 1;
55476 }
55477
55478 return (dcnt > ncnt * 2);
55479}
55480
55481// are the (x,y)-values in gd.data mostly text?
55482// require twice as many DISTINCT categories as distinct numbers
55483function category(a) {
55484 // test at most 1000 points
55485 var inc = Math.max(1, (a.length - 1) / 1000);
55486 var curvenums = 0;
55487 var curvecats = 0;
55488 var seen = {};
55489
55490 for(var i = 0; i < a.length; i += inc) {
55491 var ai = a[Math.round(i)];
55492 var stri = String(ai);
55493 if(seen[stri]) continue;
55494 seen[stri] = 1;
55495
55496 if(typeof ai === 'boolean') curvecats++;
55497 else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
55498 else if(typeof ai === 'string') curvecats++;
55499 }
55500
55501 return curvecats > curvenums * 2;
55502}
55503
55504// very-loose requirements for multicategory,
55505// trace modules that should never auto-type to multicategory
55506// should be declared with 'noMultiCategory'
55507function multiCategory(a) {
55508 return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]);
55509}
55510
55511},{"../../constants/numerical":158,"../../lib":178,"fast-isnumeric":18}],224:[function(_dereq_,module,exports){
55512/**
55513* Copyright 2012-2020, Plotly, Inc.
55514* All rights reserved.
55515*
55516* This source code is licensed under the MIT license found in the
55517* LICENSE file in the root directory of this source tree.
55518*/
55519
55520'use strict';
55521
55522var isNumeric = _dereq_('fast-isnumeric');
55523
55524var Registry = _dereq_('../../registry');
55525var Lib = _dereq_('../../lib');
55526
55527var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
55528
55529var layoutAttributes = _dereq_('./layout_attributes');
55530var handleTickValueDefaults = _dereq_('./tick_value_defaults');
55531var handleTickMarkDefaults = _dereq_('./tick_mark_defaults');
55532var handleTickLabelDefaults = _dereq_('./tick_label_defaults');
55533var handleCategoryOrderDefaults = _dereq_('./category_order_defaults');
55534var handleLineGridDefaults = _dereq_('./line_grid_defaults');
55535var setConvert = _dereq_('./set_convert');
55536
55537var DAY_OF_WEEK = _dereq_('./constants').WEEKDAY_PATTERN;
55538var HOUR = _dereq_('./constants').HOUR_PATTERN;
55539
55540/**
55541 * options: object containing:
55542 *
55543 * letter: 'x' or 'y'
55544 * title: name of the axis (ie 'Colorbar') to go in default title
55545 * font: the default font to inherit
55546 * outerTicks: boolean, should ticks default to outside?
55547 * showGrid: boolean, should gridlines be shown by default?
55548 * noHover: boolean, this axis doesn't support hover effects?
55549 * noTickson: boolean, this axis doesn't support 'tickson'
55550 * data: the plot data, used to manage categories
55551 * bgColor: the plot background color, to calculate default gridline colors
55552 * calendar:
55553 * splomStash:
55554 * visibleDflt: boolean
55555 * reverseDflt: boolean
55556 * automargin: boolean
55557 */
55558module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
55559 var letter = options.letter;
55560 var font = options.font || {};
55561 var splomStash = options.splomStash || {};
55562
55563 var visible = coerce('visible', !options.visibleDflt);
55564
55565 var axTemplate = containerOut._template || {};
55566 var axType = containerOut.type || axTemplate.type || '-';
55567
55568 if(axType === 'date') {
55569 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
55570 handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar);
55571 }
55572
55573 setConvert(containerOut, layoutOut);
55574
55575 var autorangeDflt = !containerOut.isValidRange(containerIn.range);
55576 if(autorangeDflt && options.reverseDflt) autorangeDflt = 'reversed';
55577 var autoRange = coerce('autorange', autorangeDflt);
55578 if(autoRange && (axType === 'linear' || axType === '-')) coerce('rangemode');
55579
55580 coerce('range');
55581 containerOut.cleanRange();
55582
55583 handleCategoryOrderDefaults(containerIn, containerOut, coerce, options);
55584
55585 if(axType !== 'category' && !options.noHover) coerce('hoverformat');
55586
55587 var dfltColor = coerce('color');
55588 // if axis.color was provided, use it for fonts too; otherwise,
55589 // inherit from global font color in case that was provided.
55590 // Compare to dflt rather than to containerIn, so we can provide color via
55591 // template too.
55592 var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
55593 // try to get default title from splom trace, fallback to graph-wide value
55594 var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];
55595
55596 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 1});
55597 if(!visible) return containerOut;
55598
55599 coerce('title.text', dfltTitle);
55600 Lib.coerceFont(coerce, 'title.font', {
55601 family: font.family,
55602 size: Math.round(font.size * 1.2),
55603 color: dfltFontColor
55604 });
55605
55606 handleTickValueDefaults(containerIn, containerOut, coerce, axType);
55607 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 2});
55608 handleTickMarkDefaults(containerIn, containerOut, coerce, options);
55609 handleLineGridDefaults(containerIn, containerOut, coerce, {
55610 dfltColor: dfltColor,
55611 bgColor: options.bgColor,
55612 showGrid: options.showGrid,
55613 attributes: layoutAttributes
55614 });
55615
55616 if(containerOut.showline || containerOut.ticks) coerce('mirror');
55617
55618 if(options.automargin) coerce('automargin');
55619
55620 var isMultiCategory = axType === 'multicategory';
55621
55622 if(!options.noTickson &&
55623 (axType === 'category' || isMultiCategory) &&
55624 (containerOut.ticks || containerOut.showgrid)
55625 ) {
55626 var ticksonDflt;
55627 if(isMultiCategory) ticksonDflt = 'boundaries';
55628 coerce('tickson', ticksonDflt);
55629 }
55630
55631 if(isMultiCategory) {
55632 var showDividers = coerce('showdividers');
55633 if(showDividers) {
55634 coerce('dividercolor');
55635 coerce('dividerwidth');
55636 }
55637 }
55638
55639 if(axType === 'date') {
55640 handleArrayContainerDefaults(containerIn, containerOut, {
55641 name: 'rangebreaks',
55642 inclusionAttr: 'enabled',
55643 handleItemDefaults: rangebreaksDefaults
55644 });
55645
55646 if(!containerOut.rangebreaks.length) {
55647 delete containerOut.rangebreaks;
55648 } else {
55649 for(var k = 0; k < containerOut.rangebreaks.length; k++) {
55650 if(containerOut.rangebreaks[k].pattern === DAY_OF_WEEK) {
55651 containerOut._hasDayOfWeekBreaks = true;
55652 break;
55653 }
55654 }
55655
55656 setConvert(containerOut, layoutOut);
55657
55658 if(layoutOut._has('scattergl') || layoutOut._has('splom')) {
55659 for(var i = 0; i < options.data.length; i++) {
55660 var trace = options.data[i];
55661 if(trace.type === 'scattergl' || trace.type === 'splom') {
55662 trace.visible = false;
55663 Lib.warn(trace.type +
55664 ' traces do not work on axes with rangebreaks.' +
55665 ' Setting trace ' + trace.index + ' to `visible: false`.');
55666 }
55667 }
55668 }
55669 }
55670 }
55671
55672 return containerOut;
55673};
55674
55675function rangebreaksDefaults(itemIn, itemOut, containerOut) {
55676 function coerce(attr, dflt) {
55677 return Lib.coerce(itemIn, itemOut, layoutAttributes.rangebreaks, attr, dflt);
55678 }
55679
55680 var enabled = coerce('enabled');
55681
55682 if(enabled) {
55683 var bnds = coerce('bounds');
55684 if(bnds && bnds.length >= 2) {
55685 var dfltPattern = '';
55686 var i, q;
55687 if(bnds.length === 2) {
55688 for(i = 0; i < 2; i++) {
55689 q = indexOfDay(bnds[i]);
55690 if(q) {
55691 dfltPattern = DAY_OF_WEEK;
55692 break;
55693 }
55694 }
55695 }
55696 var pattern = coerce('pattern', dfltPattern);
55697 if(pattern === DAY_OF_WEEK) {
55698 for(i = 0; i < 2; i++) {
55699 q = indexOfDay(bnds[i]);
55700 if(q) {
55701 // convert to integers i.e 'Sunday' --> 0
55702 itemOut.bounds[i] = bnds[i] = q - 1;
55703 }
55704 }
55705 }
55706 if(pattern) {
55707 // ensure types and ranges
55708 for(i = 0; i < 2; i++) {
55709 q = bnds[i];
55710 switch(pattern) {
55711 case DAY_OF_WEEK :
55712 if(!isNumeric(q)) {
55713 itemOut.enabled = false;
55714 return;
55715 }
55716 q = +q;
55717
55718 if(
55719 q !== Math.floor(q) || // don't accept fractional days for mow
55720 q < 0 || q >= 7
55721 ) {
55722 itemOut.enabled = false;
55723 return;
55724 }
55725 // use number
55726 itemOut.bounds[i] = bnds[i] = q;
55727 break;
55728
55729 case HOUR :
55730 if(!isNumeric(q)) {
55731 itemOut.enabled = false;
55732 return;
55733 }
55734 q = +q;
55735
55736 if(q < 0 || q > 24) { // accept 24
55737 itemOut.enabled = false;
55738 return;
55739 }
55740 // use number
55741 itemOut.bounds[i] = bnds[i] = q;
55742 break;
55743 }
55744 }
55745 }
55746
55747 if(containerOut.autorange === false) {
55748 var rng = containerOut.range;
55749
55750 // if bounds are bigger than the (set) range, disable break
55751 if(rng[0] < rng[1]) {
55752 if(bnds[0] < rng[0] && bnds[1] > rng[1]) {
55753 itemOut.enabled = false;
55754 return;
55755 }
55756 } else if(bnds[0] > rng[0] && bnds[1] < rng[1]) {
55757 itemOut.enabled = false;
55758 return;
55759 }
55760 }
55761 } else {
55762 var values = coerce('values');
55763
55764 if(values && values.length) {
55765 coerce('dvalue');
55766 } else {
55767 itemOut.enabled = false;
55768 return;
55769 }
55770 }
55771 }
55772}
55773
55774// these numbers are one more than what bounds would be mapped to
55775var dayStrToNum = {
55776 sun: 1,
55777 mon: 2,
55778 tue: 3,
55779 wed: 4,
55780 thu: 5,
55781 fri: 6,
55782 sat: 7
55783};
55784
55785function indexOfDay(v) {
55786 if(typeof v !== 'string') return;
55787 return dayStrToNum[
55788 v.substr(0, 3).toLowerCase()
55789 ];
55790}
55791
55792},{"../../lib":178,"../../registry":269,"../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":18}],225:[function(_dereq_,module,exports){
55793/**
55794* Copyright 2012-2020, Plotly, Inc.
55795* All rights reserved.
55796*
55797* This source code is licensed under the MIT license found in the
55798* LICENSE file in the root directory of this source tree.
55799*/
55800
55801'use strict';
55802
55803var Registry = _dereq_('../../registry');
55804
55805var constants = _dereq_('./constants');
55806
55807
55808// convert between axis names (xaxis, xaxis2, etc, elements of gd.layout)
55809// and axis id's (x, x2, etc). Would probably have ditched 'xaxis'
55810// completely in favor of just 'x' if it weren't ingrained in the API etc.
55811exports.id2name = function id2name(id) {
55812 if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
55813 var axNum = id.substr(1);
55814 if(axNum === '1') axNum = '';
55815 return id.charAt(0) + 'axis' + axNum;
55816};
55817
55818exports.name2id = function name2id(name) {
55819 if(!name.match(constants.AX_NAME_PATTERN)) return;
55820 var axNum = name.substr(5);
55821 if(axNum === '1') axNum = '';
55822 return name.charAt(0) + axNum;
55823};
55824
55825exports.cleanId = function cleanId(id, axLetter) {
55826 if(!id.match(constants.AX_ID_PATTERN)) return;
55827 if(axLetter && id.charAt(0) !== axLetter) return;
55828
55829 var axNum = id.substr(1).replace(/^0+/, '');
55830 if(axNum === '1') axNum = '';
55831 return id.charAt(0) + axNum;
55832};
55833
55834// get all axis objects, as restricted in listNames
55835exports.list = function(gd, axLetter, only2d) {
55836 var fullLayout = gd._fullLayout;
55837 if(!fullLayout) return [];
55838
55839 var idList = exports.listIds(gd, axLetter);
55840 var out = new Array(idList.length);
55841 var i;
55842
55843 for(i = 0; i < idList.length; i++) {
55844 var idi = idList[i];
55845 out[i] = fullLayout[idi.charAt(0) + 'axis' + idi.substr(1)];
55846 }
55847
55848 if(!only2d) {
55849 var sceneIds3D = fullLayout._subplots.gl3d || [];
55850
55851 for(i = 0; i < sceneIds3D.length; i++) {
55852 var scene = fullLayout[sceneIds3D[i]];
55853
55854 if(axLetter) out.push(scene[axLetter + 'axis']);
55855 else out.push(scene.xaxis, scene.yaxis, scene.zaxis);
55856 }
55857 }
55858
55859 return out;
55860};
55861
55862// get all axis ids, optionally restricted by letter
55863// this only makes sense for 2d axes
55864exports.listIds = function(gd, axLetter) {
55865 var fullLayout = gd._fullLayout;
55866 if(!fullLayout) return [];
55867
55868 var subplotLists = fullLayout._subplots;
55869 if(axLetter) return subplotLists[axLetter + 'axis'];
55870 return subplotLists.xaxis.concat(subplotLists.yaxis);
55871};
55872
55873// get an axis object from its id 'x','x2' etc
55874// optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it
55875exports.getFromId = function(gd, id, type) {
55876 var fullLayout = gd._fullLayout;
55877
55878 if(type === 'x') id = id.replace(/y[0-9]*/, '');
55879 else if(type === 'y') id = id.replace(/x[0-9]*/, '');
55880
55881 return fullLayout[exports.id2name(id)];
55882};
55883
55884// get an axis object of specified type from the containing trace
55885exports.getFromTrace = function(gd, fullTrace, type) {
55886 var fullLayout = gd._fullLayout;
55887 var ax = null;
55888
55889 if(Registry.traceIs(fullTrace, 'gl3d')) {
55890 var scene = fullTrace.scene;
55891 if(scene.substr(0, 5) === 'scene') {
55892 ax = fullLayout[scene][type + 'axis'];
55893 }
55894 } else {
55895 ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
55896 }
55897
55898 return ax;
55899};
55900
55901// sort x, x2, x10, y, y2, y10...
55902exports.idSort = function(id1, id2) {
55903 var letter1 = id1.charAt(0);
55904 var letter2 = id2.charAt(0);
55905 if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1;
55906 return +(id1.substr(1) || 1) - +(id2.substr(1) || 1);
55907};
55908
55909exports.getAxisGroup = function getAxisGroup(fullLayout, axId) {
55910 var matchGroups = fullLayout._axisMatchGroups;
55911
55912 for(var i = 0; i < matchGroups.length; i++) {
55913 var group = matchGroups[i];
55914 if(group[axId]) return 'g' + i;
55915 }
55916 return axId;
55917};
55918
55919},{"../../registry":269,"./constants":228}],226:[function(_dereq_,module,exports){
55920/**
55921* Copyright 2012-2020, Plotly, Inc.
55922* All rights reserved.
55923*
55924* This source code is licensed under the MIT license found in the
55925* LICENSE file in the root directory of this source tree.
55926*/
55927
55928'use strict';
55929
55930function findCategories(ax, opts) {
55931 var dataAttr = opts.dataAttr || ax._id.charAt(0);
55932 var lookup = {};
55933 var axData;
55934 var i, j;
55935
55936 if(opts.axData) {
55937 // non-x/y case
55938 axData = opts.axData;
55939 } else {
55940 // x/y case
55941 axData = [];
55942 for(i = 0; i < opts.data.length; i++) {
55943 var trace = opts.data[i];
55944 if(trace[dataAttr + 'axis'] === ax._id) {
55945 axData.push(trace);
55946 }
55947 }
55948 }
55949
55950 for(i = 0; i < axData.length; i++) {
55951 var vals = axData[i][dataAttr];
55952 for(j = 0; j < vals.length; j++) {
55953 var v = vals[j];
55954 if(v !== null && v !== undefined) {
55955 lookup[v] = 1;
55956 }
55957 }
55958 }
55959
55960 return Object.keys(lookup);
55961}
55962
55963/**
55964 * Fills in category* default and initial categories.
55965 *
55966 * @param {object} containerIn : input axis object
55967 * @param {object} containerOut : full axis object
55968 * @param {function} coerce : Lib.coerce fn wrapper
55969 * @param {object} opts :
55970 * - data {array} : (full) data trace
55971 * OR
55972 * - axData {array} : (full) data associated with axis being coerced here
55973 * - dataAttr {string} : attribute name corresponding to coordinate array
55974 */
55975module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce, opts) {
55976 if(containerOut.type !== 'category') return;
55977
55978 var arrayIn = containerIn.categoryarray;
55979 var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
55980
55981 // override default 'categoryorder' value when non-empty array is supplied
55982 var orderDefault;
55983 if(isValidArray) orderDefault = 'array';
55984
55985 var order = coerce('categoryorder', orderDefault);
55986 var array;
55987
55988 // coerce 'categoryarray' only in array order case
55989 if(order === 'array') {
55990 array = coerce('categoryarray');
55991 }
55992
55993 // cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
55994 if(!isValidArray && order === 'array') {
55995 order = containerOut.categoryorder = 'trace';
55996 }
55997
55998 // set up things for makeCalcdata
55999 if(order === 'trace') {
56000 containerOut._initialCategories = [];
56001 } else if(order === 'array') {
56002 containerOut._initialCategories = array.slice();
56003 } else {
56004 array = findCategories(containerOut, opts).sort();
56005 if(order === 'category ascending') {
56006 containerOut._initialCategories = array;
56007 } else if(order === 'category descending') {
56008 containerOut._initialCategories = array.reverse();
56009 }
56010 }
56011};
56012
56013},{}],227:[function(_dereq_,module,exports){
56014/**
56015* Copyright 2012-2020, Plotly, Inc.
56016* All rights reserved.
56017*
56018* This source code is licensed under the MIT license found in the
56019* LICENSE file in the root directory of this source tree.
56020*/
56021
56022'use strict';
56023
56024var isNumeric = _dereq_('fast-isnumeric');
56025var Lib = _dereq_('../../lib');
56026var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
56027
56028/**
56029 * Return a validated dtick value for this axis
56030 *
56031 * @param {any} dtick: the candidate dtick. valid values are numbers and strings,
56032 * and further constrained depending on the axis type.
56033 * @param {string} axType: the axis type
56034 */
56035exports.dtick = function(dtick, axType) {
56036 var isLog = axType === 'log';
56037 var isDate = axType === 'date';
56038 var isCat = axType === 'category';
56039 var dtickDflt = isDate ? ONEDAY : 1;
56040
56041 if(!dtick) return dtickDflt;
56042
56043 if(isNumeric(dtick)) {
56044 dtick = Number(dtick);
56045 if(dtick <= 0) return dtickDflt;
56046 if(isCat) {
56047 // category dtick must be positive integers
56048 return Math.max(1, Math.round(dtick));
56049 }
56050 if(isDate) {
56051 // date dtick must be at least 0.1ms (our current precision)
56052 return Math.max(0.1, dtick);
56053 }
56054 return dtick;
56055 }
56056
56057 if(typeof dtick !== 'string' || !(isDate || isLog)) {
56058 return dtickDflt;
56059 }
56060
56061 var prefix = dtick.charAt(0);
56062 var dtickNum = dtick.substr(1);
56063 dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0;
56064
56065 if((dtickNum <= 0) || !(
56066 // "M<n>" gives ticks every (integer) n months
56067 (isDate && prefix === 'M' && dtickNum === Math.round(dtickNum)) ||
56068 // "L<f>" gives ticks linearly spaced in data (not in position) every (float) f
56069 (isLog && prefix === 'L') ||
56070 // "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5
56071 (isLog && prefix === 'D' && (dtickNum === 1 || dtickNum === 2))
56072 )) {
56073 return dtickDflt;
56074 }
56075
56076 return dtick;
56077};
56078
56079/**
56080 * Return a validated tick0 for this axis
56081 *
56082 * @param {any} tick0: the candidate tick0. Valid values are numbers and strings,
56083 * further constrained depending on the axis type
56084 * @param {string} axType: the axis type
56085 * @param {string} calendar: for date axes, the calendar to validate/convert with
56086 * @param {any} dtick: an already valid dtick. Only used for D1 and D2 log dticks,
56087 * which do not support tick0 at all.
56088 */
56089exports.tick0 = function(tick0, axType, calendar, dtick) {
56090 if(axType === 'date') {
56091 return Lib.cleanDate(tick0, Lib.dateTick0(calendar));
56092 }
56093 if(dtick === 'D1' || dtick === 'D2') {
56094 // D1 and D2 modes ignore tick0 entirely
56095 return undefined;
56096 }
56097 // Aside from date axes, tick0 must be numeric
56098 return isNumeric(tick0) ? Number(tick0) : 0;
56099};
56100
56101},{"../../constants/numerical":158,"../../lib":178,"fast-isnumeric":18}],228:[function(_dereq_,module,exports){
56102/**
56103* Copyright 2012-2020, Plotly, Inc.
56104* All rights reserved.
56105*
56106* This source code is licensed under the MIT license found in the
56107* LICENSE file in the root directory of this source tree.
56108*/
56109
56110'use strict';
56111
56112var counterRegex = _dereq_('../../lib/regex').counter;
56113
56114module.exports = {
56115 idRegex: {
56116 x: counterRegex('x'),
56117 y: counterRegex('y')
56118 },
56119
56120 attrRegex: counterRegex('[xy]axis'),
56121
56122 // axis match regular expression
56123 xAxisMatch: counterRegex('xaxis'),
56124 yAxisMatch: counterRegex('yaxis'),
56125
56126 // pattern matching axis ids and names
56127 // note that this is more permissive than counterRegex, as
56128 // id2name, name2id, and cleanId accept "x1" etc
56129 AX_ID_PATTERN: /^[xyz][0-9]*$/,
56130 AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/,
56131
56132 // and for 2D subplots
56133 SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/,
56134
56135 HOUR_PATTERN: 'hour',
56136 WEEKDAY_PATTERN: 'day of week',
56137
56138 // pixels to move mouse before you stop clamping to starting point
56139 MINDRAG: 8,
56140
56141 // smallest dimension allowed for a select box
56142 MINSELECT: 12,
56143
56144 // smallest dimension allowed for a zoombox
56145 MINZOOM: 20,
56146
56147 // width of axis drag regions
56148 DRAGGERSIZE: 20,
56149
56150 // max pixels off straight before a lasso select line counts as bent
56151 BENDPX: 1.5,
56152
56153 // delay before a redraw (relayout) after smooth panning and zooming
56154 REDRAWDELAY: 50,
56155
56156 // throttling limit (ms) for selectPoints calls
56157 SELECTDELAY: 100,
56158
56159 // cache ID suffix for throttle
56160 SELECTID: '-select',
56161
56162 // last resort axis ranges for x and y axes if we have no data
56163 DFLTRANGEX: [-1, 6],
56164 DFLTRANGEY: [-1, 4],
56165
56166 // Layers to keep trace types in the right order
56167 // N.B. each 'unique' plot method must have its own layer
56168 traceLayerClasses: [
56169 'imagelayer',
56170 'heatmaplayer',
56171 'contourcarpetlayer', 'contourlayer',
56172 'funnellayer', 'waterfalllayer', 'barlayer',
56173 'carpetlayer',
56174 'violinlayer',
56175 'boxlayer',
56176 'ohlclayer',
56177 'scattercarpetlayer', 'scatterlayer'
56178 ],
56179
56180 clipOnAxisFalseQuery: [
56181 '.scatterlayer',
56182 '.barlayer',
56183 '.funnellayer',
56184 '.waterfalllayer'
56185 ],
56186
56187 layerValue2layerClass: {
56188 'above traces': 'above',
56189 'below traces': 'below'
56190 }
56191};
56192
56193},{"../../lib/regex":193}],229:[function(_dereq_,module,exports){
56194/**
56195* Copyright 2012-2020, Plotly, Inc.
56196* All rights reserved.
56197*
56198* This source code is licensed under the MIT license found in the
56199* LICENSE file in the root directory of this source tree.
56200*/
56201
56202'use strict';
56203
56204var Lib = _dereq_('../../lib');
56205var id2name = _dereq_('./axis_ids').id2name;
56206var scaleZoom = _dereq_('./scale_zoom');
56207var makePadFn = _dereq_('./autorange').makePadFn;
56208var concatExtremes = _dereq_('./autorange').concatExtremes;
56209
56210var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL;
56211var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
56212
56213exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, opts) {
56214 var allAxisIds = opts.allAxisIds;
56215 var layoutOut = opts.layoutOut;
56216 var scaleanchorDflt = opts.scaleanchorDflt;
56217 var constrainDflt = opts.constrainDflt;
56218 var constraintGroups = layoutOut._axisConstraintGroups;
56219 var matchGroups = layoutOut._axisMatchGroups;
56220 var axId = containerOut._id;
56221 var axLetter = axId.charAt(0);
56222 var splomStash = ((layoutOut._splomAxes || {})[axLetter] || {})[axId] || {};
56223 var thisID = containerOut._id;
56224 var letter = thisID.charAt(0);
56225
56226 // coerce the constraint mechanics even if this axis has no scaleanchor
56227 // because it may be the anchor of another axis.
56228 var constrain = coerce('constrain', constrainDflt);
56229 Lib.coerce(containerIn, containerOut, {
56230 constraintoward: {
56231 valType: 'enumerated',
56232 values: letter === 'x' ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'],
56233 dflt: letter === 'x' ? 'center' : 'middle'
56234 }
56235 }, 'constraintoward');
56236
56237 var matches, matchOpts;
56238
56239 if((containerIn.matches || splomStash.matches) && !containerOut.fixedrange) {
56240 matchOpts = getConstraintOpts(matchGroups, thisID, allAxisIds, layoutOut);
56241 matches = Lib.coerce(containerIn, containerOut, {
56242 matches: {
56243 valType: 'enumerated',
56244 values: matchOpts.linkableAxes || [],
56245 dflt: splomStash.matches
56246 }
56247 }, 'matches');
56248 }
56249
56250 // 'matches' wins over 'scaleanchor' (for now)
56251 var scaleanchor, scaleOpts;
56252
56253 if(!matches &&
56254 !(containerOut.fixedrange && constrain !== 'domain') &&
56255 (containerIn.scaleanchor || scaleanchorDflt)
56256 ) {
56257 scaleOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut, constrain);
56258 scaleanchor = Lib.coerce(containerIn, containerOut, {
56259 scaleanchor: {
56260 valType: 'enumerated',
56261 values: scaleOpts.linkableAxes || []
56262 }
56263 }, 'scaleanchor', scaleanchorDflt);
56264 }
56265
56266 if(matches) {
56267 delete containerOut.constrain;
56268 updateConstraintGroups(matchGroups, matchOpts.thisGroup, thisID, matches, 1);
56269 } else if(allAxisIds.indexOf(containerIn.matches) !== -1) {
56270 Lib.warn('ignored ' + containerOut._name + '.matches: "' +
56271 containerIn.matches + '" to avoid either an infinite loop ' +
56272 'or because the target axis has fixed range.');
56273 }
56274
56275 if(scaleanchor) {
56276 var scaleratio = coerce('scaleratio');
56277
56278 // TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
56279 // but that seems hacky. Better way to say "must be a positive number"?
56280 // Of course if you use several super-tiny values you could eventually
56281 // force a product of these to zero and all hell would break loose...
56282 // Likewise with super-huge values.
56283 if(!scaleratio) scaleratio = containerOut.scaleratio = 1;
56284
56285 updateConstraintGroups(constraintGroups, scaleOpts.thisGroup, thisID, scaleanchor, scaleratio);
56286 } else if(allAxisIds.indexOf(containerIn.scaleanchor) !== -1) {
56287 Lib.warn('ignored ' + containerOut._name + '.scaleanchor: "' +
56288 containerIn.scaleanchor + '" to avoid either an infinite loop ' +
56289 'and possibly inconsistent scaleratios, or because the target ' +
56290 'axis has fixed range or this axis declares a *matches* constraint.');
56291 }
56292};
56293
56294// If this axis is already part of a constraint group, we can't
56295// scaleanchor any other axis in that group, or we'd make a loop.
56296// Filter allAxisIds to enforce this, also matching axis types.
56297function getConstraintOpts(groups, thisID, allAxisIds, layoutOut, constrain) {
56298 var doesNotConstrainRange = constrain !== 'range';
56299 var thisType = layoutOut[id2name(thisID)].type;
56300 var i, j, idj, axj;
56301
56302 var linkableAxes = [];
56303 for(j = 0; j < allAxisIds.length; j++) {
56304 idj = allAxisIds[j];
56305 if(idj === thisID) continue;
56306
56307 axj = layoutOut[id2name(idj)];
56308 if(axj.type === thisType) {
56309 if(!axj.fixedrange) {
56310 linkableAxes.push(idj);
56311 } else if(doesNotConstrainRange && axj.anchor) {
56312 // allow domain constraints on subplots where
56313 // BOTH axes have fixedrange:true and constrain:domain
56314 var counterAxj = layoutOut[id2name(axj.anchor)];
56315 if(counterAxj.fixedrange) {
56316 linkableAxes.push(idj);
56317 }
56318 }
56319 }
56320 }
56321
56322 for(i = 0; i < groups.length; i++) {
56323 if(groups[i][thisID]) {
56324 var thisGroup = groups[i];
56325
56326 var linkableAxesNoLoops = [];
56327 for(j = 0; j < linkableAxes.length; j++) {
56328 idj = linkableAxes[j];
56329 if(!thisGroup[idj]) linkableAxesNoLoops.push(idj);
56330 }
56331 return {linkableAxes: linkableAxesNoLoops, thisGroup: thisGroup};
56332 }
56333 }
56334
56335 return {linkableAxes: linkableAxes, thisGroup: null};
56336}
56337
56338/*
56339 * Add this axis to the axis constraint groups, which is the collection
56340 * of axes that are all constrained together on scale.
56341 *
56342 * constraintGroups: a list of objects. each object is
56343 * {axis_id: scale_within_group}, where scale_within_group is
56344 * only important relative to the rest of the group, and defines
56345 * the relative scales between all axes in the group
56346 *
56347 * thisGroup: the group the current axis is already in
56348 * thisID: the id if the current axis
56349 * scaleanchor: the id of the axis to scale it with
56350 * scaleratio: the ratio of this axis to the scaleanchor axis
56351 */
56352function updateConstraintGroups(constraintGroups, thisGroup, thisID, scaleanchor, scaleratio) {
56353 var i, j, groupi, keyj, thisGroupIndex;
56354
56355 if(thisGroup === null) {
56356 thisGroup = {};
56357 thisGroup[thisID] = 1;
56358 thisGroupIndex = constraintGroups.length;
56359 constraintGroups.push(thisGroup);
56360 } else {
56361 thisGroupIndex = constraintGroups.indexOf(thisGroup);
56362 }
56363
56364 var thisGroupKeys = Object.keys(thisGroup);
56365
56366 // we know that this axis isn't in any other groups, but we don't know
56367 // about the scaleanchor axis. If it is, we need to merge the groups.
56368 for(i = 0; i < constraintGroups.length; i++) {
56369 groupi = constraintGroups[i];
56370 if(i !== thisGroupIndex && groupi[scaleanchor]) {
56371 var baseScale = groupi[scaleanchor];
56372 for(j = 0; j < thisGroupKeys.length; j++) {
56373 keyj = thisGroupKeys[j];
56374 groupi[keyj] = baseScale * scaleratio * thisGroup[keyj];
56375 }
56376 constraintGroups.splice(thisGroupIndex, 1);
56377 return;
56378 }
56379 }
56380
56381 // otherwise, we insert the new scaleanchor axis as the base scale (1)
56382 // in its group, and scale the rest of the group to it
56383 if(scaleratio !== 1) {
56384 for(j = 0; j < thisGroupKeys.length; j++) {
56385 thisGroup[thisGroupKeys[j]] *= scaleratio;
56386 }
56387 }
56388 thisGroup[scaleanchor] = 1;
56389}
56390
56391exports.enforce = function enforce(gd) {
56392 var fullLayout = gd._fullLayout;
56393 var constraintGroups = fullLayout._axisConstraintGroups || [];
56394
56395 var i, j, axisID, ax, normScale, mode, factor;
56396
56397 for(i = 0; i < constraintGroups.length; i++) {
56398 var group = constraintGroups[i];
56399 var axisIDs = Object.keys(group);
56400
56401 var minScale = Infinity;
56402 var maxScale = 0;
56403 // mostly matchScale will be the same as minScale
56404 // ie we expand axis ranges to encompass *everything*
56405 // that's currently in any of their ranges, but during
56406 // autorange of a subset of axes we will ignore other
56407 // axes for this purpose.
56408 var matchScale = Infinity;
56409 var normScales = {};
56410 var axes = {};
56411 var hasAnyDomainConstraint = false;
56412
56413 // find the (normalized) scale of each axis in the group
56414 for(j = 0; j < axisIDs.length; j++) {
56415 axisID = axisIDs[j];
56416 axes[axisID] = ax = fullLayout[id2name(axisID)];
56417
56418 if(ax._inputDomain) ax.domain = ax._inputDomain.slice();
56419 else ax._inputDomain = ax.domain.slice();
56420
56421 if(!ax._inputRange) ax._inputRange = ax.range.slice();
56422
56423 // set axis scale here so we can use _m rather than
56424 // having to calculate it from length and range
56425 ax.setScale();
56426
56427 // abs: inverted scales still satisfy the constraint
56428 normScales[axisID] = normScale = Math.abs(ax._m) / group[axisID];
56429 minScale = Math.min(minScale, normScale);
56430 if(ax.constrain === 'domain' || !ax._constraintShrinkable) {
56431 matchScale = Math.min(matchScale, normScale);
56432 }
56433
56434 // this has served its purpose, so remove it
56435 delete ax._constraintShrinkable;
56436 maxScale = Math.max(maxScale, normScale);
56437
56438 if(ax.constrain === 'domain') hasAnyDomainConstraint = true;
56439 }
56440
56441 // Do we have a constraint mismatch? Give a small buffer for rounding errors
56442 if(minScale > ALMOST_EQUAL * maxScale && !hasAnyDomainConstraint) continue;
56443
56444 // now increase any ranges we need to until all normalized scales are equal
56445 for(j = 0; j < axisIDs.length; j++) {
56446 axisID = axisIDs[j];
56447 normScale = normScales[axisID];
56448 ax = axes[axisID];
56449 mode = ax.constrain;
56450
56451 // even if the scale didn't change, if we're shrinking domain
56452 // we need to recalculate in case `constraintoward` changed
56453 if(normScale !== matchScale || mode === 'domain') {
56454 factor = normScale / matchScale;
56455
56456 if(mode === 'range') {
56457 scaleZoom(ax, factor);
56458 } else {
56459 // mode === 'domain'
56460
56461 var inputDomain = ax._inputDomain;
56462 var domainShrunk = (ax.domain[1] - ax.domain[0]) /
56463 (inputDomain[1] - inputDomain[0]);
56464 var rangeShrunk = (ax.r2l(ax.range[1]) - ax.r2l(ax.range[0])) /
56465 (ax.r2l(ax._inputRange[1]) - ax.r2l(ax._inputRange[0]));
56466
56467 factor /= domainShrunk;
56468
56469 if(factor * rangeShrunk < 1) {
56470 // we've asked to magnify the axis more than we can just by
56471 // enlarging the domain - so we need to constrict range
56472 ax.domain = ax._input.domain = inputDomain.slice();
56473 scaleZoom(ax, factor);
56474 continue;
56475 }
56476
56477 if(rangeShrunk < 1) {
56478 // the range has previously been constricted by ^^, but we've
56479 // switched to the domain-constricted regime, so reset range
56480 ax.range = ax._input.range = ax._inputRange.slice();
56481 factor *= rangeShrunk;
56482 }
56483
56484 if(ax.autorange) {
56485 /*
56486 * range & factor may need to change because range was
56487 * calculated for the larger scaling, so some pixel
56488 * paddings may get cut off when we reduce the domain.
56489 *
56490 * This is easier than the regular autorange calculation
56491 * because we already know the scaling `m`, but we still
56492 * need to cut out impossible constraints (like
56493 * annotations with super-long arrows). That's what
56494 * outerMin/Max are for - if the expansion was going to
56495 * go beyond the original domain, it must be impossible
56496 */
56497 var rl0 = ax.r2l(ax.range[0]);
56498 var rl1 = ax.r2l(ax.range[1]);
56499 var rangeCenter = (rl0 + rl1) / 2;
56500 var rangeMin = rangeCenter;
56501 var rangeMax = rangeCenter;
56502 var halfRange = Math.abs(rl1 - rangeCenter);
56503 // extra tiny bit for rounding errors, in case we actually
56504 // *are* expanding to the full domain
56505 var outerMin = rangeCenter - halfRange * factor * 1.0001;
56506 var outerMax = rangeCenter + halfRange * factor * 1.0001;
56507 var getPad = makePadFn(ax);
56508
56509 updateDomain(ax, factor);
56510 var m = Math.abs(ax._m);
56511 var extremes = concatExtremes(gd, ax);
56512 var minArray = extremes.min;
56513 var maxArray = extremes.max;
56514 var newVal;
56515 var k;
56516
56517 for(k = 0; k < minArray.length; k++) {
56518 newVal = minArray[k].val - getPad(minArray[k]) / m;
56519 if(newVal > outerMin && newVal < rangeMin) {
56520 rangeMin = newVal;
56521 }
56522 }
56523
56524 for(k = 0; k < maxArray.length; k++) {
56525 newVal = maxArray[k].val + getPad(maxArray[k]) / m;
56526 if(newVal < outerMax && newVal > rangeMax) {
56527 rangeMax = newVal;
56528 }
56529 }
56530
56531 var domainExpand = (rangeMax - rangeMin) / (2 * halfRange);
56532 factor /= domainExpand;
56533
56534 rangeMin = ax.l2r(rangeMin);
56535 rangeMax = ax.l2r(rangeMax);
56536 ax.range = ax._input.range = (rl0 < rl1) ?
56537 [rangeMin, rangeMax] : [rangeMax, rangeMin];
56538 }
56539
56540 updateDomain(ax, factor);
56541 }
56542 }
56543 }
56544 }
56545};
56546
56547// For use before autoranging, check if this axis was previously constrained
56548// by domain but no longer is
56549exports.clean = function clean(gd, ax) {
56550 if(ax._inputDomain) {
56551 var isConstrained = false;
56552 var axId = ax._id;
56553 var constraintGroups = gd._fullLayout._axisConstraintGroups;
56554 for(var j = 0; j < constraintGroups.length; j++) {
56555 if(constraintGroups[j][axId]) {
56556 isConstrained = true;
56557 break;
56558 }
56559 }
56560 if(!isConstrained || ax.constrain !== 'domain') {
56561 ax._input.domain = ax.domain = ax._inputDomain;
56562 delete ax._inputDomain;
56563 }
56564 }
56565};
56566
56567function updateDomain(ax, factor) {
56568 var inputDomain = ax._inputDomain;
56569 var centerFraction = FROM_BL[ax.constraintoward];
56570 var center = inputDomain[0] + (inputDomain[1] - inputDomain[0]) * centerFraction;
56571
56572 ax.domain = ax._input.domain = [
56573 center + (inputDomain[0] - center) / factor,
56574 center + (inputDomain[1] - center) / factor
56575 ];
56576 ax.setScale();
56577}
56578
56579},{"../../constants/alignment":154,"../../constants/numerical":158,"../../lib":178,"./autorange":221,"./axis_ids":225,"./scale_zoom":240}],230:[function(_dereq_,module,exports){
56580/**
56581* Copyright 2012-2020, Plotly, Inc.
56582* All rights reserved.
56583*
56584* This source code is licensed under the MIT license found in the
56585* LICENSE file in the root directory of this source tree.
56586*/
56587
56588'use strict';
56589
56590var d3 = _dereq_('d3');
56591var tinycolor = _dereq_('tinycolor2');
56592var supportsPassive = _dereq_('has-passive-events');
56593
56594var Registry = _dereq_('../../registry');
56595var Lib = _dereq_('../../lib');
56596var svgTextUtils = _dereq_('../../lib/svg_text_utils');
56597var Color = _dereq_('../../components/color');
56598var Drawing = _dereq_('../../components/drawing');
56599var Fx = _dereq_('../../components/fx');
56600var Axes = _dereq_('./axes');
56601var setCursor = _dereq_('../../lib/setcursor');
56602var dragElement = _dereq_('../../components/dragelement');
56603var helpers = _dereq_('../../components/dragelement/helpers');
56604var selectingOrDrawing = helpers.selectingOrDrawing;
56605var freeMode = helpers.freeMode;
56606
56607var FROM_TL = _dereq_('../../constants/alignment').FROM_TL;
56608var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
56609var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
56610
56611var Plots = _dereq_('../plots');
56612
56613var getFromId = _dereq_('./axis_ids').getFromId;
56614var prepSelect = _dereq_('./select').prepSelect;
56615var clearSelect = _dereq_('./select').clearSelect;
56616var selectOnClick = _dereq_('./select').selectOnClick;
56617var scaleZoom = _dereq_('./scale_zoom');
56618
56619var constants = _dereq_('./constants');
56620var MINDRAG = constants.MINDRAG;
56621var MINZOOM = constants.MINZOOM;
56622
56623// flag for showing "doubleclick to zoom out" only at the beginning
56624var SHOWZOOMOUTTIP = true;
56625
56626// dragBox: create an element to drag one or more axis ends
56627// inputs:
56628// plotinfo - which subplot are we making dragboxes on?
56629// x,y,w,h - left, top, width, height of the box
56630// ns - how does this drag the vertical axis?
56631// 'n' - top only
56632// 's' - bottom only
56633// 'ns' - top and bottom together, difference unchanged
56634// ew - same for horizontal axis
56635function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
56636 // mouseDown stores ms of first mousedown event in the last
56637 // `gd._context.doubleClickDelay` ms on the drag bars
56638 // numClicks stores how many mousedowns have been seen
56639 // within `gd._context.doubleClickDelay` so we can check for click or doubleclick events
56640 // dragged stores whether a drag has occurred, so we don't have to
56641 // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px
56642 var zoomlayer = gd._fullLayout._zoomlayer;
56643 var isMainDrag = (ns + ew === 'nsew');
56644 var singleEnd = (ns + ew).length === 1;
56645
56646 // main subplot x and y (i.e. found in plotinfo - the main ones)
56647 var xa0, ya0;
56648 // {ax._id: ax} hash objects
56649 var xaHash, yaHash;
56650 // xaHash/yaHash values (arrays)
56651 var xaxes, yaxes;
56652 // main axis offsets
56653 var xs, ys;
56654 // main axis lengths
56655 var pw, ph;
56656 // contains keys 'xaHash', 'yaHash', 'xaxes', and 'yaxes'
56657 // which are the x/y {ax._id: ax} hash objects and their values
56658 // for linked axis relative to this subplot
56659 var links;
56660 // similar to `links` but for matching axes
56661 var matches;
56662 // set to ew/ns val when active, set to '' when inactive
56663 var xActive, yActive;
56664 // are all axes in this subplot are fixed?
56665 var allFixedRanges;
56666 // do we need to edit x/y ranges?
56667 var editX, editY;
56668 // graph-wide optimization flags
56669 var hasScatterGl, hasSplom, hasSVG;
56670 // collected changes to be made to the plot by relayout at the end
56671 var updates;
56672
56673 function recomputeAxisLists() {
56674 xa0 = plotinfo.xaxis;
56675 ya0 = plotinfo.yaxis;
56676 pw = xa0._length;
56677 ph = ya0._length;
56678 xs = xa0._offset;
56679 ys = ya0._offset;
56680
56681 xaHash = {};
56682 xaHash[xa0._id] = xa0;
56683 yaHash = {};
56684 yaHash[ya0._id] = ya0;
56685
56686 // if we're dragging two axes at once, also drag overlays
56687 if(ns && ew) {
56688 var overlays = plotinfo.overlays;
56689 for(var i = 0; i < overlays.length; i++) {
56690 var xa = overlays[i].xaxis;
56691 xaHash[xa._id] = xa;
56692 var ya = overlays[i].yaxis;
56693 yaHash[ya._id] = ya;
56694 }
56695 }
56696
56697 xaxes = hashValues(xaHash);
56698 yaxes = hashValues(yaHash);
56699 xActive = isDirectionActive(xaxes, ew);
56700 yActive = isDirectionActive(yaxes, ns);
56701 allFixedRanges = !yActive && !xActive;
56702
56703 links = calcLinks(gd, gd._fullLayout._axisConstraintGroups, xaHash, yaHash);
56704 matches = calcLinks(gd, gd._fullLayout._axisMatchGroups, xaHash, yaHash);
56705 editX = ew || links.isSubplotConstrained || matches.isSubplotConstrained;
56706 editY = ns || links.isSubplotConstrained || matches.isSubplotConstrained;
56707
56708 var fullLayout = gd._fullLayout;
56709 hasScatterGl = fullLayout._has('scattergl');
56710 hasSplom = fullLayout._has('splom');
56711 hasSVG = fullLayout._has('svg');
56712 }
56713
56714 recomputeAxisLists();
56715
56716 var cursor = getDragCursor(yActive + xActive, gd._fullLayout.dragmode, isMainDrag);
56717 var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h);
56718
56719 // still need to make the element if the axes are disabled
56720 // but nuke its events (except for maindrag which needs them for hover)
56721 // and stop there
56722 if(allFixedRanges && !isMainDrag) {
56723 dragger.onmousedown = null;
56724 dragger.style.pointerEvents = 'none';
56725 return dragger;
56726 }
56727
56728 var dragOptions = {
56729 element: dragger,
56730 gd: gd,
56731 plotinfo: plotinfo
56732 };
56733
56734 dragOptions.prepFn = function(e, startX, startY) {
56735 var dragModePrev = dragOptions.dragmode;
56736 var dragModeNow = gd._fullLayout.dragmode;
56737 if(dragModeNow !== dragModePrev) {
56738 dragOptions.dragmode = dragModeNow;
56739 }
56740
56741 recomputeAxisLists();
56742
56743 if(!allFixedRanges) {
56744 if(isMainDrag) {
56745 // main dragger handles all drag modes, and changes
56746 // to pan (or to zoom if it already is pan) on shift
56747 if(e.shiftKey) {
56748 if(dragModeNow === 'pan') dragModeNow = 'zoom';
56749 else if(!selectingOrDrawing(dragModeNow)) dragModeNow = 'pan';
56750 } else if(e.ctrlKey) {
56751 dragModeNow = 'pan';
56752 }
56753 } else {
56754 // all other draggers just pan
56755 dragModeNow = 'pan';
56756 }
56757 }
56758
56759 if(freeMode(dragModeNow)) dragOptions.minDrag = 1;
56760 else dragOptions.minDrag = undefined;
56761
56762 if(selectingOrDrawing(dragModeNow)) {
56763 dragOptions.xaxes = xaxes;
56764 dragOptions.yaxes = yaxes;
56765 // this attaches moveFn, clickFn, doneFn on dragOptions
56766 prepSelect(e, startX, startY, dragOptions, dragModeNow);
56767 } else {
56768 dragOptions.clickFn = clickFn;
56769 if(selectingOrDrawing(dragModePrev)) {
56770 // TODO Fix potential bug
56771 // Note: clearing / resetting selection state only happens, when user
56772 // triggers at least one interaction in pan/zoom mode. Otherwise, the
56773 // select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection
56774 // cache isn't cleared. So when the user switches back to select/lasso and
56775 // 'adds to a selection' with Shift, the "old", seemingly removed outlines
56776 // are redrawn again because the selection cache still holds their coordinates.
56777 // However, this isn't easily solved, since plots.js would need
56778 // to have a reference to the dragOptions object (which holds the
56779 // selection cache).
56780 clearAndResetSelect();
56781 }
56782
56783 if(!allFixedRanges) {
56784 if(dragModeNow === 'zoom') {
56785 dragOptions.moveFn = zoomMove;
56786 dragOptions.doneFn = zoomDone;
56787
56788 // zoomMove takes care of the threshold, but we need to
56789 // minimize this so that constrained zoom boxes will flip
56790 // orientation at the right place
56791 dragOptions.minDrag = 1;
56792
56793 zoomPrep(e, startX, startY);
56794 } else if(dragModeNow === 'pan') {
56795 dragOptions.moveFn = plotDrag;
56796 dragOptions.doneFn = dragTail;
56797 }
56798 }
56799 }
56800
56801 gd._fullLayout._redrag = function() {
56802 var dragDataNow = gd._dragdata;
56803
56804 if(dragDataNow && dragDataNow.element === dragger) {
56805 var dragModeNow = gd._fullLayout.dragmode;
56806
56807 if(!selectingOrDrawing(dragModeNow)) {
56808 recomputeAxisLists();
56809 updateSubplots([0, 0, pw, ph]);
56810 dragOptions.moveFn(dragDataNow.dx, dragDataNow.dy);
56811 }
56812
56813 // TODO should we try to "re-select" under select/lasso modes?
56814 // probably best to wait for https://github.com/plotly/plotly.js/issues/1851
56815 }
56816 };
56817 };
56818
56819 function clearAndResetSelect() {
56820 // clear selection polygon cache (if any)
56821 dragOptions.plotinfo.selection = false;
56822 // clear selection outlines
56823 clearSelect(gd);
56824 }
56825
56826 function clickFn(numClicks, evt) {
56827 var gd = dragOptions.gd;
56828 if(gd._fullLayout._activeShapeIndex >= 0) {
56829 gd._fullLayout._deactivateShape(gd);
56830 return;
56831 }
56832
56833 var clickmode = gd._fullLayout.clickmode;
56834
56835 removeZoombox(gd);
56836
56837 if(numClicks === 2 && !singleEnd) doubleClick();
56838
56839 if(isMainDrag) {
56840 if(clickmode.indexOf('select') > -1) {
56841 selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions);
56842 }
56843
56844 if(clickmode.indexOf('event') > -1) {
56845 Fx.click(gd, evt, plotinfo.id);
56846 }
56847 } else if(numClicks === 1 && singleEnd) {
56848 var ax = ns ? ya0 : xa0;
56849 var end = (ns === 's' || ew === 'w') ? 0 : 1;
56850 var attrStr = ax._name + '.range[' + end + ']';
56851 var initialText = getEndText(ax, end);
56852 var hAlign = 'left';
56853 var vAlign = 'middle';
56854
56855 if(ax.fixedrange) return;
56856
56857 if(ns) {
56858 vAlign = (ns === 'n') ? 'top' : 'bottom';
56859 if(ax.side === 'right') hAlign = 'right';
56860 } else if(ew === 'e') hAlign = 'right';
56861
56862 if(gd._context.showAxisRangeEntryBoxes) {
56863 d3.select(dragger)
56864 .call(svgTextUtils.makeEditable, {
56865 gd: gd,
56866 immediate: true,
56867 background: gd._fullLayout.paper_bgcolor,
56868 text: String(initialText),
56869 fill: ax.tickfont ? ax.tickfont.color : '#444',
56870 horizontalAlign: hAlign,
56871 verticalAlign: vAlign
56872 })
56873 .on('edit', function(text) {
56874 var v = ax.d2r(text);
56875 if(v !== undefined) {
56876 Registry.call('_guiRelayout', gd, attrStr, v);
56877 }
56878 });
56879 }
56880 }
56881 }
56882
56883 dragElement.init(dragOptions);
56884
56885 // x/y px position at start of drag
56886 var x0, y0;
56887 // bbox object of the zoombox
56888 var box;
56889 // luminance of bg behind zoombox
56890 var lum;
56891 // zoombox path outline
56892 var path0;
56893 // is zoombox dimmed (during drag)
56894 var dimmed;
56895 // 'x'-only, 'y' or 'xy' zooming
56896 var zoomMode;
56897 // zoombox d3 selection
56898 var zb;
56899 // zoombox corner d3 selection
56900 var corners;
56901 // zoom takes over minDrag, so it also has to take over gd._dragged
56902 var zoomDragged;
56903
56904 function zoomPrep(e, startX, startY) {
56905 var dragBBox = dragger.getBoundingClientRect();
56906 x0 = startX - dragBBox.left;
56907 y0 = startY - dragBBox.top;
56908 box = {l: x0, r: x0, w: 0, t: y0, b: y0, h: 0};
56909 lum = gd._hmpixcount ?
56910 (gd._hmlumcount / gd._hmpixcount) :
56911 tinycolor(gd._fullLayout.plot_bgcolor).getLuminance();
56912 path0 = 'M0,0H' + pw + 'V' + ph + 'H0V0';
56913 dimmed = false;
56914 zoomMode = 'xy';
56915 zoomDragged = false;
56916 zb = makeZoombox(zoomlayer, lum, xs, ys, path0);
56917 corners = makeCorners(zoomlayer, xs, ys);
56918 }
56919
56920 function zoomMove(dx0, dy0) {
56921 if(gd._transitioningWithDuration) {
56922 return false;
56923 }
56924
56925 var x1 = Math.max(0, Math.min(pw, dx0 + x0));
56926 var y1 = Math.max(0, Math.min(ph, dy0 + y0));
56927 var dx = Math.abs(x1 - x0);
56928 var dy = Math.abs(y1 - y0);
56929
56930 box.l = Math.min(x0, x1);
56931 box.r = Math.max(x0, x1);
56932 box.t = Math.min(y0, y1);
56933 box.b = Math.max(y0, y1);
56934
56935 function noZoom() {
56936 zoomMode = '';
56937 box.r = box.l;
56938 box.t = box.b;
56939 corners.attr('d', 'M0,0Z');
56940 }
56941
56942 if(links.isSubplotConstrained) {
56943 if(dx > MINZOOM || dy > MINZOOM) {
56944 zoomMode = 'xy';
56945 if(dx / pw > dy / ph) {
56946 dy = dx * ph / pw;
56947 if(y0 > y1) box.t = y0 - dy;
56948 else box.b = y0 + dy;
56949 } else {
56950 dx = dy * pw / ph;
56951 if(x0 > x1) box.l = x0 - dx;
56952 else box.r = x0 + dx;
56953 }
56954 corners.attr('d', xyCorners(box));
56955 } else {
56956 noZoom();
56957 }
56958 } else if(matches.isSubplotConstrained) {
56959 if(dx > MINZOOM || dy > MINZOOM) {
56960 zoomMode = 'xy';
56961
56962 var r0 = Math.min(box.l / pw, (ph - box.b) / ph);
56963 var r1 = Math.max(box.r / pw, (ph - box.t) / ph);
56964
56965 box.l = r0 * pw;
56966 box.r = r1 * pw;
56967 box.b = (1 - r0) * ph;
56968 box.t = (1 - r1) * ph;
56969 corners.attr('d', xyCorners(box));
56970 } else {
56971 noZoom();
56972 }
56973 } else if(!yActive || dy < Math.min(Math.max(dx * 0.6, MINDRAG), MINZOOM)) {
56974 // look for small drags in one direction or the other,
56975 // and only drag the other axis
56976
56977 if(dx < MINDRAG || !xActive) {
56978 noZoom();
56979 } else {
56980 box.t = 0;
56981 box.b = ph;
56982 zoomMode = 'x';
56983 corners.attr('d', xCorners(box, y0));
56984 }
56985 } else if(!xActive || dx < Math.min(dy * 0.6, MINZOOM)) {
56986 box.l = 0;
56987 box.r = pw;
56988 zoomMode = 'y';
56989 corners.attr('d', yCorners(box, x0));
56990 } else {
56991 zoomMode = 'xy';
56992 corners.attr('d', xyCorners(box));
56993 }
56994 box.w = box.r - box.l;
56995 box.h = box.b - box.t;
56996
56997 if(zoomMode) zoomDragged = true;
56998 gd._dragged = zoomDragged;
56999
57000 updateZoombox(zb, corners, box, path0, dimmed, lum);
57001 computeZoomUpdates();
57002 gd.emit('plotly_relayouting', updates);
57003 dimmed = true;
57004 }
57005
57006 function computeZoomUpdates() {
57007 updates = {};
57008
57009 // TODO: edit linked axes in zoomAxRanges and in dragTail
57010 if(zoomMode === 'xy' || zoomMode === 'x') {
57011 zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes);
57012 updateMatchedAxRange('x', updates);
57013 }
57014 if(zoomMode === 'xy' || zoomMode === 'y') {
57015 zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes);
57016 updateMatchedAxRange('y', updates);
57017 }
57018 }
57019
57020 function zoomDone() {
57021 computeZoomUpdates();
57022 removeZoombox(gd);
57023 dragTail();
57024 showDoubleClickNotifier(gd);
57025 }
57026
57027 // scroll zoom, on all draggers except corners
57028 var scrollViewBox = [0, 0, pw, ph];
57029 // wait a little after scrolling before redrawing
57030 var redrawTimer = null;
57031 var REDRAWDELAY = constants.REDRAWDELAY;
57032 var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo;
57033
57034 function zoomWheel(e) {
57035 // deactivate mousewheel scrolling on embedded graphs
57036 // devs can override this with layout._enablescrollzoom,
57037 // but _ ensures this setting won't leave their page
57038 if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
57039 return;
57040 }
57041
57042 clearAndResetSelect();
57043
57044 // If a transition is in progress, then disable any behavior:
57045 if(gd._transitioningWithDuration) {
57046 e.preventDefault();
57047 e.stopPropagation();
57048 return;
57049 }
57050
57051 recomputeAxisLists();
57052
57053 clearTimeout(redrawTimer);
57054
57055 var wheelDelta = -e.deltaY;
57056 if(!isFinite(wheelDelta)) wheelDelta = e.wheelDelta / 10;
57057 if(!isFinite(wheelDelta)) {
57058 Lib.log('Did not find wheel motion attributes: ', e);
57059 return;
57060 }
57061
57062 var zoom = Math.exp(-Math.min(Math.max(wheelDelta, -20), 20) / 200);
57063 var gbb = mainplot.draglayer.select('.nsewdrag').node().getBoundingClientRect();
57064 var xfrac = (e.clientX - gbb.left) / gbb.width;
57065 var yfrac = (gbb.bottom - e.clientY) / gbb.height;
57066 var i;
57067
57068 function zoomWheelOneAxis(ax, centerFraction, zoom) {
57069 if(ax.fixedrange) return;
57070
57071 var axRange = Lib.simpleMap(ax.range, ax.r2l);
57072 var v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction;
57073 function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); }
57074 ax.range = axRange.map(doZoom);
57075 }
57076
57077 if(editX) {
57078 // if we're only zooming this axis because of constraints,
57079 // zoom it about the center
57080 if(!ew) xfrac = 0.5;
57081
57082 for(i = 0; i < xaxes.length; i++) {
57083 zoomWheelOneAxis(xaxes[i], xfrac, zoom);
57084 }
57085 updateMatchedAxRange('x');
57086
57087 scrollViewBox[2] *= zoom;
57088 scrollViewBox[0] += scrollViewBox[2] * xfrac * (1 / zoom - 1);
57089 }
57090 if(editY) {
57091 if(!ns) yfrac = 0.5;
57092
57093 for(i = 0; i < yaxes.length; i++) {
57094 zoomWheelOneAxis(yaxes[i], yfrac, zoom);
57095 }
57096 updateMatchedAxRange('y');
57097
57098 scrollViewBox[3] *= zoom;
57099 scrollViewBox[1] += scrollViewBox[3] * (1 - yfrac) * (1 / zoom - 1);
57100 }
57101
57102 // viewbox redraw at first
57103 updateSubplots(scrollViewBox);
57104 ticksAndAnnotations();
57105
57106 gd.emit('plotly_relayouting', updates);
57107
57108 // then replot after a delay to make sure
57109 // no more scrolling is coming
57110 redrawTimer = setTimeout(function() {
57111 scrollViewBox = [0, 0, pw, ph];
57112 dragTail();
57113 }, REDRAWDELAY);
57114
57115 e.preventDefault();
57116 return;
57117 }
57118
57119 // everything but the corners gets wheel zoom
57120 if(ns.length * ew.length !== 1) {
57121 attachWheelEventHandler(dragger, zoomWheel);
57122 }
57123
57124 // plotDrag: move the plot in response to a drag
57125 function plotDrag(dx, dy) {
57126 // If a transition is in progress, then disable any behavior:
57127 if(gd._transitioningWithDuration) {
57128 return;
57129 }
57130
57131 // prevent axis drawing from monkeying with margins until we're done
57132 gd._fullLayout._replotting = true;
57133
57134 if(xActive === 'ew' || yActive === 'ns') {
57135 if(xActive) {
57136 dragAxList(xaxes, dx);
57137 updateMatchedAxRange('x');
57138 }
57139 if(yActive) {
57140 dragAxList(yaxes, dy);
57141 updateMatchedAxRange('y');
57142 }
57143 updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]);
57144 ticksAndAnnotations();
57145 gd.emit('plotly_relayouting', updates);
57146 return;
57147 }
57148
57149 // dz: set a new value for one end (0 or 1) of an axis array axArray,
57150 // and return a pixel shift for that end for the viewbox
57151 // based on pixel drag distance d
57152 // TODO: this makes (generally non-fatal) errors when you get
57153 // near floating point limits
57154 function dz(axArray, end, d) {
57155 var otherEnd = 1 - end;
57156 var movedAx;
57157 var newLinearizedEnd;
57158 for(var i = 0; i < axArray.length; i++) {
57159 var axi = axArray[i];
57160 if(axi.fixedrange) continue;
57161 movedAx = axi;
57162 newLinearizedEnd = axi._rl[otherEnd] +
57163 (axi._rl[end] - axi._rl[otherEnd]) / dZoom(d / axi._length);
57164 var newEnd = axi.l2r(newLinearizedEnd);
57165
57166 // if l2r comes back false or undefined, it means we've dragged off
57167 // the end of valid ranges - so stop.
57168 if(newEnd !== false && newEnd !== undefined) axi.range[end] = newEnd;
57169 }
57170 return movedAx._length * (movedAx._rl[end] - newLinearizedEnd) /
57171 (movedAx._rl[end] - movedAx._rl[otherEnd]);
57172 }
57173
57174 if(links.isSubplotConstrained && xActive && yActive) {
57175 // dragging a corner of a constrained subplot:
57176 // respect the fixed corner, but harmonize dx and dy
57177 var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1;
57178 var dxyFraction = (dx / pw + dxySign * dy / ph) / 2;
57179 dx = dxyFraction * pw;
57180 dy = dxySign * dxyFraction * ph;
57181 }
57182
57183 if(xActive === 'w') dx = dz(xaxes, 0, dx);
57184 else if(xActive === 'e') dx = dz(xaxes, 1, -dx);
57185 else if(!xActive) dx = 0;
57186
57187 if(yActive === 'n') dy = dz(yaxes, 1, dy);
57188 else if(yActive === 's') dy = dz(yaxes, 0, -dy);
57189 else if(!yActive) dy = 0;
57190
57191 var xStart = (xActive === 'w') ? dx : 0;
57192 var yStart = (yActive === 'n') ? dy : 0;
57193
57194 if(links.isSubplotConstrained) {
57195 var i;
57196 if(!xActive && yActive.length === 1) {
57197 // dragging one end of the y axis of a constrained subplot
57198 // scale the other axis the same about its middle
57199 for(i = 0; i < xaxes.length; i++) {
57200 xaxes[i].range = xaxes[i]._r.slice();
57201 scaleZoom(xaxes[i], 1 - dy / ph);
57202 }
57203 dx = dy * pw / ph;
57204 xStart = dx / 2;
57205 }
57206 if(!yActive && xActive.length === 1) {
57207 for(i = 0; i < yaxes.length; i++) {
57208 yaxes[i].range = yaxes[i]._r.slice();
57209 scaleZoom(yaxes[i], 1 - dx / pw);
57210 }
57211 dy = dx * ph / pw;
57212 yStart = dy / 2;
57213 }
57214 }
57215
57216 updateMatchedAxRange('x');
57217 updateMatchedAxRange('y');
57218 updateSubplots([xStart, yStart, pw - dx, ph - dy]);
57219 ticksAndAnnotations();
57220 gd.emit('plotly_relayouting', updates);
57221 }
57222
57223 function updateMatchedAxRange(axLetter, out) {
57224 var matchedAxes = matches.isSubplotConstrained ?
57225 {x: yaxes, y: xaxes}[axLetter] :
57226 matches[axLetter + 'axes'];
57227
57228 var constrainedAxes = matches.isSubplotConstrained ?
57229 {x: xaxes, y: yaxes}[axLetter] :
57230 [];
57231
57232 for(var i = 0; i < matchedAxes.length; i++) {
57233 var ax = matchedAxes[i];
57234 var axId = ax._id;
57235 var axId2 = matches.xLinks[axId] || matches.yLinks[axId];
57236 var ax2 = constrainedAxes[0] || xaHash[axId2] || yaHash[axId2];
57237
57238 if(ax2) {
57239 if(out) {
57240 // zoombox case - don't mutate 'range', just add keys in 'updates'
57241 out[ax._name + '.range[0]'] = out[ax2._name + '.range[0]'];
57242 out[ax._name + '.range[1]'] = out[ax2._name + '.range[1]'];
57243 } else {
57244 ax.range = ax2.range.slice();
57245 }
57246 }
57247 }
57248 }
57249
57250 // Draw ticks and annotations (and other components) when ranges change.
57251 // Also records the ranges that have changed for use by update at the end.
57252 function ticksAndAnnotations() {
57253 var activeAxIds = [];
57254 var i;
57255
57256 function pushActiveAxIds(axList) {
57257 for(i = 0; i < axList.length; i++) {
57258 if(!axList[i].fixedrange) activeAxIds.push(axList[i]._id);
57259 }
57260 }
57261
57262 if(editX) {
57263 pushActiveAxIds(xaxes);
57264 pushActiveAxIds(links.xaxes);
57265 pushActiveAxIds(matches.xaxes);
57266 }
57267 if(editY) {
57268 pushActiveAxIds(yaxes);
57269 pushActiveAxIds(links.yaxes);
57270 pushActiveAxIds(matches.yaxes);
57271 }
57272
57273 updates = {};
57274 for(i = 0; i < activeAxIds.length; i++) {
57275 var axId = activeAxIds[i];
57276 var ax = getFromId(gd, axId);
57277 Axes.drawOne(gd, ax, {skipTitle: true});
57278 updates[ax._name + '.range[0]'] = ax.range[0];
57279 updates[ax._name + '.range[1]'] = ax.range[1];
57280 }
57281
57282 Axes.redrawComponents(gd, activeAxIds);
57283 }
57284
57285 function doubleClick() {
57286 if(gd._transitioningWithDuration) return;
57287
57288 var doubleClickConfig = gd._context.doubleClick;
57289
57290 var axList = [];
57291 if(xActive) axList = axList.concat(xaxes);
57292 if(yActive) axList = axList.concat(yaxes);
57293 if(matches.xaxes) axList = axList.concat(matches.xaxes);
57294 if(matches.yaxes) axList = axList.concat(matches.yaxes);
57295
57296 var attrs = {};
57297 var ax, i, rangeInitial;
57298
57299 // For reset+autosize mode:
57300 // If *any* of the main axes is not at its initial range
57301 // (or autoranged, if we have no initial range, to match the logic in
57302 // doubleClickConfig === 'reset' below), we reset.
57303 // If they are *all* at their initial ranges, then we autosize.
57304 if(doubleClickConfig === 'reset+autosize') {
57305 doubleClickConfig = 'autosize';
57306
57307 for(i = 0; i < axList.length; i++) {
57308 ax = axList[i];
57309 if((ax._rangeInitial && (
57310 ax.range[0] !== ax._rangeInitial[0] ||
57311 ax.range[1] !== ax._rangeInitial[1]
57312 )) ||
57313 (!ax._rangeInitial && !ax.autorange)
57314 ) {
57315 doubleClickConfig = 'reset';
57316 break;
57317 }
57318 }
57319 }
57320
57321 if(doubleClickConfig === 'autosize') {
57322 // don't set the linked axes here, so relayout marks them as shrinkable
57323 // and we autosize just to the requested axis/axes
57324 for(i = 0; i < axList.length; i++) {
57325 ax = axList[i];
57326 if(!ax.fixedrange) attrs[ax._name + '.autorange'] = true;
57327 }
57328 } else if(doubleClickConfig === 'reset') {
57329 // when we're resetting, reset all linked axes too, so we get back
57330 // to the fully-auto-with-constraints situation
57331 if(xActive || links.isSubplotConstrained) axList = axList.concat(links.xaxes);
57332 if(yActive && !links.isSubplotConstrained) axList = axList.concat(links.yaxes);
57333
57334 if(links.isSubplotConstrained) {
57335 if(!xActive) axList = axList.concat(xaxes);
57336 else if(!yActive) axList = axList.concat(yaxes);
57337 }
57338
57339 for(i = 0; i < axList.length; i++) {
57340 ax = axList[i];
57341
57342 if(!ax.fixedrange) {
57343 if(!ax._rangeInitial) {
57344 attrs[ax._name + '.autorange'] = true;
57345 } else {
57346 rangeInitial = ax._rangeInitial;
57347 attrs[ax._name + '.range[0]'] = rangeInitial[0];
57348 attrs[ax._name + '.range[1]'] = rangeInitial[1];
57349 }
57350 }
57351 }
57352 }
57353
57354 gd.emit('plotly_doubleclick', null);
57355 Registry.call('_guiRelayout', gd, attrs);
57356 }
57357
57358 // dragTail - finish a drag event with a redraw
57359 function dragTail() {
57360 // put the subplot viewboxes back to default (Because we're going to)
57361 // be repositioning the data in the relayout. But DON'T call
57362 // ticksAndAnnotations again - it's unnecessary and would overwrite `updates`
57363 updateSubplots([0, 0, pw, ph]);
57364
57365 // since we may have been redrawing some things during the drag, we may have
57366 // accumulated MathJax promises - wait for them before we relayout.
57367 Lib.syncOrAsync([
57368 Plots.previousPromises,
57369 function() {
57370 gd._fullLayout._replotting = false;
57371 Registry.call('_guiRelayout', gd, updates);
57372 }
57373 ], gd);
57374 }
57375
57376 // updateSubplots - find all plot viewboxes that should be
57377 // affected by this drag, and update them. look for all plots
57378 // sharing an affected axis (including the one being dragged),
57379 // includes also scattergl and splom logic.
57380 function updateSubplots(viewBox) {
57381 var fullLayout = gd._fullLayout;
57382 var plotinfos = fullLayout._plots;
57383 var subplots = fullLayout._subplots.cartesian;
57384 var i, sp, xa, ya;
57385
57386 if(hasSplom) {
57387 Registry.subplotsRegistry.splom.drag(gd);
57388 }
57389
57390 if(hasScatterGl) {
57391 for(i = 0; i < subplots.length; i++) {
57392 sp = plotinfos[subplots[i]];
57393 xa = sp.xaxis;
57394 ya = sp.yaxis;
57395
57396 if(sp._scene) {
57397 var xrng = Lib.simpleMap(xa.range, xa.r2l);
57398 var yrng = Lib.simpleMap(ya.range, ya.r2l);
57399 sp._scene.update({range: [xrng[0], yrng[0], xrng[1], yrng[1]]});
57400 }
57401 }
57402 }
57403
57404 if(hasSplom || hasScatterGl) {
57405 clearGlCanvases(gd);
57406 redrawReglTraces(gd);
57407 }
57408
57409 if(hasSVG) {
57410 var xScaleFactor = viewBox[2] / xa0._length;
57411 var yScaleFactor = viewBox[3] / ya0._length;
57412
57413 for(i = 0; i < subplots.length; i++) {
57414 sp = plotinfos[subplots[i]];
57415 xa = sp.xaxis;
57416 ya = sp.yaxis;
57417
57418 var editX2 = editX && !xa.fixedrange && xaHash[xa._id];
57419 var editY2 = editY && !ya.fixedrange && yaHash[ya._id];
57420
57421 var xScaleFactor2, yScaleFactor2;
57422 var clipDx, clipDy;
57423
57424 if(editX2) {
57425 xScaleFactor2 = xScaleFactor;
57426 clipDx = ew ? viewBox[0] : getShift(xa, xScaleFactor2);
57427 } else if(matches.xaHash[xa._id]) {
57428 xScaleFactor2 = xScaleFactor;
57429 clipDx = viewBox[0] * xa._length / xa0._length;
57430 } else if(matches.yaHash[xa._id]) {
57431 xScaleFactor2 = yScaleFactor;
57432 clipDx = yActive === 'ns' ?
57433 -viewBox[1] * xa._length / ya0._length :
57434 getShift(xa, xScaleFactor2, {n: 'top', s: 'bottom'}[yActive]);
57435 } else {
57436 xScaleFactor2 = getLinkedScaleFactor(xa, xScaleFactor, yScaleFactor);
57437 clipDx = scaleAndGetShift(xa, xScaleFactor2);
57438 }
57439
57440 if(editY2) {
57441 yScaleFactor2 = yScaleFactor;
57442 clipDy = ns ? viewBox[1] : getShift(ya, yScaleFactor2);
57443 } else if(matches.yaHash[ya._id]) {
57444 yScaleFactor2 = yScaleFactor;
57445 clipDy = viewBox[1] * ya._length / ya0._length;
57446 } else if(matches.xaHash[ya._id]) {
57447 yScaleFactor2 = xScaleFactor;
57448 clipDy = xActive === 'ew' ?
57449 -viewBox[0] * ya._length / xa0._length :
57450 getShift(ya, yScaleFactor2, {e: 'right', w: 'left'}[xActive]);
57451 } else {
57452 yScaleFactor2 = getLinkedScaleFactor(ya, xScaleFactor, yScaleFactor);
57453 clipDy = scaleAndGetShift(ya, yScaleFactor2);
57454 }
57455
57456 // don't scale at all if neither axis is scalable here
57457 if(!xScaleFactor2 && !yScaleFactor2) {
57458 continue;
57459 }
57460
57461 // but if only one is, reset the other axis scaling
57462 if(!xScaleFactor2) xScaleFactor2 = 1;
57463 if(!yScaleFactor2) yScaleFactor2 = 1;
57464
57465 var plotDx = xa._offset - clipDx / xScaleFactor2;
57466 var plotDy = ya._offset - clipDy / yScaleFactor2;
57467
57468 // TODO could be more efficient here:
57469 // setTranslate and setScale do a lot of extra work
57470 // when working independently, should perhaps combine
57471 // them into a single routine.
57472 sp.clipRect
57473 .call(Drawing.setTranslate, clipDx, clipDy)
57474 .call(Drawing.setScale, xScaleFactor2, yScaleFactor2);
57475
57476 sp.plot
57477 .call(Drawing.setTranslate, plotDx, plotDy)
57478 .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
57479
57480 // apply an inverse scale to individual points to counteract
57481 // the scale of the trace group.
57482 // apply only when scale changes, as adjusting the scale of
57483 // all the points can be expansive.
57484 if(xScaleFactor2 !== sp.xScaleFactor || yScaleFactor2 !== sp.yScaleFactor) {
57485 Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2);
57486 Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2);
57487 }
57488
57489 Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp);
57490
57491 // update x/y scaleFactor stash
57492 sp.xScaleFactor = xScaleFactor2;
57493 sp.yScaleFactor = yScaleFactor2;
57494 }
57495 }
57496 }
57497
57498 // Find the appropriate scaling for this axis, if it's linked to the
57499 // dragged axes by constraints. 0 is special, it means this axis shouldn't
57500 // ever be scaled (will be converted to 1 if the other axis is scaled)
57501 function getLinkedScaleFactor(ax, xScaleFactor, yScaleFactor) {
57502 if(ax.fixedrange) return 0;
57503
57504 if(editX && links.xaHash[ax._id]) {
57505 return xScaleFactor;
57506 }
57507 if(editY && (links.isSubplotConstrained ? links.xaHash : links.yaHash)[ax._id]) {
57508 return yScaleFactor;
57509 }
57510 return 0;
57511 }
57512
57513 function scaleAndGetShift(ax, scaleFactor) {
57514 if(scaleFactor) {
57515 ax.range = ax._r.slice();
57516 scaleZoom(ax, scaleFactor);
57517 return getShift(ax, scaleFactor);
57518 }
57519 return 0;
57520 }
57521
57522 function getShift(ax, scaleFactor, from) {
57523 return ax._length * (1 - scaleFactor) * FROM_TL[from || ax.constraintoward || 'middle'];
57524 }
57525
57526 return dragger;
57527}
57528
57529function makeDragger(plotinfo, nodeName, dragClass, cursor) {
57530 var dragger3 = Lib.ensureSingle(plotinfo.draglayer, nodeName, dragClass, function(s) {
57531 s.classed('drag', true)
57532 .style({fill: 'transparent', 'stroke-width': 0})
57533 .attr('data-subplot', plotinfo.id);
57534 });
57535
57536 dragger3.call(setCursor, cursor);
57537
57538 return dragger3.node();
57539}
57540
57541function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) {
57542 var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor);
57543 d3.select(dragger).call(Drawing.setRect, x, y, w, h);
57544 return dragger;
57545}
57546
57547function isDirectionActive(axList, activeVal) {
57548 for(var i = 0; i < axList.length; i++) {
57549 if(!axList[i].fixedrange) return activeVal;
57550 }
57551 return '';
57552}
57553
57554function getEndText(ax, end) {
57555 var initialVal = ax.range[end];
57556 var diff = Math.abs(initialVal - ax.range[1 - end]);
57557 var dig;
57558
57559 // TODO: this should basically be ax.r2d but we're doing extra
57560 // rounding here... can we clean up at all?
57561 if(ax.type === 'date') {
57562 return initialVal;
57563 } else if(ax.type === 'log') {
57564 dig = Math.ceil(Math.max(0, -Math.log(diff) / Math.LN10)) + 3;
57565 return d3.format('.' + dig + 'g')(Math.pow(10, initialVal));
57566 } else { // linear numeric (or category... but just show numbers here)
57567 dig = Math.floor(Math.log(Math.abs(initialVal)) / Math.LN10) -
57568 Math.floor(Math.log(diff) / Math.LN10) + 4;
57569 return d3.format('.' + String(dig) + 'g')(initialVal);
57570 }
57571}
57572
57573function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
57574 for(var i = 0; i < axList.length; i++) {
57575 var axi = axList[i];
57576 if(axi.fixedrange) continue;
57577
57578 if(axi.rangebreaks) {
57579 var isY = axi._id.charAt(0) === 'y';
57580 var r0F = isY ? (1 - r0Fraction) : r0Fraction;
57581 var r1F = isY ? (1 - r1Fraction) : r1Fraction;
57582
57583 updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0F * axi._length));
57584 updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1F * axi._length));
57585 } else {
57586 var axRangeLinear0 = axi._rl[0];
57587 var axRangeLinearSpan = axi._rl[1] - axRangeLinear0;
57588 updates[axi._name + '.range[0]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction);
57589 updates[axi._name + '.range[1]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction);
57590 }
57591 }
57592
57593 // zoom linked axes about their centers
57594 if(linkedAxes && linkedAxes.length) {
57595 var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;
57596 zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, []);
57597 }
57598}
57599
57600function dragAxList(axList, pix) {
57601 for(var i = 0; i < axList.length; i++) {
57602 var axi = axList[i];
57603 if(!axi.fixedrange) {
57604 if(axi.rangebreaks) {
57605 var p0 = 0;
57606 var p1 = axi._length;
57607 var d0 = axi.p2l(p0 + pix) - axi.p2l(p0);
57608 var d1 = axi.p2l(p1 + pix) - axi.p2l(p1);
57609 var delta = (d0 + d1) / 2;
57610
57611 axi.range = [
57612 axi.l2r(axi._rl[0] - delta),
57613 axi.l2r(axi._rl[1] - delta)
57614 ];
57615 } else {
57616 axi.range = [
57617 axi.l2r(axi._rl[0] - pix / axi._m),
57618 axi.l2r(axi._rl[1] - pix / axi._m)
57619 ];
57620 }
57621 }
57622 }
57623}
57624
57625// common transform for dragging one end of an axis
57626// d>0 is compressing scale (cursor is over the plot,
57627// the axis end should move with the cursor)
57628// d<0 is expanding (cursor is off the plot, axis end moves
57629// nonlinearly so you can expand far)
57630function dZoom(d) {
57631 return 1 - ((d >= 0) ? Math.min(d, 0.9) :
57632 1 / (1 / Math.max(d, -0.3) + 3.222));
57633}
57634
57635function getDragCursor(nsew, dragmode, isMainDrag) {
57636 if(!nsew) return 'pointer';
57637 if(nsew === 'nsew') {
57638 // in this case here, clear cursor and
57639 // use the cursor style set on <g .draglayer>
57640 if(isMainDrag) return '';
57641 if(dragmode === 'pan') return 'move';
57642 return 'crosshair';
57643 }
57644 return nsew.toLowerCase() + '-resize';
57645}
57646
57647function makeZoombox(zoomlayer, lum, xs, ys, path0) {
57648 return zoomlayer.append('path')
57649 .attr('class', 'zoombox')
57650 .style({
57651 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
57652 'stroke-width': 0
57653 })
57654 .attr('transform', 'translate(' + xs + ', ' + ys + ')')
57655 .attr('d', path0 + 'Z');
57656}
57657
57658function makeCorners(zoomlayer, xs, ys) {
57659 return zoomlayer.append('path')
57660 .attr('class', 'zoombox-corners')
57661 .style({
57662 fill: Color.background,
57663 stroke: Color.defaultLine,
57664 'stroke-width': 1,
57665 opacity: 0
57666 })
57667 .attr('transform', 'translate(' + xs + ', ' + ys + ')')
57668 .attr('d', 'M0,0Z');
57669}
57670
57671function updateZoombox(zb, corners, box, path0, dimmed, lum) {
57672 zb.attr('d',
57673 path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) +
57674 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z');
57675 transitionZoombox(zb, corners, dimmed, lum);
57676}
57677
57678function transitionZoombox(zb, corners, dimmed, lum) {
57679 if(!dimmed) {
57680 zb.transition()
57681 .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
57682 'rgba(255,255,255,0.3)')
57683 .duration(200);
57684 corners.transition()
57685 .style('opacity', 1)
57686 .duration(200);
57687 }
57688}
57689
57690function removeZoombox(gd) {
57691 d3.select(gd)
57692 .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
57693 .remove();
57694}
57695
57696function showDoubleClickNotifier(gd) {
57697 if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
57698 Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long');
57699 SHOWZOOMOUTTIP = false;
57700 }
57701}
57702
57703function xCorners(box, y0) {
57704 return 'M' +
57705 (box.l - 0.5) + ',' + (y0 - MINZOOM - 0.5) +
57706 'h-3v' + (2 * MINZOOM + 1) + 'h3ZM' +
57707 (box.r + 0.5) + ',' + (y0 - MINZOOM - 0.5) +
57708 'h3v' + (2 * MINZOOM + 1) + 'h-3Z';
57709}
57710
57711function yCorners(box, x0) {
57712 return 'M' +
57713 (x0 - MINZOOM - 0.5) + ',' + (box.t - 0.5) +
57714 'v-3h' + (2 * MINZOOM + 1) + 'v3ZM' +
57715 (x0 - MINZOOM - 0.5) + ',' + (box.b + 0.5) +
57716 'v3h' + (2 * MINZOOM + 1) + 'v-3Z';
57717}
57718
57719function xyCorners(box) {
57720 var clen = Math.floor(Math.min(box.b - box.t, box.r - box.l, MINZOOM) / 2);
57721 return 'M' +
57722 (box.l - 3.5) + ',' + (box.t - 0.5 + clen) + 'h3v' + (-clen) +
57723 'h' + clen + 'v-3h-' + (clen + 3) + 'ZM' +
57724 (box.r + 3.5) + ',' + (box.t - 0.5 + clen) + 'h-3v' + (-clen) +
57725 'h' + (-clen) + 'v-3h' + (clen + 3) + 'ZM' +
57726 (box.r + 3.5) + ',' + (box.b + 0.5 - clen) + 'h-3v' + clen +
57727 'h' + (-clen) + 'v3h' + (clen + 3) + 'ZM' +
57728 (box.l - 3.5) + ',' + (box.b + 0.5 - clen) + 'h3v' + clen +
57729 'h' + clen + 'v3h-' + (clen + 3) + 'Z';
57730}
57731
57732function calcLinks(gd, groups, xaHash, yaHash) {
57733 var isSubplotConstrained = false;
57734 var xLinks = {};
57735 var yLinks = {};
57736 var xID, yID, xLinkID, yLinkID;
57737
57738 for(var i = 0; i < groups.length; i++) {
57739 var group = groups[i];
57740 // check if any of the x axes we're dragging is in this constraint group
57741 for(xID in xaHash) {
57742 if(group[xID]) {
57743 // put the rest of these axes into xLinks, if we're not already
57744 // dragging them, so we know to scale these axes automatically too
57745 // to match the changes in the dragged x axes
57746 for(xLinkID in group) {
57747 if(!(xLinkID.charAt(0) === 'x' ? xaHash : yaHash)[xLinkID]) {
57748 xLinks[xLinkID] = xID;
57749 }
57750 }
57751
57752 // check if the x and y axes of THIS drag are linked
57753 for(yID in yaHash) {
57754 if(group[yID]) isSubplotConstrained = true;
57755 }
57756 }
57757 }
57758
57759 // now check if any of the y axes we're dragging is in this constraint group
57760 // only look for outside links, as we've already checked for links within the dragger
57761 for(yID in yaHash) {
57762 if(group[yID]) {
57763 for(yLinkID in group) {
57764 if(!(yLinkID.charAt(0) === 'x' ? xaHash : yaHash)[yLinkID]) {
57765 yLinks[yLinkID] = yID;
57766 }
57767 }
57768 }
57769 }
57770 }
57771
57772 if(isSubplotConstrained) {
57773 // merge xLinks and yLinks if the subplot is constrained,
57774 // since we'll always apply both anyway and the two will contain
57775 // duplicates
57776 Lib.extendFlat(xLinks, yLinks);
57777 yLinks = {};
57778 }
57779
57780 var xaHashLinked = {};
57781 var xaxesLinked = [];
57782 for(xLinkID in xLinks) {
57783 var xa = getFromId(gd, xLinkID);
57784 xaxesLinked.push(xa);
57785 xaHashLinked[xa._id] = xa;
57786 }
57787
57788 var yaHashLinked = {};
57789 var yaxesLinked = [];
57790 for(yLinkID in yLinks) {
57791 var ya = getFromId(gd, yLinkID);
57792 yaxesLinked.push(ya);
57793 yaHashLinked[ya._id] = ya;
57794 }
57795
57796 return {
57797 xaHash: xaHashLinked,
57798 yaHash: yaHashLinked,
57799 xaxes: xaxesLinked,
57800 yaxes: yaxesLinked,
57801 xLinks: xLinks,
57802 yLinks: yLinks,
57803 isSubplotConstrained: isSubplotConstrained
57804 };
57805}
57806
57807// still seems to be some confusion about onwheel vs onmousewheel...
57808function attachWheelEventHandler(element, handler) {
57809 if(!supportsPassive) {
57810 if(element.onwheel !== undefined) element.onwheel = handler;
57811 else if(element.onmousewheel !== undefined) element.onmousewheel = handler;
57812 } else {
57813 var wheelEventName = element.onwheel !== undefined ? 'wheel' : 'mousewheel';
57814
57815 if(element._onwheel) {
57816 element.removeEventListener(wheelEventName, element._onwheel);
57817 }
57818 element._onwheel = handler;
57819
57820 element.addEventListener(wheelEventName, handler, {passive: false});
57821 }
57822}
57823
57824function hashValues(hash) {
57825 var out = [];
57826 for(var k in hash) out.push(hash[k]);
57827 return out;
57828}
57829
57830module.exports = {
57831 makeDragBox: makeDragBox,
57832
57833 makeDragger: makeDragger,
57834 makeRectDragger: makeRectDragger,
57835 makeZoombox: makeZoombox,
57836 makeCorners: makeCorners,
57837
57838 updateZoombox: updateZoombox,
57839 xyCorners: xyCorners,
57840 transitionZoombox: transitionZoombox,
57841 removeZoombox: removeZoombox,
57842 showDoubleClickNotifier: showDoubleClickNotifier,
57843
57844 attachWheelEventHandler: attachWheelEventHandler
57845};
57846
57847},{"../../components/color":52,"../../components/dragelement":71,"../../components/dragelement/helpers":70,"../../components/drawing":74,"../../components/fx":92,"../../constants/alignment":154,"../../lib":178,"../../lib/clear_gl_canvases":167,"../../lib/setcursor":197,"../../lib/svg_text_utils":199,"../../plot_api/subroutines":213,"../../registry":269,"../plots":256,"./axes":222,"./axis_ids":225,"./constants":228,"./scale_zoom":240,"./select":241,"d3":16,"has-passive-events":21,"tinycolor2":35}],231:[function(_dereq_,module,exports){
57848/**
57849* Copyright 2012-2020, Plotly, Inc.
57850* All rights reserved.
57851*
57852* This source code is licensed under the MIT license found in the
57853* LICENSE file in the root directory of this source tree.
57854*/
57855
57856
57857'use strict';
57858
57859var d3 = _dereq_('d3');
57860
57861var Fx = _dereq_('../../components/fx');
57862var dragElement = _dereq_('../../components/dragelement');
57863var setCursor = _dereq_('../../lib/setcursor');
57864
57865var makeDragBox = _dereq_('./dragbox').makeDragBox;
57866var DRAGGERSIZE = _dereq_('./constants').DRAGGERSIZE;
57867
57868exports.initInteractions = function initInteractions(gd) {
57869 var fullLayout = gd._fullLayout;
57870
57871 if(gd._context.staticPlot) {
57872 // this sweeps up more than just cartesian drag elements...
57873 d3.select(gd).selectAll('.drag').remove();
57874 return;
57875 }
57876
57877 if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return;
57878
57879 var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
57880 // sort overlays last, then by x axis number, then y axis number
57881 if((fullLayout._plots[a].mainplot && true) ===
57882 (fullLayout._plots[b].mainplot && true)) {
57883 var aParts = a.split('y');
57884 var bParts = b.split('y');
57885 return (aParts[0] === bParts[0]) ?
57886 (Number(aParts[1] || 1) - Number(bParts[1] || 1)) :
57887 (Number(aParts[0] || 1) - Number(bParts[0] || 1));
57888 }
57889 return fullLayout._plots[a].mainplot ? 1 : -1;
57890 });
57891
57892 subplots.forEach(function(subplot) {
57893 var plotinfo = fullLayout._plots[subplot];
57894 var xa = plotinfo.xaxis;
57895 var ya = plotinfo.yaxis;
57896
57897 // main and corner draggers need not be repeated for
57898 // overlaid subplots - these draggers drag them all
57899 if(!plotinfo.mainplot) {
57900 // main dragger goes over the grids and data, so we use its
57901 // mousemove events for all data hover effects
57902 var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset,
57903 xa._length, ya._length, 'ns', 'ew');
57904
57905 maindrag.onmousemove = function(evt) {
57906 // This is on `gd._fullLayout`, *not* fullLayout because the reference
57907 // changes by the time this is called again.
57908 gd._fullLayout._rehover = function() {
57909 if((gd._fullLayout._hoversubplot === subplot) && gd._fullLayout._plots[subplot]) {
57910 Fx.hover(gd, evt, subplot);
57911 }
57912 };
57913
57914 Fx.hover(gd, evt, subplot);
57915
57916 // Note that we have *not* used the cached fullLayout variable here
57917 // since that may be outdated when this is called as a callback later on
57918 gd._fullLayout._lasthover = maindrag;
57919 gd._fullLayout._hoversubplot = subplot;
57920 };
57921
57922 /*
57923 * IMPORTANT:
57924 * We must check for the presence of the drag cover here.
57925 * If we don't, a 'mouseout' event is triggered on the
57926 * maindrag before each 'click' event, which has the effect
57927 * of clearing the hoverdata; thus, cancelling the click event.
57928 */
57929 maindrag.onmouseout = function(evt) {
57930 if(gd._dragging) return;
57931
57932 // When the mouse leaves this maindrag, unset the hovered subplot.
57933 // This may cause problems if it leaves the subplot directly *onto*
57934 // another subplot, but that's a tiny corner case at the moment.
57935 gd._fullLayout._hoversubplot = null;
57936
57937 dragElement.unhover(gd, evt);
57938 };
57939
57940 // corner draggers
57941 if(gd._context.showAxisDragHandles) {
57942 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE,
57943 DRAGGERSIZE, DRAGGERSIZE, 'n', 'w');
57944 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE,
57945 DRAGGERSIZE, DRAGGERSIZE, 'n', 'e');
57946 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length,
57947 DRAGGERSIZE, DRAGGERSIZE, 's', 'w');
57948 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length,
57949 DRAGGERSIZE, DRAGGERSIZE, 's', 'e');
57950 }
57951 }
57952 if(gd._context.showAxisDragHandles) {
57953 // x axis draggers - if you have overlaid plots,
57954 // these drag each axis separately
57955 if(subplot === xa._mainSubplot) {
57956 // the y position of the main x axis line
57957 var y0 = xa._mainLinePosition;
57958 if(xa.side === 'top') y0 -= DRAGGERSIZE;
57959 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0,
57960 xa._length * 0.8, DRAGGERSIZE, '', 'ew');
57961 makeDragBox(gd, plotinfo, xa._offset, y0,
57962 xa._length * 0.1, DRAGGERSIZE, '', 'w');
57963 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0,
57964 xa._length * 0.1, DRAGGERSIZE, '', 'e');
57965 }
57966 // y axis draggers
57967 if(subplot === ya._mainSubplot) {
57968 // the x position of the main y axis line
57969 var x0 = ya._mainLinePosition;
57970 if(ya.side !== 'right') x0 -= DRAGGERSIZE;
57971 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1,
57972 DRAGGERSIZE, ya._length * 0.8, 'ns', '');
57973 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9,
57974 DRAGGERSIZE, ya._length * 0.1, 's', '');
57975 makeDragBox(gd, plotinfo, x0, ya._offset,
57976 DRAGGERSIZE, ya._length * 0.1, 'n', '');
57977 }
57978 }
57979 });
57980
57981 // In case you mousemove over some hovertext, send it to Fx.hover too
57982 // we do this so that we can put the hover text in front of everything,
57983 // but still be able to interact with everything as if it isn't there
57984 var hoverLayer = fullLayout._hoverlayer.node();
57985
57986 hoverLayer.onmousemove = function(evt) {
57987 evt.target = gd._fullLayout._lasthover;
57988 Fx.hover(gd, evt, fullLayout._hoversubplot);
57989 };
57990
57991 hoverLayer.onclick = function(evt) {
57992 evt.target = gd._fullLayout._lasthover;
57993 Fx.click(gd, evt);
57994 };
57995
57996 // also delegate mousedowns... TODO: does this actually work?
57997 hoverLayer.onmousedown = function(evt) {
57998 gd._fullLayout._lasthover.onmousedown(evt);
57999 };
58000
58001 exports.updateFx(gd);
58002};
58003
58004// Minimal set of update needed on 'modebar' edits.
58005// We only need to update the <g .draglayer> cursor style.
58006//
58007// Note that changing the axis configuration and/or the fixedrange attribute
58008// should trigger a full initInteractions.
58009exports.updateFx = function(gd) {
58010 var fullLayout = gd._fullLayout;
58011 var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair';
58012 setCursor(fullLayout._draggers, cursor);
58013};
58014
58015},{"../../components/dragelement":71,"../../components/fx":92,"../../lib/setcursor":197,"./constants":228,"./dragbox":230,"d3":16}],232:[function(_dereq_,module,exports){
58016/**
58017* Copyright 2012-2020, Plotly, Inc.
58018* All rights reserved.
58019*
58020* This source code is licensed under the MIT license found in the
58021* LICENSE file in the root directory of this source tree.
58022*/
58023
58024
58025'use strict';
58026
58027function clearOutlineControllers(gd) {
58028 var zoomLayer = gd._fullLayout._zoomlayer;
58029 if(zoomLayer) {
58030 zoomLayer.selectAll('.outline-controllers').remove();
58031 }
58032}
58033
58034function clearSelect(gd) {
58035 var zoomLayer = gd._fullLayout._zoomlayer;
58036 if(zoomLayer) {
58037 // until we get around to persistent selections, remove the outline
58038 // here. The selection itself will be removed when the plot redraws
58039 // at the end.
58040 zoomLayer.selectAll('.select-outline').remove();
58041 }
58042
58043 gd._fullLayout._drawing = false;
58044}
58045
58046module.exports = {
58047 clearOutlineControllers: clearOutlineControllers,
58048 clearSelect: clearSelect
58049};
58050
58051},{}],233:[function(_dereq_,module,exports){
58052/**
58053* Copyright 2012-2020, Plotly, Inc.
58054* All rights reserved.
58055*
58056* This source code is licensed under the MIT license found in the
58057* LICENSE file in the root directory of this source tree.
58058*/
58059
58060
58061'use strict';
58062
58063// in v2 (once log ranges are fixed),
58064// we'll be able to p2r here for all axis types
58065function p2r(ax, v) {
58066 switch(ax.type) {
58067 case 'log':
58068 return ax.p2d(v);
58069 case 'date':
58070 return ax.p2r(v, 0, ax.calendar);
58071 default:
58072 return ax.p2r(v);
58073 }
58074}
58075
58076function r2p(ax, v) {
58077 switch(ax.type) {
58078 case 'log':
58079 return ax.d2p(v);
58080 case 'date':
58081 return ax.r2p(v, 0, ax.calendar);
58082 default:
58083 return ax.r2p(v);
58084 }
58085}
58086
58087function axValue(ax) {
58088 var index = (ax._id.charAt(0) === 'y') ? 1 : 0;
58089 return function(v) { return p2r(ax, v[index]); };
58090}
58091
58092function getTransform(plotinfo) {
58093 return 'translate(' +
58094 plotinfo.xaxis._offset + ',' +
58095 plotinfo.yaxis._offset + ')';
58096}
58097
58098module.exports = {
58099 p2r: p2r,
58100 r2p: r2p,
58101 axValue: axValue,
58102 getTransform: getTransform
58103};
58104
58105},{}],234:[function(_dereq_,module,exports){
58106/**
58107* Copyright 2012-2020, Plotly, Inc.
58108* All rights reserved.
58109*
58110* This source code is licensed under the MIT license found in the
58111* LICENSE file in the root directory of this source tree.
58112*/
58113
58114
58115'use strict';
58116
58117var Registry = _dereq_('../../registry');
58118var Lib = _dereq_('../../lib');
58119
58120/**
58121 * Factory function for checking component arrays for subplot references.
58122 *
58123 * @param {string} containerArrayName: the top-level array in gd.layout to check
58124 * If an item in this container is found that references a cartesian x and/or y axis,
58125 * ensure cartesian is marked as a base plot module and record the axes (and subplot
58126 * if both refs are axes) in gd._fullLayout
58127 *
58128 * @return {function}: with args layoutIn (gd.layout) and layoutOut (gd._fullLayout)
58129 * as expected of a component includeBasePlot method
58130 */
58131module.exports = function makeIncludeComponents(containerArrayName) {
58132 return function includeComponents(layoutIn, layoutOut) {
58133 var array = layoutIn[containerArrayName];
58134 if(!Array.isArray(array)) return;
58135
58136 var Cartesian = Registry.subplotsRegistry.cartesian;
58137 var idRegex = Cartesian.idRegex;
58138 var subplots = layoutOut._subplots;
58139 var xaList = subplots.xaxis;
58140 var yaList = subplots.yaxis;
58141 var cartesianList = subplots.cartesian;
58142 var hasCartesianOrGL2D = layoutOut._has('cartesian') || layoutOut._has('gl2d');
58143
58144 for(var i = 0; i < array.length; i++) {
58145 var itemi = array[i];
58146 if(!Lib.isPlainObject(itemi)) continue;
58147
58148 var xref = itemi.xref;
58149 var yref = itemi.yref;
58150
58151 var hasXref = idRegex.x.test(xref);
58152 var hasYref = idRegex.y.test(yref);
58153 if(hasXref || hasYref) {
58154 if(!hasCartesianOrGL2D) Lib.pushUnique(layoutOut._basePlotModules, Cartesian);
58155
58156 var newAxis = false;
58157 if(hasXref && xaList.indexOf(xref) === -1) {
58158 xaList.push(xref);
58159 newAxis = true;
58160 }
58161 if(hasYref && yaList.indexOf(yref) === -1) {
58162 yaList.push(yref);
58163 newAxis = true;
58164 }
58165
58166 /*
58167 * Notice the logic here: only add a subplot for a component if
58168 * it's referencing both x and y axes AND it's creating a new axis
58169 * so for example if your plot already has xy and x2y2, an annotation
58170 * on x2y or xy2 will not create a new subplot.
58171 */
58172 if(newAxis && hasXref && hasYref) {
58173 cartesianList.push(xref + yref);
58174 }
58175 }
58176 }
58177 };
58178};
58179
58180},{"../../lib":178,"../../registry":269}],235:[function(_dereq_,module,exports){
58181/**
58182* Copyright 2012-2020, Plotly, Inc.
58183* All rights reserved.
58184*
58185* This source code is licensed under the MIT license found in the
58186* LICENSE file in the root directory of this source tree.
58187*/
58188
58189
58190'use strict';
58191
58192var d3 = _dereq_('d3');
58193
58194var Registry = _dereq_('../../registry');
58195var Lib = _dereq_('../../lib');
58196var Plots = _dereq_('../plots');
58197var Drawing = _dereq_('../../components/drawing');
58198
58199var getModuleCalcData = _dereq_('../get_data').getModuleCalcData;
58200var axisIds = _dereq_('./axis_ids');
58201var constants = _dereq_('./constants');
58202var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
58203
58204var ensureSingle = Lib.ensureSingle;
58205
58206function ensureSingleAndAddDatum(parent, nodeType, className) {
58207 return Lib.ensureSingle(parent, nodeType, className, function(s) {
58208 s.datum(className);
58209 });
58210}
58211
58212exports.name = 'cartesian';
58213
58214exports.attr = ['xaxis', 'yaxis'];
58215
58216exports.idRoot = ['x', 'y'];
58217
58218exports.idRegex = constants.idRegex;
58219
58220exports.attrRegex = constants.attrRegex;
58221
58222exports.attributes = _dereq_('./attributes');
58223
58224exports.layoutAttributes = _dereq_('./layout_attributes');
58225
58226exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
58227
58228exports.transitionAxes = _dereq_('./transition_axes');
58229
58230exports.finalizeSubplots = function(layoutIn, layoutOut) {
58231 var subplots = layoutOut._subplots;
58232 var xList = subplots.xaxis;
58233 var yList = subplots.yaxis;
58234 var spSVG = subplots.cartesian;
58235 var spAll = spSVG.concat(subplots.gl2d || []);
58236 var allX = {};
58237 var allY = {};
58238 var i, xi, yi;
58239
58240 for(i = 0; i < spAll.length; i++) {
58241 var parts = spAll[i].split('y');
58242 allX[parts[0]] = 1;
58243 allY['y' + parts[1]] = 1;
58244 }
58245
58246 // check for x axes with no subplot, and make one from the anchor of that x axis
58247 for(i = 0; i < xList.length; i++) {
58248 xi = xList[i];
58249 if(!allX[xi]) {
58250 yi = (layoutIn[axisIds.id2name(xi)] || {}).anchor;
58251 if(!constants.idRegex.y.test(yi)) yi = 'y';
58252 spSVG.push(xi + yi);
58253 spAll.push(xi + yi);
58254
58255 if(!allY[yi]) {
58256 allY[yi] = 1;
58257 Lib.pushUnique(yList, yi);
58258 }
58259 }
58260 }
58261
58262 // same for y axes with no subplot
58263 for(i = 0; i < yList.length; i++) {
58264 yi = yList[i];
58265 if(!allY[yi]) {
58266 xi = (layoutIn[axisIds.id2name(yi)] || {}).anchor;
58267 if(!constants.idRegex.x.test(xi)) xi = 'x';
58268 spSVG.push(xi + yi);
58269 spAll.push(xi + yi);
58270
58271 if(!allX[xi]) {
58272 allX[xi] = 1;
58273 Lib.pushUnique(xList, xi);
58274 }
58275 }
58276 }
58277
58278 // finally, if we've gotten here we're supposed to show cartesian...
58279 // so if there are NO subplots at all, make one from the first
58280 // x & y axes in the input layout
58281 if(!spAll.length) {
58282 xi = '';
58283 yi = '';
58284 for(var ki in layoutIn) {
58285 if(constants.attrRegex.test(ki)) {
58286 var axLetter = ki.charAt(0);
58287 if(axLetter === 'x') {
58288 if(!xi || (+ki.substr(5) < +xi.substr(5))) {
58289 xi = ki;
58290 }
58291 } else if(!yi || (+ki.substr(5) < +yi.substr(5))) {
58292 yi = ki;
58293 }
58294 }
58295 }
58296 xi = xi ? axisIds.name2id(xi) : 'x';
58297 yi = yi ? axisIds.name2id(yi) : 'y';
58298 xList.push(xi);
58299 yList.push(yi);
58300 spSVG.push(xi + yi);
58301 }
58302};
58303
58304/**
58305 * Cartesian.plot
58306 *
58307 * @param {DOM div | object} gd
58308 * @param {array (optional)} traces
58309 * array of traces indices to plot
58310 * if undefined, plots all cartesian traces,
58311 * @param {object} (optional) transitionOpts
58312 * transition option object
58313 * @param {function} (optional) makeOnCompleteCallback
58314 * transition make callback function from Plots.transition
58315 */
58316exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
58317 var fullLayout = gd._fullLayout;
58318 var subplots = fullLayout._subplots.cartesian;
58319 var calcdata = gd.calcdata;
58320 var i;
58321
58322 if(!Array.isArray(traces)) {
58323 // If traces is not provided, then it's a complete replot and missing
58324 // traces are removed
58325 traces = [];
58326 for(i = 0; i < calcdata.length; i++) traces.push(i);
58327 }
58328
58329 for(i = 0; i < subplots.length; i++) {
58330 var subplot = subplots[i];
58331 var subplotInfo = fullLayout._plots[subplot];
58332
58333 // Get all calcdata for this subplot:
58334 var cdSubplot = [];
58335 var pcd;
58336
58337 for(var j = 0; j < calcdata.length; j++) {
58338 var cd = calcdata[j];
58339 var trace = cd[0].trace;
58340
58341 // Skip trace if whitelist provided and it's not whitelisted:
58342 // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue;
58343 if(trace.xaxis + trace.yaxis === subplot) {
58344 // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet
58345 // axis has actually changed:
58346 //
58347 // If this trace is specifically requested, add it to the list:
58348 if(traces.indexOf(trace.index) !== -1 || trace.carpet) {
58349 // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate
58350 // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill
58351 // is outdated. So this retroactively adds the previous trace if the
58352 // traces are interdependent.
58353 if(
58354 pcd &&
58355 pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot &&
58356 ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 &&
58357 cdSubplot.indexOf(pcd) === -1
58358 ) {
58359 cdSubplot.push(pcd);
58360 }
58361
58362 cdSubplot.push(cd);
58363 }
58364
58365 // Track the previous trace on this subplot for the retroactive-add step
58366 // above:
58367 pcd = cd;
58368 }
58369 }
58370
58371 plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback);
58372 }
58373};
58374
58375function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
58376 var traceLayerClasses = constants.traceLayerClasses;
58377 var fullLayout = gd._fullLayout;
58378 var modules = fullLayout._modules;
58379 var _module, cdModuleAndOthers, cdModule;
58380
58381 var layerData = [];
58382 var zoomScaleQueryParts = [];
58383
58384 for(var i = 0; i < modules.length; i++) {
58385 _module = modules[i];
58386 var name = _module.name;
58387 var categories = Registry.modules[name].categories;
58388
58389 if(categories.svg) {
58390 var className = (_module.layerName || name + 'layer');
58391 var plotMethod = _module.plot;
58392
58393 // plot all visible traces of this type on this subplot at once
58394 cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
58395 cdModule = cdModuleAndOthers[0];
58396 // don't need to search the found traces again - in fact we need to NOT
58397 // so that if two modules share the same plotter we don't double-plot
58398 cdSubplot = cdModuleAndOthers[1];
58399
58400 if(cdModule.length) {
58401 layerData.push({
58402 i: traceLayerClasses.indexOf(className),
58403 className: className,
58404 plotMethod: plotMethod,
58405 cdModule: cdModule
58406 });
58407 }
58408
58409 if(categories.zoomScale) {
58410 zoomScaleQueryParts.push('.' + className);
58411 }
58412 }
58413 }
58414
58415 layerData.sort(function(a, b) { return a.i - b.i; });
58416
58417 var layers = plotinfo.plot.selectAll('g.mlayer')
58418 .data(layerData, function(d) { return d.className; });
58419
58420 layers.enter().append('g')
58421 .attr('class', function(d) { return d.className; })
58422 .classed('mlayer', true)
58423 .classed('rangeplot', plotinfo.isRangePlot);
58424
58425 layers.exit().remove();
58426
58427 layers.order();
58428
58429 layers.each(function(d) {
58430 var sel = d3.select(this);
58431 var className = d.className;
58432
58433 d.plotMethod(
58434 gd, plotinfo, d.cdModule, sel,
58435 transitionOpts, makeOnCompleteCallback
58436 );
58437
58438 // layers that allow `cliponaxis: false`
58439 if(constants.clipOnAxisFalseQuery.indexOf('.' + className) === -1) {
58440 Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
58441 }
58442 });
58443
58444 // call Scattergl.plot separately
58445 if(fullLayout._has('scattergl')) {
58446 _module = Registry.getModule('scattergl');
58447 cdModule = getModuleCalcData(cdSubplot, _module)[0];
58448 _module.plot(gd, plotinfo, cdModule);
58449 }
58450
58451 // stash "hot" selections for faster interaction on drag and scroll
58452 if(!gd._context.staticPlot) {
58453 if(plotinfo._hasClipOnAxisFalse) {
58454 plotinfo.clipOnAxisFalseTraces = plotinfo.plot
58455 .selectAll(constants.clipOnAxisFalseQuery.join(','))
58456 .selectAll('.trace');
58457 }
58458
58459 if(zoomScaleQueryParts.length) {
58460 var traces = plotinfo.plot
58461 .selectAll(zoomScaleQueryParts.join(','))
58462 .selectAll('.trace');
58463
58464 plotinfo.zoomScalePts = traces.selectAll('path.point');
58465 plotinfo.zoomScaleTxt = traces.selectAll('.textpoint');
58466 }
58467 }
58468}
58469
58470exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
58471 var oldPlots = oldFullLayout._plots || {};
58472 var newPlots = newFullLayout._plots || {};
58473 var oldSubplotList = oldFullLayout._subplots || {};
58474 var plotinfo;
58475 var i, k;
58476
58477 // when going from a large splom graph to something else,
58478 // we need to clear <g subplot> so that the new cartesian subplot
58479 // can have the correct layer ordering
58480 if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) {
58481 for(k in oldPlots) {
58482 plotinfo = oldPlots[k];
58483 if(plotinfo.plotgroup) plotinfo.plotgroup.remove();
58484 }
58485 }
58486
58487 var hadGl = (oldFullLayout._has && oldFullLayout._has('gl'));
58488 var hasGl = (newFullLayout._has && newFullLayout._has('gl'));
58489
58490 if(hadGl && !hasGl) {
58491 for(k in oldPlots) {
58492 plotinfo = oldPlots[k];
58493 if(plotinfo._scene) plotinfo._scene.destroy();
58494 }
58495 }
58496
58497 // delete any titles we don't need anymore
58498 // check if axis list has changed, and if so clear old titles
58499 if(oldSubplotList.xaxis && oldSubplotList.yaxis) {
58500 var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout});
58501 for(i = 0; i < oldAxIDs.length; i++) {
58502 var oldAxId = oldAxIDs[i];
58503 if(!newFullLayout[axisIds.id2name(oldAxId)]) {
58504 oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove();
58505 }
58506 }
58507 }
58508
58509 var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian'));
58510 var hasCartesian = (newFullLayout._has && newFullLayout._has('cartesian'));
58511
58512 if(hadCartesian && !hasCartesian) {
58513 // if we've gotten rid of all cartesian traces, remove all the subplot svg items
58514
58515 purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout);
58516 oldFullLayout._defs.selectAll('.axesclip').remove();
58517 delete oldFullLayout._axisConstraintGroups;
58518 } else if(oldSubplotList.cartesian) {
58519 // otherwise look for subplots we need to remove
58520
58521 for(i = 0; i < oldSubplotList.cartesian.length; i++) {
58522 var oldSubplotId = oldSubplotList.cartesian[i];
58523 if(!newPlots[oldSubplotId]) {
58524 var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y';
58525 oldFullLayout._cartesianlayer.selectAll(selector).remove();
58526 removeSubplotExtras(oldSubplotId, oldFullLayout);
58527 }
58528 }
58529 }
58530};
58531
58532exports.drawFramework = function(gd) {
58533 var fullLayout = gd._fullLayout;
58534 var subplotData = makeSubplotData(gd);
58535
58536 var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot')
58537 .data(subplotData, String);
58538
58539 subplotLayers.enter().append('g')
58540 .attr('class', function(d) { return 'subplot ' + d[0]; });
58541
58542 subplotLayers.order();
58543
58544 subplotLayers.exit()
58545 .call(purgeSubplotLayers, fullLayout);
58546
58547 subplotLayers.each(function(d) {
58548 var id = d[0];
58549 var plotinfo = fullLayout._plots[id];
58550
58551 plotinfo.plotgroup = d3.select(this);
58552 makeSubplotLayer(gd, plotinfo);
58553
58554 // make separate drag layers for each subplot,
58555 // but append them to paper rather than the plot groups,
58556 // so they end up on top of the rest
58557 plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id);
58558 });
58559};
58560
58561exports.rangePlot = function(gd, plotinfo, cdSubplot) {
58562 makeSubplotLayer(gd, plotinfo);
58563 plotOne(gd, plotinfo, cdSubplot);
58564 Plots.style(gd);
58565};
58566
58567function makeSubplotData(gd) {
58568 var fullLayout = gd._fullLayout;
58569 var ids = fullLayout._subplots.cartesian;
58570 var len = ids.length;
58571 var i, j, id, plotinfo, xa, ya;
58572
58573 // split 'regular' and 'overlaying' subplots
58574 var regulars = [];
58575 var overlays = [];
58576
58577 for(i = 0; i < len; i++) {
58578 id = ids[i];
58579 plotinfo = fullLayout._plots[id];
58580 xa = plotinfo.xaxis;
58581 ya = plotinfo.yaxis;
58582
58583 var xa2 = xa._mainAxis;
58584 var ya2 = ya._mainAxis;
58585 var mainplot = xa2._id + ya2._id;
58586 var mainplotinfo = fullLayout._plots[mainplot];
58587 plotinfo.overlays = [];
58588
58589 if(mainplot !== id && mainplotinfo) {
58590 plotinfo.mainplot = mainplot;
58591 plotinfo.mainplotinfo = mainplotinfo;
58592 overlays.push(id);
58593 } else {
58594 plotinfo.mainplot = undefined;
58595 plotinfo.mainPlotinfo = undefined;
58596 regulars.push(id);
58597 }
58598 }
58599
58600 // fill in list of overlaying subplots in 'main plot'
58601 for(i = 0; i < overlays.length; i++) {
58602 id = overlays[i];
58603 plotinfo = fullLayout._plots[id];
58604 plotinfo.mainplotinfo.overlays.push(plotinfo);
58605 }
58606
58607 // put 'regular' subplot data before 'overlaying'
58608 var subplotIds = regulars.concat(overlays);
58609 var subplotData = new Array(len);
58610
58611 for(i = 0; i < len; i++) {
58612 id = subplotIds[i];
58613 plotinfo = fullLayout._plots[id];
58614 xa = plotinfo.xaxis;
58615 ya = plotinfo.yaxis;
58616
58617 // use info about axis layer and overlaying pattern
58618 // to clean what need to be cleaned up in exit selection
58619 var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || ''];
58620 for(j = 0; j < plotinfo.overlays.length; j++) {
58621 d.push(plotinfo.overlays[j].id);
58622 }
58623 subplotData[i] = d;
58624 }
58625
58626 return subplotData;
58627}
58628
58629function makeSubplotLayer(gd, plotinfo) {
58630 var plotgroup = plotinfo.plotgroup;
58631 var id = plotinfo.id;
58632 var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
58633 var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
58634 var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms;
58635
58636 if(!plotinfo.mainplot) {
58637 if(hasOnlyLargeSploms) {
58638 // TODO could do even better
58639 // - we don't need plot (but we would have to mock it in lsInner
58640 // and other places
58641 // - we don't (x|y)lines and (x|y)axislayer for most subplots
58642 // usually just the bottom x and left y axes.
58643 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
58644 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
58645 plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above');
58646 plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above');
58647 } else {
58648 var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
58649 plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
58650 plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
58651
58652 plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
58653 plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
58654
58655 ensureSingle(plotgroup, 'path', 'xlines-below');
58656 ensureSingle(plotgroup, 'path', 'ylines-below');
58657 plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
58658
58659 ensureSingle(plotgroup, 'g', 'xaxislayer-below');
58660 ensureSingle(plotgroup, 'g', 'yaxislayer-below');
58661 plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
58662
58663 plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
58664 plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
58665
58666 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
58667 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
58668 plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above');
58669
58670 ensureSingle(plotgroup, 'g', 'xaxislayer-above');
58671 ensureSingle(plotgroup, 'g', 'yaxislayer-above');
58672 plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
58673
58674 // set refs to correct layers as determined by 'axis.layer'
58675 plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
58676 plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
58677 plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
58678 plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
58679 }
58680 } else {
58681 var mainplotinfo = plotinfo.mainplotinfo;
58682 var mainplotgroup = mainplotinfo.plotgroup;
58683 var xId = id + '-x';
58684 var yId = id + '-y';
58685
58686 // now make the components of overlaid subplots
58687 // overlays don't have backgrounds, and append all
58688 // their other components to the corresponding
58689 // extra groups of their main plots.
58690
58691 plotinfo.gridlayer = mainplotinfo.gridlayer;
58692 plotinfo.zerolinelayer = mainplotinfo.zerolinelayer;
58693
58694 ensureSingle(mainplotinfo.overlinesBelow, 'path', xId);
58695 ensureSingle(mainplotinfo.overlinesBelow, 'path', yId);
58696 ensureSingle(mainplotinfo.overaxesBelow, 'g', xId);
58697 ensureSingle(mainplotinfo.overaxesBelow, 'g', yId);
58698
58699 plotinfo.plot = ensureSingle(mainplotinfo.overplot, 'g', id);
58700
58701 ensureSingle(mainplotinfo.overlinesAbove, 'path', xId);
58702 ensureSingle(mainplotinfo.overlinesAbove, 'path', yId);
58703 ensureSingle(mainplotinfo.overaxesAbove, 'g', xId);
58704 ensureSingle(mainplotinfo.overaxesAbove, 'g', yId);
58705
58706 // set refs to correct layers as determined by 'abovetraces'
58707 plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
58708 plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
58709 plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
58710 plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
58711 }
58712
58713 // common attributes for all subplots, overlays or not
58714
58715 if(!hasOnlyLargeSploms) {
58716 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
58717 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
58718 plotinfo.gridlayer.selectAll('g')
58719 .map(function(d) { return d[0]; })
58720 .sort(axisIds.idSort);
58721 }
58722
58723 plotinfo.xlines
58724 .style('fill', 'none')
58725 .classed('crisp', true);
58726
58727 plotinfo.ylines
58728 .style('fill', 'none')
58729 .classed('crisp', true);
58730}
58731
58732function purgeSubplotLayers(layers, fullLayout) {
58733 if(!layers) return;
58734
58735 var overlayIdsToRemove = {};
58736
58737 layers.each(function(d) {
58738 var id = d[0];
58739 var plotgroup = d3.select(this);
58740
58741 plotgroup.remove();
58742 removeSubplotExtras(id, fullLayout);
58743 overlayIdsToRemove[id] = true;
58744
58745 // do not remove individual axis <clipPath>s here
58746 // as other subplots may need them
58747 });
58748
58749 // must remove overlaid subplot trace layers 'manually'
58750
58751 for(var k in fullLayout._plots) {
58752 var subplotInfo = fullLayout._plots[k];
58753 var overlays = subplotInfo.overlays || [];
58754
58755 for(var j = 0; j < overlays.length; j++) {
58756 var overlayInfo = overlays[j];
58757
58758 if(overlayIdsToRemove[overlayInfo.id]) {
58759 overlayInfo.plot.selectAll('.trace').remove();
58760 }
58761 }
58762 }
58763}
58764
58765function removeSubplotExtras(subplotId, fullLayout) {
58766 fullLayout._draggers.selectAll('g.' + subplotId).remove();
58767 fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove();
58768}
58769
58770exports.toSVG = function(gd) {
58771 var imageRoot = gd._fullLayout._glimages;
58772 var root = d3.select(gd).selectAll('.svg-container');
58773 var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
58774 .selectAll('.gl-canvas-context, .gl-canvas-focus');
58775
58776 function canvasToImage() {
58777 var canvas = this;
58778 var imageData = canvas.toDataURL('image/png');
58779 var image = imageRoot.append('svg:image');
58780
58781 image.attr({
58782 xmlns: xmlnsNamespaces.svg,
58783 'xlink:href': imageData,
58784 preserveAspectRatio: 'none',
58785 x: 0,
58786 y: 0,
58787 width: canvas.width,
58788 height: canvas.height
58789 });
58790 }
58791
58792 canvases.each(canvasToImage);
58793};
58794
58795exports.updateFx = _dereq_('./graph_interact').updateFx;
58796
58797},{"../../components/drawing":74,"../../constants/xmlns_namespaces":159,"../../lib":178,"../../registry":269,"../get_data":252,"../plots":256,"./attributes":220,"./axis_ids":225,"./constants":228,"./graph_interact":231,"./layout_attributes":236,"./layout_defaults":237,"./transition_axes":246,"d3":16}],236:[function(_dereq_,module,exports){
58798/**
58799* Copyright 2012-2020, Plotly, Inc.
58800* All rights reserved.
58801*
58802* This source code is licensed under the MIT license found in the
58803* LICENSE file in the root directory of this source tree.
58804*/
58805
58806'use strict';
58807
58808var fontAttrs = _dereq_('../font_attributes');
58809var colorAttrs = _dereq_('../../components/color/attributes');
58810var dash = _dereq_('../../components/drawing/attributes').dash;
58811var extendFlat = _dereq_('../../lib/extend').extendFlat;
58812var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
58813
58814var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK;
58815var DATE_FORMAT_LINK = _dereq_('../../constants/docs').DATE_FORMAT_LINK;
58816var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
58817var constants = _dereq_('./constants');
58818var HOUR = constants.HOUR_PATTERN;
58819var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
58820
58821module.exports = {
58822 visible: {
58823 valType: 'boolean',
58824
58825 editType: 'plot',
58826
58827 },
58828 color: {
58829 valType: 'color',
58830 dflt: colorAttrs.defaultLine,
58831
58832 editType: 'ticks',
58833
58834 },
58835 title: {
58836 text: {
58837 valType: 'string',
58838
58839 editType: 'ticks',
58840
58841 },
58842 font: fontAttrs({
58843 editType: 'ticks',
58844
58845 }),
58846 standoff: {
58847 valType: 'number',
58848
58849 min: 0,
58850 editType: 'ticks',
58851
58852 },
58853 editType: 'ticks'
58854 },
58855 type: {
58856 valType: 'enumerated',
58857 // '-' means we haven't yet run autotype or couldn't find any data
58858 // it gets turned into linear in gd._fullLayout but not copied back
58859 // to gd.data like the others are.
58860 values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'],
58861 dflt: '-',
58862
58863 editType: 'calc',
58864 // we forget when an axis has been autotyped, just writing the auto
58865 // value back to the input - so it doesn't make sense to template this.
58866 // Note: we do NOT prohibit this in `coerce`, so if someone enters a
58867 // type in the template explicitly it will be honored as the default.
58868 _noTemplating: true,
58869
58870 },
58871 autorange: {
58872 valType: 'enumerated',
58873 values: [true, false, 'reversed'],
58874 dflt: true,
58875
58876 editType: 'axrange',
58877 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
58878
58879 },
58880 rangemode: {
58881 valType: 'enumerated',
58882 values: ['normal', 'tozero', 'nonnegative'],
58883 dflt: 'normal',
58884
58885 editType: 'plot',
58886
58887 },
58888 range: {
58889 valType: 'info_array',
58890
58891 items: [
58892 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true},
58893 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true}
58894 ],
58895 editType: 'axrange',
58896 impliedEdits: {'autorange': false},
58897 anim: true,
58898
58899 },
58900 fixedrange: {
58901 valType: 'boolean',
58902 dflt: false,
58903
58904 editType: 'calc',
58905
58906 },
58907 // scaleanchor: not used directly, just put here for reference
58908 // values are any opposite-letter axis id
58909 scaleanchor: {
58910 valType: 'enumerated',
58911 values: [
58912 constants.idRegex.x.toString(),
58913 constants.idRegex.y.toString()
58914 ],
58915
58916 editType: 'plot',
58917
58918 },
58919 scaleratio: {
58920 valType: 'number',
58921 min: 0,
58922 dflt: 1,
58923
58924 editType: 'plot',
58925
58926 },
58927 constrain: {
58928 valType: 'enumerated',
58929 values: ['range', 'domain'],
58930 dflt: 'range',
58931
58932 editType: 'plot',
58933
58934 },
58935 // constraintoward: not used directly, just put here for reference
58936 constraintoward: {
58937 valType: 'enumerated',
58938 values: ['left', 'center', 'right', 'top', 'middle', 'bottom'],
58939
58940 editType: 'plot',
58941
58942 },
58943 matches: {
58944 valType: 'enumerated',
58945 values: [
58946 constants.idRegex.x.toString(),
58947 constants.idRegex.y.toString()
58948 ],
58949
58950 editType: 'calc',
58951
58952 },
58953
58954 rangebreaks: templatedArray('rangebreak', {
58955 enabled: {
58956 valType: 'boolean',
58957
58958 dflt: true,
58959 editType: 'calc',
58960
58961 },
58962
58963 bounds: {
58964 valType: 'info_array',
58965
58966 items: [
58967 {valType: 'any', editType: 'calc'},
58968 {valType: 'any', editType: 'calc'}
58969 ],
58970 editType: 'calc',
58971
58972 },
58973
58974 pattern: {
58975 valType: 'enumerated',
58976 values: [DAY_OF_WEEK, HOUR, ''],
58977
58978 editType: 'calc',
58979
58980 },
58981
58982 values: {
58983 valType: 'info_array',
58984 freeLength: true,
58985
58986 editType: 'calc',
58987 items: {
58988 valType: 'any',
58989 editType: 'calc'
58990 },
58991
58992 },
58993 dvalue: {
58994 // TODO could become 'any' to add support for 'months', 'years'
58995 valType: 'number',
58996
58997 editType: 'calc',
58998 min: 0,
58999 dflt: ONEDAY,
59000
59001 },
59002
59003 /*
59004 gap: {
59005 valType: 'number',
59006 min: 0,
59007 dflt: 0, // for *date* axes, maybe something else for *linear*
59008 editType: 'calc',
59009
59010
59011 },
59012 gapmode: {
59013 valType: 'enumerated',
59014 values: ['pixels', 'fraction'],
59015 dflt: 'pixels',
59016 editType: 'calc',
59017
59018
59019 },
59020 */
59021
59022 // To complete https://github.com/plotly/plotly.js/issues/4210
59023 // we additionally need `gap` and make this work on *linear*, and
59024 // possibly all other cartesian axis types. We possibly would also need
59025 // some style attributes controlling the zig-zag on the corresponding
59026 // axis.
59027
59028 editType: 'calc'
59029 }),
59030
59031 // ticks
59032 tickmode: {
59033 valType: 'enumerated',
59034 values: ['auto', 'linear', 'array'],
59035
59036 editType: 'ticks',
59037 impliedEdits: {tick0: undefined, dtick: undefined},
59038
59039 },
59040 nticks: {
59041 valType: 'integer',
59042 min: 0,
59043 dflt: 0,
59044
59045 editType: 'ticks',
59046
59047 },
59048 tick0: {
59049 valType: 'any',
59050
59051 editType: 'ticks',
59052 impliedEdits: {tickmode: 'linear'},
59053
59054 },
59055 dtick: {
59056 valType: 'any',
59057
59058 editType: 'ticks',
59059 impliedEdits: {tickmode: 'linear'},
59060
59061 },
59062 tickvals: {
59063 valType: 'data_array',
59064 editType: 'ticks',
59065
59066 },
59067 ticktext: {
59068 valType: 'data_array',
59069 editType: 'ticks',
59070
59071 },
59072 ticks: {
59073 valType: 'enumerated',
59074 values: ['outside', 'inside', ''],
59075
59076 editType: 'ticks',
59077
59078 },
59079 tickson: {
59080 valType: 'enumerated',
59081 values: ['labels', 'boundaries'],
59082
59083 dflt: 'labels',
59084 editType: 'ticks',
59085
59086 },
59087 mirror: {
59088 valType: 'enumerated',
59089 values: [true, 'ticks', false, 'all', 'allticks'],
59090 dflt: false,
59091
59092 editType: 'ticks+layoutstyle',
59093
59094 },
59095 ticklen: {
59096 valType: 'number',
59097 min: 0,
59098 dflt: 5,
59099
59100 editType: 'ticks',
59101
59102 },
59103 tickwidth: {
59104 valType: 'number',
59105 min: 0,
59106 dflt: 1,
59107
59108 editType: 'ticks',
59109
59110 },
59111 tickcolor: {
59112 valType: 'color',
59113 dflt: colorAttrs.defaultLine,
59114
59115 editType: 'ticks',
59116
59117 },
59118 showticklabels: {
59119 valType: 'boolean',
59120 dflt: true,
59121
59122 editType: 'ticks',
59123
59124 },
59125 automargin: {
59126 valType: 'boolean',
59127 dflt: false,
59128
59129 editType: 'ticks',
59130
59131 },
59132 showspikes: {
59133 valType: 'boolean',
59134 dflt: false,
59135
59136 editType: 'modebar',
59137
59138 },
59139 spikecolor: {
59140 valType: 'color',
59141 dflt: null,
59142
59143 editType: 'none',
59144
59145 },
59146 spikethickness: {
59147 valType: 'number',
59148 dflt: 3,
59149
59150 editType: 'none',
59151
59152 },
59153 spikedash: extendFlat({}, dash, {dflt: 'dash', editType: 'none'}),
59154 spikemode: {
59155 valType: 'flaglist',
59156 flags: ['toaxis', 'across', 'marker'],
59157
59158 dflt: 'toaxis',
59159 editType: 'none',
59160
59161 },
59162 spikesnap: {
59163 valType: 'enumerated',
59164 values: ['data', 'cursor', 'hovered data'],
59165 dflt: 'data',
59166
59167 editType: 'none',
59168
59169 },
59170 tickfont: fontAttrs({
59171 editType: 'ticks',
59172
59173 }),
59174 tickangle: {
59175 valType: 'angle',
59176 dflt: 'auto',
59177
59178 editType: 'ticks',
59179
59180 },
59181 tickprefix: {
59182 valType: 'string',
59183 dflt: '',
59184
59185 editType: 'ticks',
59186
59187 },
59188 showtickprefix: {
59189 valType: 'enumerated',
59190 values: ['all', 'first', 'last', 'none'],
59191 dflt: 'all',
59192
59193 editType: 'ticks',
59194
59195 },
59196 ticksuffix: {
59197 valType: 'string',
59198 dflt: '',
59199
59200 editType: 'ticks',
59201
59202 },
59203 showticksuffix: {
59204 valType: 'enumerated',
59205 values: ['all', 'first', 'last', 'none'],
59206 dflt: 'all',
59207
59208 editType: 'ticks',
59209
59210 },
59211 showexponent: {
59212 valType: 'enumerated',
59213 values: ['all', 'first', 'last', 'none'],
59214 dflt: 'all',
59215
59216 editType: 'ticks',
59217
59218 },
59219 exponentformat: {
59220 valType: 'enumerated',
59221 values: ['none', 'e', 'E', 'power', 'SI', 'B'],
59222 dflt: 'B',
59223
59224 editType: 'ticks',
59225
59226 },
59227 separatethousands: {
59228 valType: 'boolean',
59229 dflt: false,
59230
59231 editType: 'ticks',
59232
59233 },
59234 tickformat: {
59235 valType: 'string',
59236 dflt: '',
59237
59238 editType: 'ticks',
59239
59240 },
59241 tickformatstops: templatedArray('tickformatstop', {
59242 enabled: {
59243 valType: 'boolean',
59244
59245 dflt: true,
59246 editType: 'ticks',
59247
59248 },
59249 dtickrange: {
59250 valType: 'info_array',
59251
59252 items: [
59253 {valType: 'any', editType: 'ticks'},
59254 {valType: 'any', editType: 'ticks'}
59255 ],
59256 editType: 'ticks',
59257
59258 },
59259 value: {
59260 valType: 'string',
59261 dflt: '',
59262
59263 editType: 'ticks',
59264
59265 },
59266 editType: 'ticks'
59267 }),
59268 hoverformat: {
59269 valType: 'string',
59270 dflt: '',
59271
59272 editType: 'none',
59273
59274 },
59275 // lines and grids
59276 showline: {
59277 valType: 'boolean',
59278 dflt: false,
59279
59280 editType: 'ticks+layoutstyle',
59281
59282 },
59283 linecolor: {
59284 valType: 'color',
59285 dflt: colorAttrs.defaultLine,
59286
59287 editType: 'layoutstyle',
59288
59289 },
59290 linewidth: {
59291 valType: 'number',
59292 min: 0,
59293 dflt: 1,
59294
59295 editType: 'ticks+layoutstyle',
59296
59297 },
59298 showgrid: {
59299 valType: 'boolean',
59300
59301 editType: 'ticks',
59302
59303 },
59304 gridcolor: {
59305 valType: 'color',
59306 dflt: colorAttrs.lightLine,
59307
59308 editType: 'ticks',
59309
59310 },
59311 gridwidth: {
59312 valType: 'number',
59313 min: 0,
59314 dflt: 1,
59315
59316 editType: 'ticks',
59317
59318 },
59319 zeroline: {
59320 valType: 'boolean',
59321
59322 editType: 'ticks',
59323
59324 },
59325 zerolinecolor: {
59326 valType: 'color',
59327 dflt: colorAttrs.defaultLine,
59328
59329 editType: 'ticks',
59330
59331 },
59332 zerolinewidth: {
59333 valType: 'number',
59334 dflt: 1,
59335
59336 editType: 'ticks',
59337
59338 },
59339
59340 showdividers: {
59341 valType: 'boolean',
59342 dflt: true,
59343
59344 editType: 'ticks',
59345
59346 },
59347 dividercolor: {
59348 valType: 'color',
59349 dflt: colorAttrs.defaultLine,
59350
59351 editType: 'ticks',
59352
59353 },
59354 dividerwidth: {
59355 valType: 'number',
59356 dflt: 1,
59357
59358 editType: 'ticks',
59359
59360 },
59361 // TODO dividerlen: that would override "to label base" length?
59362
59363 // positioning attributes
59364 // anchor: not used directly, just put here for reference
59365 // values are any opposite-letter axis id
59366 anchor: {
59367 valType: 'enumerated',
59368 values: [
59369 'free',
59370 constants.idRegex.x.toString(),
59371 constants.idRegex.y.toString()
59372 ],
59373
59374 editType: 'plot',
59375
59376 },
59377 // side: not used directly, as values depend on direction
59378 // values are top, bottom for x axes, and left, right for y
59379 side: {
59380 valType: 'enumerated',
59381 values: ['top', 'bottom', 'left', 'right'],
59382
59383 editType: 'plot',
59384
59385 },
59386 // overlaying: not used directly, just put here for reference
59387 // values are false and any other same-letter axis id that's not
59388 // itself overlaying anything
59389 overlaying: {
59390 valType: 'enumerated',
59391 values: [
59392 'free',
59393 constants.idRegex.x.toString(),
59394 constants.idRegex.y.toString()
59395 ],
59396
59397 editType: 'plot',
59398
59399 },
59400 layer: {
59401 valType: 'enumerated',
59402 values: ['above traces', 'below traces'],
59403 dflt: 'above traces',
59404
59405 editType: 'plot',
59406
59407 },
59408 domain: {
59409 valType: 'info_array',
59410
59411 items: [
59412 {valType: 'number', min: 0, max: 1, editType: 'plot'},
59413 {valType: 'number', min: 0, max: 1, editType: 'plot'}
59414 ],
59415 dflt: [0, 1],
59416 editType: 'plot',
59417
59418 },
59419 position: {
59420 valType: 'number',
59421 min: 0,
59422 max: 1,
59423 dflt: 0,
59424
59425 editType: 'plot',
59426
59427 },
59428 categoryorder: {
59429 valType: 'enumerated',
59430 values: [
59431 'trace', 'category ascending', 'category descending', 'array',
59432 'total ascending', 'total descending',
59433 'min ascending', 'min descending',
59434 'max ascending', 'max descending',
59435 'sum ascending', 'sum descending',
59436 'mean ascending', 'mean descending',
59437 'median ascending', 'median descending'
59438 ],
59439 dflt: 'trace',
59440
59441 editType: 'calc',
59442
59443 },
59444 categoryarray: {
59445 valType: 'data_array',
59446
59447 editType: 'calc',
59448
59449 },
59450 uirevision: {
59451 valType: 'any',
59452
59453 editType: 'none',
59454
59455 },
59456 editType: 'calc',
59457
59458 _deprecated: {
59459 autotick: {
59460 valType: 'boolean',
59461
59462 editType: 'ticks',
59463
59464 },
59465 title: {
59466 valType: 'string',
59467
59468 editType: 'ticks',
59469
59470 },
59471 titlefont: fontAttrs({
59472 editType: 'ticks',
59473
59474 })
59475 }
59476};
59477
59478},{"../../components/color/attributes":51,"../../components/drawing/attributes":73,"../../constants/docs":155,"../../constants/numerical":158,"../../lib/extend":173,"../../plot_api/plot_template":212,"../font_attributes":250,"./constants":228}],237:[function(_dereq_,module,exports){
59479/**
59480* Copyright 2012-2020, Plotly, Inc.
59481* All rights reserved.
59482*
59483* This source code is licensed under the MIT license found in the
59484* LICENSE file in the root directory of this source tree.
59485*/
59486
59487
59488'use strict';
59489
59490var Lib = _dereq_('../../lib');
59491var Color = _dereq_('../../components/color');
59492var isUnifiedHover = _dereq_('../../components/fx/helpers').isUnifiedHover;
59493var handleHoverModeDefaults = _dereq_('../../components/fx/hovermode_defaults');
59494var Template = _dereq_('../../plot_api/plot_template');
59495var basePlotLayoutAttributes = _dereq_('../layout_attributes');
59496
59497var layoutAttributes = _dereq_('./layout_attributes');
59498var handleTypeDefaults = _dereq_('./type_defaults');
59499var handleAxisDefaults = _dereq_('./axis_defaults');
59500var handleConstraintDefaults = _dereq_('./constraints').handleConstraintDefaults;
59501var handlePositionDefaults = _dereq_('./position_defaults');
59502
59503var axisIds = _dereq_('./axis_ids');
59504var id2name = axisIds.id2name;
59505var name2id = axisIds.name2id;
59506
59507var AX_ID_PATTERN = _dereq_('./constants').AX_ID_PATTERN;
59508
59509var Registry = _dereq_('../../registry');
59510var traceIs = Registry.traceIs;
59511var getComponentMethod = Registry.getComponentMethod;
59512
59513function appendList(cont, k, item) {
59514 if(Array.isArray(cont[k])) cont[k].push(item);
59515 else cont[k] = [item];
59516}
59517
59518module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
59519 var ax2traces = {};
59520 var xaMayHide = {};
59521 var yaMayHide = {};
59522 var xaMustDisplay = {};
59523 var yaMustDisplay = {};
59524 var yaMustNotReverse = {};
59525 var yaMayReverse = {};
59526 var axHasImage = {};
59527 var outerTicks = {};
59528 var noGrids = {};
59529 var i, j;
59530
59531 // look for axes in the data
59532 for(i = 0; i < fullData.length; i++) {
59533 var trace = fullData[i];
59534 if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue;
59535
59536 var xaName;
59537 if(trace.xaxis) {
59538 xaName = id2name(trace.xaxis);
59539 appendList(ax2traces, xaName, trace);
59540 } else if(trace.xaxes) {
59541 for(j = 0; j < trace.xaxes.length; j++) {
59542 appendList(ax2traces, id2name(trace.xaxes[j]), trace);
59543 }
59544 }
59545
59546 var yaName;
59547 if(trace.yaxis) {
59548 yaName = id2name(trace.yaxis);
59549 appendList(ax2traces, yaName, trace);
59550 } else if(trace.yaxes) {
59551 for(j = 0; j < trace.yaxes.length; j++) {
59552 appendList(ax2traces, id2name(trace.yaxes[j]), trace);
59553 }
59554 }
59555
59556 // logic for funnels
59557 if(trace.type === 'funnel') {
59558 if(trace.orientation === 'h') {
59559 if(xaName) xaMayHide[xaName] = true;
59560 if(yaName) yaMayReverse[yaName] = true;
59561 } else {
59562 if(yaName) yaMayHide[yaName] = true;
59563 }
59564 } else if(trace.type === 'image') {
59565 if(yaName) axHasImage[yaName] = true;
59566 if(xaName) axHasImage[xaName] = true;
59567 } else {
59568 if(yaName) {
59569 yaMustDisplay[yaName] = true;
59570 yaMustNotReverse[yaName] = true;
59571 }
59572
59573 if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
59574 if(xaName) xaMustDisplay[xaName] = true;
59575 }
59576 }
59577
59578 // Two things trigger axis visibility:
59579 // 1. is not carpet
59580 // 2. carpet that's not cheater
59581
59582 // The above check for definitely-not-cheater is not adequate. This
59583 // second list tracks which axes *could* be a cheater so that the
59584 // full condition triggering hiding is:
59585 // *could* be a cheater and *is not definitely visible*
59586 if(trace.type === 'carpet' && trace._cheater) {
59587 if(xaName) xaMayHide[xaName] = true;
59588 }
59589
59590 // check for default formatting tweaks
59591 if(traceIs(trace, '2dMap')) {
59592 outerTicks[xaName] = true;
59593 outerTicks[yaName] = true;
59594 }
59595
59596 if(traceIs(trace, 'oriented')) {
59597 var positionAxis = trace.orientation === 'h' ? yaName : xaName;
59598 noGrids[positionAxis] = true;
59599 }
59600 }
59601
59602 var subplots = layoutOut._subplots;
59603 var xIds = subplots.xaxis;
59604 var yIds = subplots.yaxis;
59605 var xNames = Lib.simpleMap(xIds, id2name);
59606 var yNames = Lib.simpleMap(yIds, id2name);
59607 var axNames = xNames.concat(yNames);
59608
59609 // plot_bgcolor only makes sense if there's a (2D) plot!
59610 // TODO: bgcolor for each subplot, to inherit from the main one
59611 var plotBgColor = Color.background;
59612 if(xIds.length && yIds.length) {
59613 plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor');
59614 }
59615
59616 var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor);
59617
59618 // name of single axis (e.g. 'xaxis', 'yaxis2')
59619 var axName;
59620 // id of single axis (e.g. 'y', 'x5')
59621 var axId;
59622 // 'x' or 'y'
59623 var axLetter;
59624 // input layout axis container
59625 var axLayoutIn;
59626 // full layout axis container
59627 var axLayoutOut;
59628
59629 function newAxLayoutOut() {
59630 var traces = ax2traces[axName] || [];
59631 axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
59632 axLayoutOut._annIndices = [];
59633 axLayoutOut._shapeIndices = [];
59634 axLayoutOut._imgIndices = [];
59635 axLayoutOut._subplotsWith = [];
59636 axLayoutOut._counterAxes = [];
59637 axLayoutOut._name = axLayoutOut._attr = axName;
59638 axLayoutOut._id = axId;
59639 }
59640
59641 function coerce(attr, dflt) {
59642 return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
59643 }
59644
59645 function coerce2(attr, dflt) {
59646 return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
59647 }
59648
59649 function getCounterAxes(axLetter) {
59650 return (axLetter === 'x') ? yIds : xIds;
59651 }
59652
59653 function getOverlayableAxes(axLetter, axName) {
59654 var list = (axLetter === 'x') ? xNames : yNames;
59655 var out = [];
59656
59657 for(var j = 0; j < list.length; j++) {
59658 var axName2 = list[j];
59659
59660 if(axName2 !== axName && !(layoutIn[axName2] || {}).overlaying) {
59661 out.push(name2id(axName2));
59662 }
59663 }
59664
59665 return out;
59666 }
59667
59668 // list of available counter axis names
59669 var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
59670 // list of all x AND y axis ids
59671 var allAxisIds = counterAxes.x.concat(counterAxes.y);
59672 // lookup and list of axis ids that axes in axNames have a reference to,
59673 // even though they are missing from allAxisIds
59674 var missingMatchedAxisIdsLookup = {};
59675 var missingMatchedAxisIds = [];
59676
59677 // fill in 'missing' axis lookup when an axis is set to match an axis
59678 // not part of the allAxisIds list, save axis type so that we can propagate
59679 // it to the missing axes
59680 function addMissingMatchedAxis() {
59681 var matchesIn = axLayoutIn.matches;
59682 if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) {
59683 missingMatchedAxisIdsLookup[matchesIn] = axLayoutIn.type;
59684 missingMatchedAxisIds = Object.keys(missingMatchedAxisIdsLookup);
59685 }
59686 }
59687
59688 var hovermode = handleHoverModeDefaults(layoutIn, layoutOut, fullData);
59689 var unifiedHover = isUnifiedHover(hovermode);
59690
59691 // first pass creates the containers, determines types, and handles most of the settings
59692 for(i = 0; i < axNames.length; i++) {
59693 axName = axNames[i];
59694 axId = name2id(axName);
59695 axLetter = axName.charAt(0);
59696
59697 if(!Lib.isPlainObject(layoutIn[axName])) {
59698 layoutIn[axName] = {};
59699 }
59700
59701 axLayoutIn = layoutIn[axName];
59702 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
59703 newAxLayoutOut();
59704
59705 var visibleDflt =
59706 (axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) ||
59707 (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]);
59708
59709 var reverseDflt =
59710 (axLetter === 'y' &&
59711 (
59712 (!yaMustNotReverse[axName] && yaMayReverse[axName]) ||
59713 axHasImage[axName]
59714 ));
59715
59716 var defaultOptions = {
59717 letter: axLetter,
59718 font: layoutOut.font,
59719 outerTicks: outerTicks[axName],
59720 showGrid: !noGrids[axName],
59721 data: ax2traces[axName] || [],
59722 bgColor: bgColor,
59723 calendar: layoutOut.calendar,
59724 automargin: true,
59725 visibleDflt: visibleDflt,
59726 reverseDflt: reverseDflt,
59727 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
59728 };
59729
59730 coerce('uirevision', layoutOut.uirevision);
59731
59732 handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions);
59733 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
59734
59735 var unifiedSpike = unifiedHover && axLetter === hovermode.charAt(0);
59736 var spikecolor = coerce2('spikecolor', unifiedHover ? axLayoutOut.color : undefined);
59737 var spikethickness = coerce2('spikethickness', unifiedHover ? 1.5 : undefined);
59738 var spikedash = coerce2('spikedash', unifiedHover ? 'dot' : undefined);
59739 var spikemode = coerce2('spikemode', unifiedHover ? 'across' : undefined);
59740 var spikesnap = coerce2('spikesnap', unifiedHover ? 'hovered data' : undefined);
59741 var showSpikes = coerce('showspikes', !!unifiedSpike || !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap);
59742
59743 if(!showSpikes) {
59744 delete axLayoutOut.spikecolor;
59745 delete axLayoutOut.spikethickness;
59746 delete axLayoutOut.spikedash;
59747 delete axLayoutOut.spikemode;
59748 delete axLayoutOut.spikesnap;
59749 }
59750
59751 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
59752 letter: axLetter,
59753 counterAxes: counterAxes[axLetter],
59754 overlayableAxes: getOverlayableAxes(axLetter, axName),
59755 grid: layoutOut.grid
59756 });
59757
59758 coerce('title.standoff');
59759
59760 addMissingMatchedAxis();
59761
59762 axLayoutOut._input = axLayoutIn;
59763 }
59764
59765 // coerce the 'missing' axes
59766 i = 0;
59767 while(i < missingMatchedAxisIds.length) {
59768 axId = missingMatchedAxisIds[i++];
59769 axName = id2name(axId);
59770 axLetter = axName.charAt(0);
59771
59772 if(!Lib.isPlainObject(layoutIn[axName])) {
59773 layoutIn[axName] = {};
59774 }
59775
59776 axLayoutIn = layoutIn[axName];
59777 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
59778 newAxLayoutOut();
59779
59780 var defaultOptions2 = {
59781 letter: axLetter,
59782 font: layoutOut.font,
59783 outerTicks: outerTicks[axName],
59784 showGrid: !noGrids[axName],
59785 data: [],
59786 bgColor: bgColor,
59787 calendar: layoutOut.calendar,
59788 automargin: true,
59789 visibleDflt: false,
59790 reverseDflt: false,
59791 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
59792 };
59793
59794 coerce('uirevision', layoutOut.uirevision);
59795
59796 axLayoutOut.type = missingMatchedAxisIdsLookup[axId] || 'linear';
59797
59798 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2, layoutOut);
59799
59800 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
59801 letter: axLetter,
59802 counterAxes: counterAxes[axLetter],
59803 overlayableAxes: getOverlayableAxes(axLetter, axName),
59804 grid: layoutOut.grid
59805 });
59806
59807 coerce('fixedrange');
59808
59809 addMissingMatchedAxis();
59810
59811 axLayoutOut._input = axLayoutIn;
59812 }
59813
59814 // quick second pass for range slider and selector defaults
59815 var rangeSliderDefaults = getComponentMethod('rangeslider', 'handleDefaults');
59816 var rangeSelectorDefaults = getComponentMethod('rangeselector', 'handleDefaults');
59817
59818 for(i = 0; i < xNames.length; i++) {
59819 axName = xNames[i];
59820 axLayoutIn = layoutIn[axName];
59821 axLayoutOut = layoutOut[axName];
59822
59823 rangeSliderDefaults(layoutIn, layoutOut, axName);
59824
59825 if(axLayoutOut.type === 'date') {
59826 rangeSelectorDefaults(
59827 axLayoutIn,
59828 axLayoutOut,
59829 layoutOut,
59830 yNames,
59831 axLayoutOut.calendar
59832 );
59833 }
59834
59835 coerce('fixedrange');
59836 }
59837
59838 for(i = 0; i < yNames.length; i++) {
59839 axName = yNames[i];
59840 axLayoutIn = layoutIn[axName];
59841 axLayoutOut = layoutOut[axName];
59842
59843 var anchoredAxis = layoutOut[id2name(axLayoutOut.anchor)];
59844
59845 var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis);
59846
59847 coerce('fixedrange', fixedRangeDflt);
59848 }
59849
59850 // Finally, handle scale constraints and matching axes.
59851 //
59852 // We need to do this after all axes have coerced both `type`
59853 // (so we link only axes of the same type) and
59854 // `fixedrange` (so we can avoid linking from OR TO a fixed axis).
59855
59856 // sets of axes linked by `scaleanchor` along with the scaleratios compounded
59857 // together, populated in handleConstraintDefaults
59858 var constraintGroups = layoutOut._axisConstraintGroups = [];
59859 // similar to _axisConstraintGroups, but for matching axes
59860 var matchGroups = layoutOut._axisMatchGroups = [];
59861 // make sure to include 'missing' axes here
59862 var allAxisIdsIncludingMissing = allAxisIds.concat(missingMatchedAxisIds);
59863 var axNamesIncludingMissing = axNames.concat(Lib.simpleMap(missingMatchedAxisIds, id2name));
59864
59865 for(i = 0; i < axNamesIncludingMissing.length; i++) {
59866 axName = axNamesIncludingMissing[i];
59867 axLetter = axName.charAt(0);
59868 axLayoutIn = layoutIn[axName];
59869 axLayoutOut = layoutOut[axName];
59870
59871 var scaleanchorDflt;
59872 if(axLetter === 'y' && !axLayoutIn.hasOwnProperty('scaleanchor') && axHasImage[axName]) {
59873 scaleanchorDflt = axLayoutOut.anchor;
59874 } else {
59875 scaleanchorDflt = undefined;
59876 }
59877
59878 var constrainDflt;
59879 if(!axLayoutIn.hasOwnProperty('constrain') && axHasImage[axName]) {
59880 constrainDflt = 'domain';
59881 } else {
59882 constrainDflt = undefined;
59883 }
59884
59885 handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, {
59886 allAxisIds: allAxisIdsIncludingMissing,
59887 layoutOut: layoutOut,
59888 scaleanchorDflt: scaleanchorDflt,
59889 constrainDflt: constrainDflt
59890 });
59891 }
59892
59893 for(i = 0; i < matchGroups.length; i++) {
59894 var group = matchGroups[i];
59895 var rng = null;
59896 var autorange = null;
59897
59898 // find 'matching' range attrs
59899 for(axId in group) {
59900 axLayoutOut = layoutOut[id2name(axId)];
59901 if(!axLayoutOut.matches) {
59902 rng = axLayoutOut.range;
59903 autorange = axLayoutOut.autorange;
59904 }
59905 }
59906 // if `ax.matches` values are reciprocal,
59907 // pick values of first axis in group
59908 if(rng === null || autorange === null) {
59909 for(axId in group) {
59910 axLayoutOut = layoutOut[id2name(axId)];
59911 rng = axLayoutOut.range;
59912 autorange = axLayoutOut.autorange;
59913 break;
59914 }
59915 }
59916 // apply matching range attrs
59917 for(axId in group) {
59918 axLayoutOut = layoutOut[id2name(axId)];
59919 if(axLayoutOut.matches) {
59920 axLayoutOut.range = rng.slice();
59921 axLayoutOut.autorange = autorange;
59922 }
59923 axLayoutOut._matchGroup = group;
59924 }
59925
59926 // remove matching axis from scaleanchor constraint groups (for now)
59927 if(constraintGroups.length) {
59928 for(axId in group) {
59929 for(j = 0; j < constraintGroups.length; j++) {
59930 var group2 = constraintGroups[j];
59931 for(var axId2 in group2) {
59932 if(axId === axId2) {
59933 Lib.warn('Axis ' + axId2 + ' is set with both ' +
59934 'a *scaleanchor* and *matches* constraint; ' +
59935 'ignoring the scale constraint.');
59936
59937 delete group2[axId2];
59938 if(Object.keys(group2).length < 2) {
59939 constraintGroups.splice(j, 1);
59940 }
59941 }
59942 }
59943 }
59944 }
59945 }
59946 }
59947};
59948
59949},{"../../components/color":52,"../../components/fx/helpers":88,"../../components/fx/hovermode_defaults":91,"../../lib":178,"../../plot_api/plot_template":212,"../../registry":269,"../layout_attributes":254,"./axis_defaults":224,"./axis_ids":225,"./constants":228,"./constraints":229,"./layout_attributes":236,"./position_defaults":239,"./type_defaults":247}],238:[function(_dereq_,module,exports){
59950/**
59951* Copyright 2012-2020, Plotly, Inc.
59952* All rights reserved.
59953*
59954* This source code is licensed under the MIT license found in the
59955* LICENSE file in the root directory of this source tree.
59956*/
59957
59958'use strict';
59959
59960var colorMix = _dereq_('tinycolor2').mix;
59961var lightFraction = _dereq_('../../components/color/attributes').lightFraction;
59962var Lib = _dereq_('../../lib');
59963
59964/**
59965 * @param {object} opts :
59966 * - dfltColor {string} : default axis color
59967 * - bgColor {string} : combined subplot bg color
59968 * - blend {number, optional} : blend percentage (to compute dflt grid color)
59969 * - showLine {boolean} : show line by default
59970 * - showGrid {boolean} : show grid by default
59971 * - noZeroLine {boolean} : don't coerce zeroline* attributes
59972 * - attributes {object} : attribute object associated with input containers
59973 */
59974module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) {
59975 opts = opts || {};
59976
59977 var dfltColor = opts.dfltColor;
59978
59979 function coerce2(attr, dflt) {
59980 return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt);
59981 }
59982
59983 var lineColor = coerce2('linecolor', dfltColor);
59984 var lineWidth = coerce2('linewidth');
59985 var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth);
59986
59987 if(!showLine) {
59988 delete containerOut.linecolor;
59989 delete containerOut.linewidth;
59990 }
59991
59992 var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString();
59993 var gridColor = coerce2('gridcolor', gridColorDflt);
59994 var gridWidth = coerce2('gridwidth');
59995 var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth);
59996
59997 if(!showGridLines) {
59998 delete containerOut.gridcolor;
59999 delete containerOut.gridwidth;
60000 }
60001
60002 if(!opts.noZeroLine) {
60003 var zeroLineColor = coerce2('zerolinecolor', dfltColor);
60004 var zeroLineWidth = coerce2('zerolinewidth');
60005 var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth);
60006
60007 if(!showZeroLine) {
60008 delete containerOut.zerolinecolor;
60009 delete containerOut.zerolinewidth;
60010 }
60011 }
60012};
60013
60014},{"../../components/color/attributes":51,"../../lib":178,"tinycolor2":35}],239:[function(_dereq_,module,exports){
60015/**
60016* Copyright 2012-2020, Plotly, Inc.
60017* All rights reserved.
60018*
60019* This source code is licensed under the MIT license found in the
60020* LICENSE file in the root directory of this source tree.
60021*/
60022
60023
60024'use strict';
60025
60026var isNumeric = _dereq_('fast-isnumeric');
60027
60028var Lib = _dereq_('../../lib');
60029
60030
60031module.exports = function handlePositionDefaults(containerIn, containerOut, coerce, options) {
60032 var counterAxes = options.counterAxes || [];
60033 var overlayableAxes = options.overlayableAxes || [];
60034 var letter = options.letter;
60035 var grid = options.grid;
60036
60037 var dfltAnchor, dfltDomain, dfltSide, dfltPosition;
60038
60039 if(grid) {
60040 dfltDomain = grid._domains[letter][grid._axisMap[containerOut._id]];
60041 dfltAnchor = grid._anchors[containerOut._id];
60042 if(dfltDomain) {
60043 dfltSide = grid[letter + 'side'].split(' ')[0];
60044 dfltPosition = grid.domain[letter][dfltSide === 'right' || dfltSide === 'top' ? 1 : 0];
60045 }
60046 }
60047
60048 // Even if there's a grid, this axis may not be in it - fall back on non-grid defaults
60049 dfltDomain = dfltDomain || [0, 1];
60050 dfltAnchor = dfltAnchor || (isNumeric(containerIn.position) ? 'free' : (counterAxes[0] || 'free'));
60051 dfltSide = dfltSide || (letter === 'x' ? 'bottom' : 'left');
60052 dfltPosition = dfltPosition || 0;
60053
60054 var anchor = Lib.coerce(containerIn, containerOut, {
60055 anchor: {
60056 valType: 'enumerated',
60057 values: ['free'].concat(counterAxes),
60058 dflt: dfltAnchor
60059 }
60060 }, 'anchor');
60061
60062 if(anchor === 'free') coerce('position', dfltPosition);
60063
60064 Lib.coerce(containerIn, containerOut, {
60065 side: {
60066 valType: 'enumerated',
60067 values: letter === 'x' ? ['bottom', 'top'] : ['left', 'right'],
60068 dflt: dfltSide
60069 }
60070 }, 'side');
60071
60072 var overlaying = false;
60073 if(overlayableAxes.length) {
60074 overlaying = Lib.coerce(containerIn, containerOut, {
60075 overlaying: {
60076 valType: 'enumerated',
60077 values: [false].concat(overlayableAxes),
60078 dflt: false
60079 }
60080 }, 'overlaying');
60081 }
60082
60083 if(!overlaying) {
60084 // TODO: right now I'm copying this domain over to overlaying axes
60085 // in ax.setscale()... but this means we still need (imperfect) logic
60086 // in the axes popover to hide domain for the overlaying axis.
60087 // perhaps I should make a private version _domain that all axes get???
60088 var domain = coerce('domain', dfltDomain);
60089
60090 // according to https://www.npmjs.com/package/canvas-size
60091 // the minimum value of max canvas width across browsers and devices is 4096
60092 // which applied in the calculation below:
60093 if(domain[0] > domain[1] - 1 / 4096) containerOut.domain = dfltDomain;
60094 Lib.noneOrAll(containerIn.domain, containerOut.domain, dfltDomain);
60095 }
60096
60097 coerce('layer');
60098
60099 return containerOut;
60100};
60101
60102},{"../../lib":178,"fast-isnumeric":18}],240:[function(_dereq_,module,exports){
60103/**
60104* Copyright 2012-2020, Plotly, Inc.
60105* All rights reserved.
60106*
60107* This source code is licensed under the MIT license found in the
60108* LICENSE file in the root directory of this source tree.
60109*/
60110
60111
60112'use strict';
60113
60114var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
60115
60116module.exports = function scaleZoom(ax, factor, centerFraction) {
60117 if(centerFraction === undefined) {
60118 centerFraction = FROM_BL[ax.constraintoward || 'center'];
60119 }
60120
60121 var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])];
60122 var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction;
60123
60124 ax.range = ax._input.range = [
60125 ax.l2r(center + (rangeLinear[0] - center) * factor),
60126 ax.l2r(center + (rangeLinear[1] - center) * factor)
60127 ];
60128};
60129
60130},{"../../constants/alignment":154}],241:[function(_dereq_,module,exports){
60131/**
60132* Copyright 2012-2020, Plotly, Inc.
60133* All rights reserved.
60134*
60135* This source code is licensed under the MIT license found in the
60136* LICENSE file in the root directory of this source tree.
60137*/
60138
60139
60140'use strict';
60141
60142var polybool = _dereq_('polybooljs');
60143
60144var Registry = _dereq_('../../registry');
60145var dashStyle = _dereq_('../../components/drawing').dashStyle;
60146var Color = _dereq_('../../components/color');
60147var Fx = _dereq_('../../components/fx');
60148var makeEventData = _dereq_('../../components/fx/helpers').makeEventData;
60149var dragHelpers = _dereq_('../../components/dragelement/helpers');
60150var freeMode = dragHelpers.freeMode;
60151var rectMode = dragHelpers.rectMode;
60152var drawMode = dragHelpers.drawMode;
60153var openMode = dragHelpers.openMode;
60154var selectMode = dragHelpers.selectMode;
60155
60156var displayOutlines = _dereq_('../../components/shapes/draw_newshape/display_outlines');
60157var handleEllipse = _dereq_('../../components/shapes/draw_newshape/helpers').handleEllipse;
60158var newShapes = _dereq_('../../components/shapes/draw_newshape/newshapes');
60159
60160var Lib = _dereq_('../../lib');
60161var polygon = _dereq_('../../lib/polygon');
60162var throttle = _dereq_('../../lib/throttle');
60163var getFromId = _dereq_('./axis_ids').getFromId;
60164var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
60165
60166var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
60167
60168var constants = _dereq_('./constants');
60169var MINSELECT = constants.MINSELECT;
60170
60171var filteredPolygon = polygon.filter;
60172var polygonTester = polygon.tester;
60173
60174var clearSelect = _dereq_('./handle_outline').clearSelect;
60175
60176var helpers = _dereq_('./helpers');
60177var p2r = helpers.p2r;
60178var axValue = helpers.axValue;
60179var getTransform = helpers.getTransform;
60180
60181function prepSelect(e, startX, startY, dragOptions, mode) {
60182 var isFreeMode = freeMode(mode);
60183 var isRectMode = rectMode(mode);
60184 var isOpenMode = openMode(mode);
60185 var isDrawMode = drawMode(mode);
60186 var isSelectMode = selectMode(mode);
60187
60188 var isLine = mode === 'drawline';
60189 var isEllipse = mode === 'drawcircle';
60190 var isLineOrEllipse = isLine || isEllipse; // cases with two start & end positions
60191
60192 var gd = dragOptions.gd;
60193 var fullLayout = gd._fullLayout;
60194 var zoomLayer = fullLayout._zoomlayer;
60195 var dragBBox = dragOptions.element.getBoundingClientRect();
60196 var plotinfo = dragOptions.plotinfo;
60197 var transform = getTransform(plotinfo);
60198 var x0 = startX - dragBBox.left;
60199 var y0 = startY - dragBBox.top;
60200 var x1 = x0;
60201 var y1 = y0;
60202 var path0 = 'M' + x0 + ',' + y0;
60203 var pw = dragOptions.xaxes[0]._length;
60204 var ph = dragOptions.yaxes[0]._length;
60205 var allAxes = dragOptions.xaxes.concat(dragOptions.yaxes);
60206 var subtract = e.altKey &&
60207 !(drawMode(mode) && isOpenMode);
60208
60209 var filterPoly, selectionTester, mergedPolygons, currentPolygon;
60210 var i, searchInfo, eventData;
60211
60212 coerceSelectionsCache(e, gd, dragOptions);
60213
60214 if(isFreeMode) {
60215 filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX);
60216 }
60217
60218 var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data(isDrawMode ? [0] : [1, 2]);
60219 var drwStyle = fullLayout.newshape;
60220
60221 outlines.enter()
60222 .append('path')
60223 .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; })
60224 .style(isDrawMode ? {
60225 opacity: drwStyle.opacity / 2,
60226 fill: isOpenMode ? undefined : drwStyle.fillcolor,
60227 stroke: drwStyle.line.color,
60228 'stroke-dasharray': dashStyle(drwStyle.line.dash, drwStyle.line.width),
60229 'stroke-width': drwStyle.line.width + 'px'
60230 } : {})
60231 .attr('fill-rule', drwStyle.fillrule)
60232 .classed('cursor-move', isDrawMode ? true : false)
60233 .attr('transform', transform)
60234 .attr('d', path0 + 'Z');
60235
60236 var corners = zoomLayer.append('path')
60237 .attr('class', 'zoombox-corners')
60238 .style({
60239 fill: Color.background,
60240 stroke: Color.defaultLine,
60241 'stroke-width': 1
60242 })
60243 .attr('transform', transform)
60244 .attr('d', 'M0,0Z');
60245
60246
60247 var throttleID = fullLayout._uid + constants.SELECTID;
60248 var selection = [];
60249
60250 // find the traces to search for selection points
60251 var searchTraces = determineSearchTraces(gd, dragOptions.xaxes,
60252 dragOptions.yaxes, dragOptions.subplot);
60253
60254 function ascending(a, b) { return a - b; }
60255
60256 // allow subplots to override fillRangeItems routine
60257 var fillRangeItems;
60258
60259 if(plotinfo.fillRangeItems) {
60260 fillRangeItems = plotinfo.fillRangeItems;
60261 } else {
60262 if(isRectMode) {
60263 fillRangeItems = function(eventData, poly) {
60264 var ranges = eventData.range = {};
60265
60266 for(i = 0; i < allAxes.length; i++) {
60267 var ax = allAxes[i];
60268 var axLetter = ax._id.charAt(0);
60269
60270 ranges[ax._id] = [
60271 p2r(ax, poly[axLetter + 'min']),
60272 p2r(ax, poly[axLetter + 'max'])
60273 ].sort(ascending);
60274 }
60275 };
60276 } else { // case of isFreeMode
60277 fillRangeItems = function(eventData, poly, filterPoly) {
60278 var dataPts = eventData.lassoPoints = {};
60279
60280 for(i = 0; i < allAxes.length; i++) {
60281 var ax = allAxes[i];
60282 dataPts[ax._id] = filterPoly.filtered.map(axValue(ax));
60283 }
60284 };
60285 }
60286 }
60287
60288 dragOptions.moveFn = function(dx0, dy0) {
60289 x1 = Math.max(0, Math.min(pw, dx0 + x0));
60290 y1 = Math.max(0, Math.min(ph, dy0 + y0));
60291
60292 var dx = Math.abs(x1 - x0);
60293 var dy = Math.abs(y1 - y0);
60294
60295 if(isRectMode) {
60296 var direction;
60297 var start, end;
60298
60299 if(isSelectMode) {
60300 var q = fullLayout.selectdirection;
60301
60302 if(q === 'any') {
60303 if(dy < Math.min(dx * 0.6, MINSELECT)) {
60304 direction = 'h';
60305 } else if(dx < Math.min(dy * 0.6, MINSELECT)) {
60306 direction = 'v';
60307 } else {
60308 direction = 'd';
60309 }
60310 } else {
60311 direction = q;
60312 }
60313
60314 switch(direction) {
60315 case 'h':
60316 start = isEllipse ? ph / 2 : 0;
60317 end = ph;
60318 break;
60319 case 'v':
60320 start = isEllipse ? pw / 2 : 0;
60321 end = pw;
60322 break;
60323 }
60324 }
60325
60326 if(isDrawMode) {
60327 switch(fullLayout.newshape.drawdirection) {
60328 case 'vertical':
60329 direction = 'h';
60330 start = isEllipse ? ph / 2 : 0;
60331 end = ph;
60332 break;
60333 case 'horizontal':
60334 direction = 'v';
60335 start = isEllipse ? pw / 2 : 0;
60336 end = pw;
60337 break;
60338 case 'ortho':
60339 if(dx < dy) {
60340 direction = 'h';
60341 start = y0;
60342 end = y1;
60343 } else {
60344 direction = 'v';
60345 start = x0;
60346 end = x1;
60347 }
60348 break;
60349 default: // i.e. case of 'diagonal'
60350 direction = 'd';
60351 }
60352 }
60353
60354 if(direction === 'h') {
60355 // horizontal motion
60356 currentPolygon = isLineOrEllipse ?
60357 handleEllipse(isEllipse, [x1, start], [x1, end]) : // using x1 instead of x0 allows adjusting the line while drawing
60358 [[x0, start], [x0, end], [x1, end], [x1, start]]; // make a vertical box
60359
60360 currentPolygon.xmin = isLineOrEllipse ? x1 : Math.min(x0, x1);
60361 currentPolygon.xmax = isLineOrEllipse ? x1 : Math.max(x0, x1);
60362 currentPolygon.ymin = Math.min(start, end);
60363 currentPolygon.ymax = Math.max(start, end);
60364 // extras to guide users in keeping a straight selection
60365 corners.attr('d', 'M' + currentPolygon.xmin + ',' + (y0 - MINSELECT) +
60366 'h-4v' + (2 * MINSELECT) + 'h4Z' +
60367 'M' + (currentPolygon.xmax - 1) + ',' + (y0 - MINSELECT) +
60368 'h4v' + (2 * MINSELECT) + 'h-4Z');
60369 } else if(direction === 'v') {
60370 // vertical motion
60371 currentPolygon = isLineOrEllipse ?
60372 handleEllipse(isEllipse, [start, y1], [end, y1]) : // using y1 instead of y0 allows adjusting the line while drawing
60373 [[start, y0], [start, y1], [end, y1], [end, y0]]; // make a horizontal box
60374
60375 currentPolygon.xmin = Math.min(start, end);
60376 currentPolygon.xmax = Math.max(start, end);
60377 currentPolygon.ymin = isLineOrEllipse ? y1 : Math.min(y0, y1);
60378 currentPolygon.ymax = isLineOrEllipse ? y1 : Math.max(y0, y1);
60379 corners.attr('d', 'M' + (x0 - MINSELECT) + ',' + currentPolygon.ymin +
60380 'v-4h' + (2 * MINSELECT) + 'v4Z' +
60381 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) +
60382 'v4h' + (2 * MINSELECT) + 'v-4Z');
60383 } else if(direction === 'd') {
60384 // diagonal motion
60385 currentPolygon = isLineOrEllipse ?
60386 handleEllipse(isEllipse, [x0, y0], [x1, y1]) :
60387 [[x0, y0], [x0, y1], [x1, y1], [x1, y0]];
60388
60389 currentPolygon.xmin = Math.min(x0, x1);
60390 currentPolygon.xmax = Math.max(x0, x1);
60391 currentPolygon.ymin = Math.min(y0, y1);
60392 currentPolygon.ymax = Math.max(y0, y1);
60393 corners.attr('d', 'M0,0Z');
60394 }
60395 } else if(isFreeMode) {
60396 filterPoly.addPt([x1, y1]);
60397 currentPolygon = filterPoly.filtered;
60398 }
60399
60400 // create outline & tester
60401 if(dragOptions.selectionDefs && dragOptions.selectionDefs.length) {
60402 mergedPolygons = mergePolygons(dragOptions.mergedPolygons, currentPolygon, subtract);
60403 currentPolygon.subtract = subtract;
60404 selectionTester = multiTester(dragOptions.selectionDefs.concat([currentPolygon]));
60405 } else {
60406 mergedPolygons = [currentPolygon];
60407 selectionTester = polygonTester(currentPolygon);
60408 }
60409
60410 // display polygons on the screen
60411 displayOutlines(convertPoly(mergedPolygons, isOpenMode), outlines, dragOptions);
60412
60413 if(isSelectMode) {
60414 throttle.throttle(
60415 throttleID,
60416 constants.SELECTDELAY,
60417 function() {
60418 selection = [];
60419
60420 var thisSelection;
60421 var traceSelections = [];
60422 var traceSelection;
60423 for(i = 0; i < searchTraces.length; i++) {
60424 searchInfo = searchTraces[i];
60425
60426 traceSelection = searchInfo._module.selectPoints(searchInfo, selectionTester);
60427 traceSelections.push(traceSelection);
60428
60429 thisSelection = fillSelectionItem(traceSelection, searchInfo);
60430
60431 if(selection.length) {
60432 for(var j = 0; j < thisSelection.length; j++) {
60433 selection.push(thisSelection[j]);
60434 }
60435 } else selection = thisSelection;
60436 }
60437
60438 eventData = {points: selection};
60439 updateSelectedState(gd, searchTraces, eventData);
60440 fillRangeItems(eventData, currentPolygon, filterPoly);
60441 dragOptions.gd.emit('plotly_selecting', eventData);
60442 }
60443 );
60444 }
60445 };
60446
60447 dragOptions.clickFn = function(numClicks, evt) {
60448 corners.remove();
60449
60450 if(gd._fullLayout._activeShapeIndex >= 0) {
60451 gd._fullLayout._deactivateShape(gd);
60452 return;
60453 }
60454 if(isDrawMode) return;
60455
60456 var clickmode = fullLayout.clickmode;
60457
60458 throttle.done(throttleID).then(function() {
60459 throttle.clear(throttleID);
60460 if(numClicks === 2) {
60461 // clear selection on doubleclick
60462 outlines.remove();
60463 for(i = 0; i < searchTraces.length; i++) {
60464 searchInfo = searchTraces[i];
60465 searchInfo._module.selectPoints(searchInfo, false);
60466 }
60467
60468 updateSelectedState(gd, searchTraces);
60469
60470 clearSelectionsCache(dragOptions);
60471
60472 gd.emit('plotly_deselect', null);
60473 } else {
60474 if(clickmode.indexOf('select') > -1) {
60475 selectOnClick(evt, gd, dragOptions.xaxes, dragOptions.yaxes,
60476 dragOptions.subplot, dragOptions, outlines);
60477 }
60478
60479 if(clickmode === 'event') {
60480 // TODO: remove in v2 - this was probably never intended to work as it does,
60481 // but in case anyone depends on it we don't want to break it now.
60482 // Note that click-to-select introduced pre v2 also emitts proper
60483 // event data when clickmode is having 'select' in its flag list.
60484 gd.emit('plotly_selected', undefined);
60485 }
60486 }
60487
60488 Fx.click(gd, evt);
60489 }).catch(Lib.error);
60490 };
60491
60492 dragOptions.doneFn = function() {
60493 corners.remove();
60494
60495 throttle.done(throttleID).then(function() {
60496 throttle.clear(throttleID);
60497 dragOptions.gd.emit('plotly_selected', eventData);
60498
60499 if(currentPolygon && dragOptions.selectionDefs) {
60500 // save last polygons
60501 currentPolygon.subtract = subtract;
60502 dragOptions.selectionDefs.push(currentPolygon);
60503
60504 // we have to keep reference to arrays container
60505 dragOptions.mergedPolygons.length = 0;
60506 [].push.apply(dragOptions.mergedPolygons, mergedPolygons);
60507 }
60508
60509 if(dragOptions.doneFnCompleted) {
60510 dragOptions.doneFnCompleted(selection);
60511 }
60512 }).catch(Lib.error);
60513
60514 if(isDrawMode) {
60515 clearSelectionsCache(dragOptions);
60516 }
60517 };
60518}
60519
60520function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) {
60521 var hoverData = gd._hoverdata;
60522 var fullLayout = gd._fullLayout;
60523 var clickmode = fullLayout.clickmode;
60524 var sendEvents = clickmode.indexOf('event') > -1;
60525 var selection = [];
60526 var searchTraces, searchInfo, currentSelectionDef, selectionTester, traceSelection;
60527 var thisTracesSelection, pointOrBinSelected, subtract, eventData, i;
60528
60529 if(isHoverDataSet(hoverData)) {
60530 coerceSelectionsCache(evt, gd, dragOptions);
60531 searchTraces = determineSearchTraces(gd, xAxes, yAxes, subplot);
60532 var clickedPtInfo = extractClickedPtInfo(hoverData, searchTraces);
60533 var isBinnedTrace = clickedPtInfo.pointNumbers.length > 0;
60534
60535
60536 // Note: potentially costly operation isPointOrBinSelected is
60537 // called as late as possible through the use of an assignment
60538 // in an if condition.
60539 if(isBinnedTrace ?
60540 isOnlyThisBinSelected(searchTraces, clickedPtInfo) :
60541 isOnlyOnePointSelected(searchTraces) &&
60542 (pointOrBinSelected = isPointOrBinSelected(clickedPtInfo))) {
60543 if(polygonOutlines) polygonOutlines.remove();
60544 for(i = 0; i < searchTraces.length; i++) {
60545 searchInfo = searchTraces[i];
60546 searchInfo._module.selectPoints(searchInfo, false);
60547 }
60548
60549 updateSelectedState(gd, searchTraces);
60550
60551 clearSelectionsCache(dragOptions);
60552
60553 if(sendEvents) {
60554 gd.emit('plotly_deselect', null);
60555 }
60556 } else {
60557 subtract = evt.shiftKey &&
60558 (pointOrBinSelected !== undefined ?
60559 pointOrBinSelected :
60560 isPointOrBinSelected(clickedPtInfo));
60561 currentSelectionDef = newPointSelectionDef(clickedPtInfo.pointNumber, clickedPtInfo.searchInfo, subtract);
60562
60563 var allSelectionDefs = dragOptions.selectionDefs.concat([currentSelectionDef]);
60564 selectionTester = multiTester(allSelectionDefs);
60565
60566 for(i = 0; i < searchTraces.length; i++) {
60567 traceSelection = searchTraces[i]._module.selectPoints(searchTraces[i], selectionTester);
60568 thisTracesSelection = fillSelectionItem(traceSelection, searchTraces[i]);
60569
60570 if(selection.length) {
60571 for(var j = 0; j < thisTracesSelection.length; j++) {
60572 selection.push(thisTracesSelection[j]);
60573 }
60574 } else selection = thisTracesSelection;
60575 }
60576
60577 eventData = {points: selection};
60578 updateSelectedState(gd, searchTraces, eventData);
60579
60580 if(currentSelectionDef && dragOptions) {
60581 dragOptions.selectionDefs.push(currentSelectionDef);
60582 }
60583
60584 if(polygonOutlines) {
60585 var polygons = dragOptions.mergedPolygons;
60586 var isOpenMode = openMode(dragOptions.dragmode);
60587
60588 // display polygons on the screen
60589 displayOutlines(convertPoly(polygons, isOpenMode), polygonOutlines, dragOptions);
60590 }
60591
60592 if(sendEvents) {
60593 gd.emit('plotly_selected', eventData);
60594 }
60595 }
60596 }
60597}
60598
60599/**
60600 * Constructs a new point selection definition object.
60601 */
60602function newPointSelectionDef(pointNumber, searchInfo, subtract) {
60603 return {
60604 pointNumber: pointNumber,
60605 searchInfo: searchInfo,
60606 subtract: subtract
60607 };
60608}
60609
60610function isPointSelectionDef(o) {
60611 return 'pointNumber' in o && 'searchInfo' in o;
60612}
60613
60614/*
60615 * Constructs a new point number tester.
60616 */
60617function newPointNumTester(pointSelectionDef) {
60618 return {
60619 xmin: 0,
60620 xmax: 0,
60621 ymin: 0,
60622 ymax: 0,
60623 pts: [],
60624 contains: function(pt, omitFirstEdge, pointNumber, searchInfo) {
60625 var idxWantedTrace = pointSelectionDef.searchInfo.cd[0].trace._expandedIndex;
60626 var idxActualTrace = searchInfo.cd[0].trace._expandedIndex;
60627 return idxActualTrace === idxWantedTrace &&
60628 pointNumber === pointSelectionDef.pointNumber;
60629 },
60630 isRect: false,
60631 degenerate: false,
60632 subtract: pointSelectionDef.subtract
60633 };
60634}
60635
60636/**
60637 * Wraps multiple selection testers.
60638 *
60639 * @param {Array} list - An array of selection testers.
60640 *
60641 * @return a selection tester object with a contains function
60642 * that can be called to evaluate a point against all wrapped
60643 * selection testers that were passed in list.
60644 */
60645function multiTester(list) {
60646 var testers = [];
60647 var xmin = isPointSelectionDef(list[0]) ? 0 : list[0][0][0];
60648 var xmax = xmin;
60649 var ymin = isPointSelectionDef(list[0]) ? 0 : list[0][0][1];
60650 var ymax = ymin;
60651
60652 for(var i = 0; i < list.length; i++) {
60653 if(isPointSelectionDef(list[i])) {
60654 testers.push(newPointNumTester(list[i]));
60655 } else {
60656 var tester = polygon.tester(list[i]);
60657 tester.subtract = list[i].subtract;
60658 testers.push(tester);
60659 xmin = Math.min(xmin, tester.xmin);
60660 xmax = Math.max(xmax, tester.xmax);
60661 ymin = Math.min(ymin, tester.ymin);
60662 ymax = Math.max(ymax, tester.ymax);
60663 }
60664 }
60665
60666 /**
60667 * Tests if the given point is within this tester.
60668 *
60669 * @param {Array} pt - [0] is the x coordinate, [1] is the y coordinate of the point.
60670 * @param {*} arg - An optional parameter to pass down to wrapped testers.
60671 * @param {number} pointNumber - The point number of the point within the underlying data array.
60672 * @param {number} searchInfo - An object identifying the trace the point is contained in.
60673 *
60674 * @return {boolean} true if point is considered to be selected, false otherwise.
60675 */
60676 function contains(pt, arg, pointNumber, searchInfo) {
60677 var contained = false;
60678 for(var i = 0; i < testers.length; i++) {
60679 if(testers[i].contains(pt, arg, pointNumber, searchInfo)) {
60680 // if contained by subtract tester - exclude the point
60681 contained = testers[i].subtract === false;
60682 }
60683 }
60684
60685 return contained;
60686 }
60687
60688 return {
60689 xmin: xmin,
60690 xmax: xmax,
60691 ymin: ymin,
60692 ymax: ymax,
60693 pts: [],
60694 contains: contains,
60695 isRect: false,
60696 degenerate: false
60697 };
60698}
60699
60700function coerceSelectionsCache(evt, gd, dragOptions) {
60701 gd._fullLayout._drawing = false;
60702
60703 var fullLayout = gd._fullLayout;
60704 var plotinfo = dragOptions.plotinfo;
60705 var dragmode = dragOptions.dragmode;
60706
60707 var selectingOnSameSubplot = (
60708 fullLayout._lastSelectedSubplot &&
60709 fullLayout._lastSelectedSubplot === plotinfo.id
60710 );
60711
60712 var hasModifierKey = (evt.shiftKey || evt.altKey) &&
60713 !(drawMode(dragmode) && openMode(dragmode));
60714
60715 if(selectingOnSameSubplot && hasModifierKey &&
60716 (plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) {
60717 // take over selection definitions from prev mode, if any
60718 dragOptions.selectionDefs = plotinfo.selection.selectionDefs;
60719 dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons;
60720 } else if(!hasModifierKey || !plotinfo.selection) {
60721 clearSelectionsCache(dragOptions);
60722 }
60723
60724 // clear selection outline when selecting a different subplot
60725 if(!selectingOnSameSubplot) {
60726 clearSelect(gd);
60727 fullLayout._lastSelectedSubplot = plotinfo.id;
60728 }
60729}
60730
60731function clearSelectionsCache(dragOptions) {
60732 var dragmode = dragOptions.dragmode;
60733 var plotinfo = dragOptions.plotinfo;
60734
60735 var gd = dragOptions.gd;
60736 if(gd._fullLayout._activeShapeIndex >= 0) {
60737 gd._fullLayout._deactivateShape(gd);
60738 }
60739
60740 if(drawMode(dragmode)) {
60741 var fullLayout = gd._fullLayout;
60742 var zoomLayer = fullLayout._zoomlayer;
60743
60744 var outlines = zoomLayer.selectAll('.select-outline-' + plotinfo.id);
60745 if(outlines && gd._fullLayout._drawing) {
60746 // add shape
60747 var shapes = newShapes(outlines, dragOptions);
60748 if(shapes) {
60749 Registry.call('_guiRelayout', gd, {
60750 shapes: shapes
60751 });
60752 }
60753
60754 gd._fullLayout._drawing = false;
60755 }
60756 }
60757
60758 plotinfo.selection = {};
60759 plotinfo.selection.selectionDefs = dragOptions.selectionDefs = [];
60760 plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = [];
60761}
60762
60763function determineSearchTraces(gd, xAxes, yAxes, subplot) {
60764 var searchTraces = [];
60765 var xAxisIds = xAxes.map(function(ax) { return ax._id; });
60766 var yAxisIds = yAxes.map(function(ax) { return ax._id; });
60767 var cd, trace, i;
60768
60769 for(i = 0; i < gd.calcdata.length; i++) {
60770 cd = gd.calcdata[i];
60771 trace = cd[0].trace;
60772
60773 if(trace.visible !== true || !trace._module || !trace._module.selectPoints) continue;
60774
60775 if(subplot && (trace.subplot === subplot || trace.geo === subplot)) {
60776 searchTraces.push(createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]));
60777 } else if(
60778 trace.type === 'splom' &&
60779 // FIXME: make sure we don't have more than single axis for splom
60780 trace._xaxes[xAxisIds[0]] && trace._yaxes[yAxisIds[0]]
60781 ) {
60782 var info = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
60783 info.scene = gd._fullLayout._splomScenes[trace.uid];
60784 searchTraces.push(info);
60785 } else if(
60786 trace.type === 'sankey'
60787 ) {
60788 var sankeyInfo = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
60789 searchTraces.push(sankeyInfo);
60790 } else {
60791 if(xAxisIds.indexOf(trace.xaxis) === -1) continue;
60792 if(yAxisIds.indexOf(trace.yaxis) === -1) continue;
60793
60794 searchTraces.push(createSearchInfo(trace._module, cd,
60795 getFromId(gd, trace.xaxis), getFromId(gd, trace.yaxis)));
60796 }
60797 }
60798
60799 return searchTraces;
60800
60801 function createSearchInfo(module, calcData, xaxis, yaxis) {
60802 return {
60803 _module: module,
60804 cd: calcData,
60805 xaxis: xaxis,
60806 yaxis: yaxis
60807 };
60808 }
60809}
60810
60811function isHoverDataSet(hoverData) {
60812 return hoverData &&
60813 Array.isArray(hoverData) &&
60814 hoverData[0].hoverOnBox !== true;
60815}
60816
60817function extractClickedPtInfo(hoverData, searchTraces) {
60818 var hoverDatum = hoverData[0];
60819 var pointNumber = -1;
60820 var pointNumbers = [];
60821 var searchInfo, i;
60822
60823 for(i = 0; i < searchTraces.length; i++) {
60824 searchInfo = searchTraces[i];
60825 if(hoverDatum.fullData._expandedIndex === searchInfo.cd[0].trace._expandedIndex) {
60826 // Special case for box (and violin)
60827 if(hoverDatum.hoverOnBox === true) {
60828 break;
60829 }
60830
60831 // Hint: in some traces like histogram, one graphical element
60832 // doesn't correspond to one particular data point, but to
60833 // bins of data points. Thus, hoverDatum can have a binNumber
60834 // property instead of pointNumber.
60835 if(hoverDatum.pointNumber !== undefined) {
60836 pointNumber = hoverDatum.pointNumber;
60837 } else if(hoverDatum.binNumber !== undefined) {
60838 pointNumber = hoverDatum.binNumber;
60839 pointNumbers = hoverDatum.pointNumbers;
60840 }
60841
60842 break;
60843 }
60844 }
60845
60846 return {
60847 pointNumber: pointNumber,
60848 pointNumbers: pointNumbers,
60849 searchInfo: searchInfo
60850 };
60851}
60852
60853function isPointOrBinSelected(clickedPtInfo) {
60854 var trace = clickedPtInfo.searchInfo.cd[0].trace;
60855 var ptNum = clickedPtInfo.pointNumber;
60856 var ptNums = clickedPtInfo.pointNumbers;
60857 var ptNumsSet = ptNums.length > 0;
60858
60859 // When pointsNumbers is set (e.g. histogram's binning),
60860 // it is assumed that when the first point of
60861 // a bin is selected, all others are as well
60862 var ptNumToTest = ptNumsSet ? ptNums[0] : ptNum;
60863
60864 // TODO potential performance improvement
60865 // Primarily we need this function to determine if a click adds
60866 // or subtracts from a selection.
60867 // In cases `trace.selectedpoints` is a huge array, indexOf
60868 // might be slow. One remedy would be to introduce a hash somewhere.
60869 return trace.selectedpoints ? trace.selectedpoints.indexOf(ptNumToTest) > -1 : false;
60870}
60871
60872function isOnlyThisBinSelected(searchTraces, clickedPtInfo) {
60873 var tracesWithSelectedPts = [];
60874 var searchInfo, trace, isSameTrace, i;
60875
60876 for(i = 0; i < searchTraces.length; i++) {
60877 searchInfo = searchTraces[i];
60878 if(searchInfo.cd[0].trace.selectedpoints && searchInfo.cd[0].trace.selectedpoints.length > 0) {
60879 tracesWithSelectedPts.push(searchInfo);
60880 }
60881 }
60882
60883 if(tracesWithSelectedPts.length === 1) {
60884 isSameTrace = tracesWithSelectedPts[0] === clickedPtInfo.searchInfo;
60885 if(isSameTrace) {
60886 trace = clickedPtInfo.searchInfo.cd[0].trace;
60887 if(trace.selectedpoints.length === clickedPtInfo.pointNumbers.length) {
60888 for(i = 0; i < clickedPtInfo.pointNumbers.length; i++) {
60889 if(trace.selectedpoints.indexOf(clickedPtInfo.pointNumbers[i]) < 0) {
60890 return false;
60891 }
60892 }
60893 return true;
60894 }
60895 }
60896 }
60897
60898 return false;
60899}
60900
60901function isOnlyOnePointSelected(searchTraces) {
60902 var len = 0;
60903 var searchInfo, trace, i;
60904
60905 for(i = 0; i < searchTraces.length; i++) {
60906 searchInfo = searchTraces[i];
60907 trace = searchInfo.cd[0].trace;
60908 if(trace.selectedpoints) {
60909 if(trace.selectedpoints.length > 1) return false;
60910
60911 len += trace.selectedpoints.length;
60912 if(len > 1) return false;
60913 }
60914 }
60915
60916 return len === 1;
60917}
60918
60919function updateSelectedState(gd, searchTraces, eventData) {
60920 var i, searchInfo, cd, trace;
60921
60922 // before anything else, update preGUI if necessary
60923 for(i = 0; i < searchTraces.length; i++) {
60924 var fullInputTrace = searchTraces[i].cd[0].trace._fullInput;
60925 var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid] || {};
60926 if(tracePreGUI.selectedpoints === undefined) {
60927 tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null;
60928 }
60929 }
60930
60931 if(eventData) {
60932 var pts = eventData.points || [];
60933
60934 for(i = 0; i < searchTraces.length; i++) {
60935 trace = searchTraces[i].cd[0].trace;
60936 trace._input.selectedpoints = trace._fullInput.selectedpoints = [];
60937 if(trace._fullInput !== trace) trace.selectedpoints = [];
60938 }
60939
60940 for(i = 0; i < pts.length; i++) {
60941 var pt = pts[i];
60942 var data = pt.data;
60943 var fullData = pt.fullData;
60944
60945 if(pt.pointIndices) {
60946 [].push.apply(data.selectedpoints, pt.pointIndices);
60947 if(trace._fullInput !== trace) {
60948 [].push.apply(fullData.selectedpoints, pt.pointIndices);
60949 }
60950 } else {
60951 data.selectedpoints.push(pt.pointIndex);
60952 if(trace._fullInput !== trace) {
60953 fullData.selectedpoints.push(pt.pointIndex);
60954 }
60955 }
60956 }
60957 } else {
60958 for(i = 0; i < searchTraces.length; i++) {
60959 trace = searchTraces[i].cd[0].trace;
60960 delete trace.selectedpoints;
60961 delete trace._input.selectedpoints;
60962 if(trace._fullInput !== trace) {
60963 delete trace._fullInput.selectedpoints;
60964 }
60965 }
60966 }
60967
60968 var hasRegl = false;
60969
60970 for(i = 0; i < searchTraces.length; i++) {
60971 searchInfo = searchTraces[i];
60972 cd = searchInfo.cd;
60973 trace = cd[0].trace;
60974
60975 if(Registry.traceIs(trace, 'regl')) {
60976 hasRegl = true;
60977 }
60978
60979 var _module = searchInfo._module;
60980 var fn = _module.styleOnSelect || _module.style;
60981 if(fn) {
60982 fn(gd, cd, cd[0].node3);
60983 if(cd[0].nodeRangePlot3) fn(gd, cd, cd[0].nodeRangePlot3);
60984 }
60985 }
60986
60987 if(hasRegl) {
60988 clearGlCanvases(gd);
60989 redrawReglTraces(gd);
60990 }
60991}
60992
60993function mergePolygons(list, poly, subtract) {
60994 var res;
60995
60996 if(subtract) {
60997 res = polybool.difference({
60998 regions: list,
60999 inverted: false
61000 }, {
61001 regions: [poly],
61002 inverted: false
61003 });
61004
61005 return res.regions;
61006 }
61007
61008 res = polybool.union({
61009 regions: list,
61010 inverted: false
61011 }, {
61012 regions: [poly],
61013 inverted: false
61014 });
61015
61016 return res.regions;
61017}
61018
61019function fillSelectionItem(selection, searchInfo) {
61020 if(Array.isArray(selection)) {
61021 var cd = searchInfo.cd;
61022 var trace = searchInfo.cd[0].trace;
61023
61024 for(var i = 0; i < selection.length; i++) {
61025 selection[i] = makeEventData(selection[i], trace, cd);
61026 }
61027 }
61028
61029 return selection;
61030}
61031
61032function convertPoly(polygonsIn, isOpenMode) { // add M and L command to draft positions
61033 var polygonsOut = [];
61034 for(var i = 0; i < polygonsIn.length; i++) {
61035 polygonsOut[i] = [];
61036 for(var j = 0; j < polygonsIn[i].length; j++) {
61037 polygonsOut[i][j] = [];
61038 polygonsOut[i][j][0] = j ? 'L' : 'M';
61039 for(var k = 0; k < polygonsIn[i][j].length; k++) {
61040 polygonsOut[i][j].push(
61041 polygonsIn[i][j][k]
61042 );
61043 }
61044 }
61045
61046 if(!isOpenMode) {
61047 polygonsOut[i].push([
61048 'Z',
61049 polygonsOut[i][0][1], // initial x
61050 polygonsOut[i][0][2] // initial y
61051 ]);
61052 }
61053 }
61054
61055 return polygonsOut;
61056}
61057
61058module.exports = {
61059 prepSelect: prepSelect,
61060 clearSelect: clearSelect,
61061 clearSelectionsCache: clearSelectionsCache,
61062 selectOnClick: selectOnClick
61063};
61064
61065},{"../../components/color":52,"../../components/dragelement/helpers":70,"../../components/drawing":74,"../../components/fx":92,"../../components/fx/helpers":88,"../../components/shapes/draw_newshape/display_outlines":137,"../../components/shapes/draw_newshape/helpers":138,"../../components/shapes/draw_newshape/newshapes":139,"../../lib":178,"../../lib/clear_gl_canvases":167,"../../lib/polygon":190,"../../lib/throttle":200,"../../plot_api/subroutines":213,"../../registry":269,"./axis_ids":225,"./constants":228,"./handle_outline":232,"./helpers":233,"polybooljs":26}],242:[function(_dereq_,module,exports){
61066/**
61067* Copyright 2012-2020, Plotly, Inc.
61068* All rights reserved.
61069*
61070* This source code is licensed under the MIT license found in the
61071* LICENSE file in the root directory of this source tree.
61072*/
61073
61074'use strict';
61075
61076var d3 = _dereq_('d3');
61077var isNumeric = _dereq_('fast-isnumeric');
61078
61079var Lib = _dereq_('../../lib');
61080var cleanNumber = Lib.cleanNumber;
61081var ms2DateTime = Lib.ms2DateTime;
61082var dateTime2ms = Lib.dateTime2ms;
61083var ensureNumber = Lib.ensureNumber;
61084var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
61085
61086var numConstants = _dereq_('../../constants/numerical');
61087var FP_SAFE = numConstants.FP_SAFE;
61088var BADNUM = numConstants.BADNUM;
61089var LOG_CLIP = numConstants.LOG_CLIP;
61090var ONEDAY = numConstants.ONEDAY;
61091var ONEHOUR = numConstants.ONEHOUR;
61092var ONEMIN = numConstants.ONEMIN;
61093var ONESEC = numConstants.ONESEC;
61094
61095var axisIds = _dereq_('./axis_ids');
61096
61097var constants = _dereq_('./constants');
61098var HOUR_PATTERN = constants.HOUR_PATTERN;
61099var WEEKDAY_PATTERN = constants.WEEKDAY_PATTERN;
61100
61101function fromLog(v) {
61102 return Math.pow(10, v);
61103}
61104
61105function isValidCategory(v) {
61106 return v !== null && v !== undefined;
61107}
61108
61109/**
61110 * Define the conversion functions for an axis data is used in 5 ways:
61111 *
61112 * d: data, in whatever form it's provided
61113 * c: calcdata: turned into numbers, but not linearized
61114 * l: linearized - same as c except for log axes (and other nonlinear
61115 * mappings later?) this is used when we need to know if it's
61116 * *possible* to show some data on this axis, without caring about
61117 * the current range
61118 * p: pixel value - mapped to the screen with current size and zoom
61119 * r: ranges, tick0, and annotation positions match one of the above
61120 * but are handled differently for different types:
61121 * - linear and date: data format (d)
61122 * - category: calcdata format (c), and will stay that way because
61123 * the data format has no continuous mapping
61124 * - log: linearized (l) format
61125 * TODO: in v2.0 we plan to change it to data format. At that point
61126 * shapes will work the same way as ranges, tick0, and annotations
61127 * so they can use this conversion too.
61128 *
61129 * Creates/updates these conversion functions, and a few more utilities
61130 * like cleanRange, and makeCalcdata
61131 *
61132 * also clears the autotick constraints ._minDtick, ._forceTick0
61133 */
61134module.exports = function setConvert(ax, fullLayout) {
61135 fullLayout = fullLayout || {};
61136
61137 var axId = (ax._id || 'x');
61138 var axLetter = axId.charAt(0);
61139
61140 function toLog(v, clip) {
61141 if(v > 0) return Math.log(v) / Math.LN10;
61142
61143 else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
61144 // clip NaN (ie past negative infinity) to LOG_CLIP axis
61145 // length past the negative edge
61146 var r0 = ax.range[0];
61147 var r1 = ax.range[1];
61148 return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1));
61149 } else return BADNUM;
61150 }
61151
61152 /*
61153 * wrapped dateTime2ms that:
61154 * - accepts ms numbers for backward compatibility
61155 * - inserts a dummy arg so calendar is the 3rd arg (see notes below).
61156 * - defaults to ax.calendar
61157 */
61158 function dt2ms(v, _, calendar) {
61159 // NOTE: Changed this behavior: previously we took any numeric value
61160 // to be a ms, even if it was a string that could be a bare year.
61161 // Now we convert it as a date if at all possible, and only try
61162 // as (local) ms if that fails.
61163 var ms = dateTime2ms(v, calendar || ax.calendar);
61164 if(ms === BADNUM) {
61165 if(isNumeric(v)) {
61166 v = +v;
61167 // keep track of tenths of ms, that `new Date` will drop
61168 // same logic as in Lib.ms2DateTime
61169 var msecTenths = Math.floor(Lib.mod(v + 0.05, 1) * 10);
61170 var msRounded = Math.round(v - msecTenths / 10);
61171 ms = dateTime2ms(new Date(msRounded)) + msecTenths / 10;
61172 } else return BADNUM;
61173 }
61174 return ms;
61175 }
61176
61177 // wrapped ms2DateTime to insert default ax.calendar
61178 function ms2dt(v, r, calendar) {
61179 return ms2DateTime(v, r, calendar || ax.calendar);
61180 }
61181
61182 function getCategoryName(v) {
61183 return ax._categories[Math.round(v)];
61184 }
61185
61186 /*
61187 * setCategoryIndex: return the index of category v,
61188 * inserting it in the list if it's not already there
61189 *
61190 * this will enter the categories in the order it
61191 * encounters them, ie all the categories from the
61192 * first data set, then all the ones from the second
61193 * that aren't in the first etc.
61194 *
61195 * it is assumed that this function is being invoked in the
61196 * already sorted category order; otherwise there would be
61197 * a disconnect between the array and the index returned
61198 */
61199 function setCategoryIndex(v) {
61200 if(isValidCategory(v)) {
61201 if(ax._categoriesMap === undefined) {
61202 ax._categoriesMap = {};
61203 }
61204
61205 if(ax._categoriesMap[v] !== undefined) {
61206 return ax._categoriesMap[v];
61207 } else {
61208 ax._categories.push(typeof v === 'number' ? String(v) : v);
61209
61210 var curLength = ax._categories.length - 1;
61211 ax._categoriesMap[v] = curLength;
61212
61213 return curLength;
61214 }
61215 }
61216 return BADNUM;
61217 }
61218
61219 function setMultiCategoryIndex(arrayIn, len) {
61220 var arrayOut = new Array(len);
61221
61222 for(var i = 0; i < len; i++) {
61223 var v0 = (arrayIn[0] || [])[i];
61224 var v1 = (arrayIn[1] || [])[i];
61225 arrayOut[i] = getCategoryIndex([v0, v1]);
61226 }
61227
61228 return arrayOut;
61229 }
61230
61231 function getCategoryIndex(v) {
61232 if(ax._categoriesMap) {
61233 return ax._categoriesMap[v];
61234 }
61235 }
61236
61237 function getCategoryPosition(v) {
61238 // d2l/d2c variant that that won't add categories but will also
61239 // allow numbers to be mapped to the linearized axis positions
61240 var index = getCategoryIndex(v);
61241 if(index !== undefined) return index;
61242 if(isNumeric(v)) return +v;
61243 }
61244
61245 // include 2 fractional digits on pixel, for PDF zooming etc
61246 function _l2p(v, m, b) { return d3.round(b + m * v, 2); }
61247
61248 function _p2l(px, m, b) { return (px - b) / m; }
61249
61250 var l2p = function l2p(v) {
61251 if(!isNumeric(v)) return BADNUM;
61252 return _l2p(v, ax._m, ax._b);
61253 };
61254
61255 var p2l = function(px) {
61256 return _p2l(px, ax._m, ax._b);
61257 };
61258
61259 if(ax.rangebreaks) {
61260 var isY = axLetter === 'y';
61261
61262 l2p = function(v) {
61263 if(!isNumeric(v)) return BADNUM;
61264 var len = ax._rangebreaks.length;
61265 if(!len) return _l2p(v, ax._m, ax._b);
61266
61267 var flip = isY;
61268 if(ax.range[0] > ax.range[1]) flip = !flip;
61269 var signAx = flip ? -1 : 1;
61270 var pos = signAx * v;
61271
61272 var q = 0;
61273 for(var i = 0; i < len; i++) {
61274 var min = signAx * ax._rangebreaks[i].min;
61275 var max = signAx * ax._rangebreaks[i].max;
61276
61277 if(pos < min) break;
61278 if(pos > max) q = i + 1;
61279 else {
61280 // when falls into break, pick 'closest' offset
61281 q = pos < (min + max) / 2 ? i : i + 1;
61282 break;
61283 }
61284 }
61285 var b2 = ax._B[q] || 0;
61286 if(!isFinite(b2)) return 0; // avoid NaN translate e.g. in positionLabels if one keep zooming exactly into a break
61287 return _l2p(v, ax._m2, b2);
61288 };
61289
61290 p2l = function(px) {
61291 var len = ax._rangebreaks.length;
61292 if(!len) return _p2l(px, ax._m, ax._b);
61293
61294 var q = 0;
61295 for(var i = 0; i < len; i++) {
61296 if(px < ax._rangebreaks[i].pmin) break;
61297 if(px > ax._rangebreaks[i].pmax) q = i + 1;
61298 }
61299 return _p2l(px, ax._m2, ax._B[q]);
61300 };
61301 }
61302
61303 // conversions among c/l/p are fairly simple - do them together for all axis types
61304 ax.c2l = (ax.type === 'log') ? toLog : ensureNumber;
61305 ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber;
61306
61307 ax.l2p = l2p;
61308 ax.p2l = p2l;
61309
61310 ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p;
61311 ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l;
61312
61313 /*
61314 * now type-specific conversions for **ALL** other combinations
61315 * they're all written out, instead of being combinations of each other, for
61316 * both clarity and speed.
61317 */
61318 if(['linear', '-'].indexOf(ax.type) !== -1) {
61319 // all are data vals, but d and r need cleaning
61320 ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber;
61321 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ensureNumber;
61322
61323 ax.d2p = ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
61324 ax.p2d = ax.p2r = p2l;
61325
61326 ax.cleanPos = ensureNumber;
61327 } else if(ax.type === 'log') {
61328 // d and c are data vals, r and l are logged (but d and r need cleaning)
61329 ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); };
61330 ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); };
61331
61332 ax.d2c = ax.r2l = cleanNumber;
61333 ax.c2d = ax.l2r = ensureNumber;
61334
61335 ax.c2r = toLog;
61336 ax.l2d = fromLog;
61337
61338 ax.d2p = function(v, clip) { return ax.l2p(ax.d2r(v, clip)); };
61339 ax.p2d = function(px) { return fromLog(p2l(px)); };
61340
61341 ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
61342 ax.p2r = p2l;
61343
61344 ax.cleanPos = ensureNumber;
61345 } else if(ax.type === 'date') {
61346 // r and d are date strings, l and c are ms
61347
61348 /*
61349 * Any of these functions with r and d on either side, calendar is the
61350 * **3rd** argument. log has reserved the second argument.
61351 *
61352 * Unless you need the special behavior of the second arg (ms2DateTime
61353 * uses this to limit precision, toLog uses true to clip negatives
61354 * to offscreen low rather than undefined), it's safe to pass 0.
61355 */
61356 ax.d2r = ax.r2d = Lib.identity;
61357
61358 ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms;
61359 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt;
61360
61361 ax.d2p = ax.r2p = function(v, _, calendar) { return ax.l2p(dt2ms(v, 0, calendar)); };
61362 ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); };
61363
61364 ax.cleanPos = function(v) { return Lib.cleanDate(v, BADNUM, ax.calendar); };
61365 } else if(ax.type === 'category') {
61366 // d is categories (string)
61367 // c and l are indices (numbers)
61368 // r is categories or numbers
61369
61370 ax.d2c = ax.d2l = setCategoryIndex;
61371 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
61372
61373 ax.d2r = ax.d2l_noadd = getCategoryPosition;
61374
61375 ax.r2c = function(v) {
61376 var index = getCategoryPosition(v);
61377 return index !== undefined ? index : ax.fraction2r(0.5);
61378 };
61379
61380 ax.l2r = ax.c2r = ensureNumber;
61381 ax.r2l = getCategoryPosition;
61382
61383 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
61384 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
61385 ax.r2p = ax.d2p;
61386 ax.p2r = p2l;
61387
61388 ax.cleanPos = function(v) {
61389 if(typeof v === 'string' && v !== '') return v;
61390 return ensureNumber(v);
61391 };
61392 } else if(ax.type === 'multicategory') {
61393 // N.B. multicategory axes don't define d2c and d2l,
61394 // as 'data-to-calcdata' conversion needs to take into
61395 // account all data array items as in ax.makeCalcdata.
61396
61397 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
61398 ax.d2r = ax.d2l_noadd = getCategoryPosition;
61399
61400 ax.r2c = function(v) {
61401 var index = getCategoryPosition(v);
61402 return index !== undefined ? index : ax.fraction2r(0.5);
61403 };
61404
61405 ax.r2c_just_indices = getCategoryIndex;
61406
61407 ax.l2r = ax.c2r = ensureNumber;
61408 ax.r2l = getCategoryPosition;
61409
61410 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
61411 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
61412 ax.r2p = ax.d2p;
61413 ax.p2r = p2l;
61414
61415 ax.cleanPos = function(v) {
61416 if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v;
61417 return ensureNumber(v);
61418 };
61419
61420 ax.setupMultiCategory = function(fullData) {
61421 var traceIndices = ax._traceIndices;
61422 var i, j;
61423
61424 var matchGroups = fullLayout._axisMatchGroups;
61425 if(matchGroups && matchGroups.length && ax._categories.length === 0) {
61426 for(i = 0; i < matchGroups.length; i++) {
61427 var group = matchGroups[i];
61428 if(group[axId]) {
61429 for(var axId2 in group) {
61430 if(axId2 !== axId) {
61431 var ax2 = fullLayout[axisIds.id2name(axId2)];
61432 traceIndices = traceIndices.concat(ax2._traceIndices);
61433 }
61434 }
61435 }
61436 }
61437 }
61438
61439 // [ [cnt, {$cat: index}], for 1,2 ]
61440 var seen = [[0, {}], [0, {}]];
61441 // [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ]
61442 var list = [];
61443
61444 for(i = 0; i < traceIndices.length; i++) {
61445 var trace = fullData[traceIndices[i]];
61446
61447 if(axLetter in trace) {
61448 var arrayIn = trace[axLetter];
61449 var len = trace._length || Lib.minRowLength(arrayIn);
61450
61451 if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) {
61452 for(j = 0; j < len; j++) {
61453 var v0 = arrayIn[0][j];
61454 var v1 = arrayIn[1][j];
61455
61456 if(isValidCategory(v0) && isValidCategory(v1)) {
61457 list.push([v0, v1]);
61458
61459 if(!(v0 in seen[0][1])) {
61460 seen[0][1][v0] = seen[0][0]++;
61461 }
61462 if(!(v1 in seen[1][1])) {
61463 seen[1][1][v1] = seen[1][0]++;
61464 }
61465 }
61466 }
61467 }
61468 }
61469 }
61470
61471 list.sort(function(a, b) {
61472 var ind0 = seen[0][1];
61473 var d = ind0[a[0]] - ind0[b[0]];
61474 if(d) return d;
61475
61476 var ind1 = seen[1][1];
61477 return ind1[a[1]] - ind1[b[1]];
61478 });
61479
61480 for(i = 0; i < list.length; i++) {
61481 setCategoryIndex(list[i]);
61482 }
61483 };
61484 }
61485
61486 // find the range value at the specified (linear) fraction of the axis
61487 ax.fraction2r = function(v) {
61488 var rl0 = ax.r2l(ax.range[0]);
61489 var rl1 = ax.r2l(ax.range[1]);
61490 return ax.l2r(rl0 + v * (rl1 - rl0));
61491 };
61492
61493 // find the fraction of the range at the specified range value
61494 ax.r2fraction = function(v) {
61495 var rl0 = ax.r2l(ax.range[0]);
61496 var rl1 = ax.r2l(ax.range[1]);
61497 return (ax.r2l(v) - rl0) / (rl1 - rl0);
61498 };
61499
61500 /*
61501 * cleanRange: make sure range is a couplet of valid & distinct values
61502 * keep numbers away from the limits of floating point numbers,
61503 * and dates away from the ends of our date system (+/- 9999 years)
61504 *
61505 * optional param rangeAttr: operate on a different attribute, like
61506 * ax._r, rather than ax.range
61507 */
61508 ax.cleanRange = function(rangeAttr, opts) {
61509 if(!opts) opts = {};
61510 if(!rangeAttr) rangeAttr = 'range';
61511
61512 var range = Lib.nestedProperty(ax, rangeAttr).get();
61513 var i, dflt;
61514
61515 if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
61516 else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
61517 else dflt = opts.dfltRange || constants.DFLTRANGEX;
61518
61519 // make sure we don't later mutate the defaults
61520 dflt = dflt.slice();
61521
61522 if(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') {
61523 dflt[0] = 0;
61524 }
61525
61526 if(!range || range.length !== 2) {
61527 Lib.nestedProperty(ax, rangeAttr).set(dflt);
61528 return;
61529 }
61530
61531 if(ax.type === 'date' && !ax.autorange) {
61532 // check if milliseconds or js date objects are provided for range
61533 // and convert to date strings
61534 range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar);
61535 range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar);
61536 }
61537
61538 for(i = 0; i < 2; i++) {
61539 if(ax.type === 'date') {
61540 if(!Lib.isDateTime(range[i], ax.calendar)) {
61541 ax[rangeAttr] = dflt;
61542 break;
61543 }
61544
61545 if(ax.r2l(range[0]) === ax.r2l(range[1])) {
61546 // split by +/- 1 second
61547 var linCenter = Lib.constrain(ax.r2l(range[0]),
61548 Lib.MIN_MS + 1000, Lib.MAX_MS - 1000);
61549 range[0] = ax.l2r(linCenter - 1000);
61550 range[1] = ax.l2r(linCenter + 1000);
61551 break;
61552 }
61553 } else {
61554 if(!isNumeric(range[i])) {
61555 if(isNumeric(range[1 - i])) {
61556 range[i] = range[1 - i] * (i ? 10 : 0.1);
61557 } else {
61558 ax[rangeAttr] = dflt;
61559 break;
61560 }
61561 }
61562
61563 if(range[i] < -FP_SAFE) range[i] = -FP_SAFE;
61564 else if(range[i] > FP_SAFE) range[i] = FP_SAFE;
61565
61566 if(range[0] === range[1]) {
61567 // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger
61568 var inc = Math.max(1, Math.abs(range[0] * 1e-6));
61569 range[0] -= inc;
61570 range[1] += inc;
61571 }
61572 }
61573 }
61574 };
61575
61576 // set scaling to pixels
61577 ax.setScale = function(usePrivateRange) {
61578 var gs = fullLayout._size;
61579
61580 // make sure we have a domain (pull it in from the axis
61581 // this one is overlaying if necessary)
61582 if(ax.overlaying) {
61583 var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying);
61584 ax.domain = ax2.domain;
61585 }
61586
61587 // While transitions are occurring, we get a double-transform
61588 // issue if we transform the drawn layer *and* use the new axis range to
61589 // draw the data. This allows us to construct setConvert using the pre-
61590 // interaction values of the range:
61591 var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range';
61592 var calendar = ax.calendar;
61593 ax.cleanRange(rangeAttr);
61594
61595 var rl0 = ax.r2l(ax[rangeAttr][0], calendar);
61596 var rl1 = ax.r2l(ax[rangeAttr][1], calendar);
61597
61598 var isY = axLetter === 'y';
61599 if(isY) {
61600 ax._offset = gs.t + (1 - ax.domain[1]) * gs.h;
61601 ax._length = gs.h * (ax.domain[1] - ax.domain[0]);
61602 ax._m = ax._length / (rl0 - rl1);
61603 ax._b = -ax._m * rl1;
61604 } else {
61605 ax._offset = gs.l + ax.domain[0] * gs.w;
61606 ax._length = gs.w * (ax.domain[1] - ax.domain[0]);
61607 ax._m = ax._length / (rl1 - rl0);
61608 ax._b = -ax._m * rl0;
61609 }
61610
61611 // set of "N" disjoint rangebreaks inside the range
61612 ax._rangebreaks = [];
61613 // length of these rangebreaks in value space - negative on reversed axes
61614 ax._lBreaks = 0;
61615 // l2p slope (same for all intervals)
61616 ax._m2 = 0;
61617 // set of l2p offsets (one for each of the (N+1) piecewise intervals)
61618 ax._B = [];
61619
61620 if(ax.rangebreaks) {
61621 var i, brk;
61622
61623 ax._rangebreaks = ax.locateBreaks(
61624 Math.min(rl0, rl1),
61625 Math.max(rl0, rl1)
61626 );
61627
61628 if(ax._rangebreaks.length) {
61629 for(i = 0; i < ax._rangebreaks.length; i++) {
61630 brk = ax._rangebreaks[i];
61631 ax._lBreaks += Math.abs(brk.max - brk.min);
61632 }
61633
61634 var flip = isY;
61635 if(rl0 > rl1) flip = !flip;
61636 if(flip) ax._rangebreaks.reverse();
61637 var sign = flip ? -1 : 1;
61638
61639 ax._m2 = sign * ax._length / (Math.abs(rl1 - rl0) - ax._lBreaks);
61640 ax._B.push(-ax._m2 * (isY ? rl1 : rl0));
61641 for(i = 0; i < ax._rangebreaks.length; i++) {
61642 brk = ax._rangebreaks[i];
61643 ax._B.push(
61644 ax._B[ax._B.length - 1] -
61645 sign * ax._m2 * (brk.max - brk.min)
61646 );
61647 }
61648
61649 // fill pixel (i.e. 'p') min/max here,
61650 // to not have to loop through the _rangebreaks twice during `p2l`
61651 for(i = 0; i < ax._rangebreaks.length; i++) {
61652 brk = ax._rangebreaks[i];
61653 brk.pmin = l2p(brk.min);
61654 brk.pmax = l2p(brk.max);
61655 }
61656 }
61657 }
61658
61659 if(!isFinite(ax._m) || !isFinite(ax._b) || ax._length < 0) {
61660 fullLayout._replotting = false;
61661 throw new Error('Something went wrong with axis scaling');
61662 }
61663 };
61664
61665 ax.maskBreaks = function(v) {
61666 var rangebreaksIn = ax.rangebreaks || [];
61667 var bnds, b0, b1, vb, vDate;
61668
61669 for(var i = 0; i < rangebreaksIn.length; i++) {
61670 var brk = rangebreaksIn[i];
61671
61672 if(brk.enabled) {
61673 if(brk.bounds) {
61674 var pattern = brk.pattern;
61675 bnds = Lib.simpleMap(brk.bounds, pattern ?
61676 cleanNumber :
61677 ax.d2c // case of pattern: ''
61678 );
61679 b0 = bnds[0];
61680 b1 = bnds[1];
61681
61682 switch(pattern) {
61683 case WEEKDAY_PATTERN:
61684 vDate = new Date(v);
61685 vb = vDate.getUTCDay();
61686
61687 if(b0 > b1) {
61688 b1 += 7;
61689 if(vb < b0) vb += 7;
61690 }
61691
61692 break;
61693 case HOUR_PATTERN:
61694 vDate = new Date(v);
61695 var hours = vDate.getUTCHours();
61696 var minutes = vDate.getUTCMinutes();
61697 var seconds = vDate.getUTCSeconds();
61698 var milliseconds = vDate.getUTCMilliseconds();
61699
61700 vb = hours + (
61701 minutes / 60 +
61702 seconds / 3600 +
61703 milliseconds / 3600000
61704 );
61705
61706 if(b0 > b1) {
61707 b1 += 24;
61708 if(vb < b0) vb += 24;
61709 }
61710
61711 break;
61712 case '':
61713 // N.B. should work on date axes as well!
61714 // e.g. { bounds: ['2020-01-04', '2020-01-05 23:59'] }
61715 // TODO should work with reversed-range axes
61716 vb = v;
61717 break;
61718 }
61719
61720 if(vb >= b0 && vb < b1) return BADNUM;
61721 } else {
61722 var vals = Lib.simpleMap(brk.values, ax.d2c).sort(Lib.sorterAsc);
61723 for(var j = 0; j < vals.length; j++) {
61724 b0 = vals[j];
61725 b1 = b0 + brk.dvalue;
61726 if(v >= b0 && v < b1) return BADNUM;
61727 }
61728 }
61729 }
61730 }
61731 return v;
61732 };
61733
61734 ax.locateBreaks = function(r0, r1) {
61735 var i, bnds, b0, b1;
61736
61737 var rangebreaksOut = [];
61738 if(!ax.rangebreaks) return rangebreaksOut;
61739
61740 var rangebreaksIn = ax.rangebreaks.slice().sort(function(a, b) {
61741 if(a.pattern === WEEKDAY_PATTERN && b.pattern === HOUR_PATTERN) return -1;
61742 if(b.pattern === WEEKDAY_PATTERN && a.pattern === HOUR_PATTERN) return 1;
61743 return 0;
61744 });
61745
61746 var addBreak = function(min, max) {
61747 min = Lib.constrain(min, r0, r1);
61748 max = Lib.constrain(max, r0, r1);
61749 if(min === max) return;
61750
61751 var isNewBreak = true;
61752 for(var j = 0; j < rangebreaksOut.length; j++) {
61753 var brkj = rangebreaksOut[j];
61754 if(min > brkj.max || max < brkj.min) {
61755 // potentially a new break
61756 } else {
61757 if(min < brkj.min) {
61758 brkj.min = min;
61759 }
61760 if(max > brkj.max) {
61761 brkj.max = max;
61762 }
61763 isNewBreak = false;
61764 }
61765 }
61766 if(isNewBreak) {
61767 rangebreaksOut.push({min: min, max: max});
61768 }
61769 };
61770
61771 for(i = 0; i < rangebreaksIn.length; i++) {
61772 var brk = rangebreaksIn[i];
61773
61774 if(brk.enabled) {
61775 if(brk.bounds) {
61776 var t0 = r0;
61777 var t1 = r1;
61778 if(brk.pattern) {
61779 // to remove decimal (most often found in auto ranges)
61780 t0 = Math.floor(t0);
61781 }
61782
61783 bnds = Lib.simpleMap(brk.bounds, brk.pattern ? cleanNumber : ax.r2l);
61784 b0 = bnds[0];
61785 b1 = bnds[1];
61786
61787 // r0 value as date
61788 var t0Date = new Date(t0);
61789 // r0 value for break pattern
61790 var bndDelta;
61791 // step in ms between rangebreaks
61792 var step;
61793
61794 switch(brk.pattern) {
61795 case WEEKDAY_PATTERN:
61796 step = 7 * ONEDAY;
61797
61798 bndDelta = (
61799 (b1 < b0 ? 7 : 0) +
61800 (b1 - b0)
61801 ) * ONEDAY;
61802
61803 t0 += b0 * ONEDAY - (
61804 t0Date.getUTCDay() * ONEDAY +
61805 t0Date.getUTCHours() * ONEHOUR +
61806 t0Date.getUTCMinutes() * ONEMIN +
61807 t0Date.getUTCSeconds() * ONESEC +
61808 t0Date.getUTCMilliseconds()
61809 );
61810 break;
61811 case HOUR_PATTERN:
61812 step = ONEDAY;
61813
61814 bndDelta = (
61815 (b1 < b0 ? 24 : 0) +
61816 (b1 - b0)
61817 ) * ONEHOUR;
61818
61819 t0 += b0 * ONEHOUR - (
61820 t0Date.getUTCHours() * ONEHOUR +
61821 t0Date.getUTCMinutes() * ONEMIN +
61822 t0Date.getUTCSeconds() * ONESEC +
61823 t0Date.getUTCMilliseconds()
61824 );
61825 break;
61826 default:
61827 t0 = Math.min(bnds[0], bnds[1]);
61828 t1 = Math.max(bnds[0], bnds[1]);
61829 step = t1 - t0;
61830 bndDelta = step;
61831 }
61832
61833 for(var t = t0; t < t1; t += step) {
61834 addBreak(t, t + bndDelta);
61835 }
61836 } else {
61837 var vals = Lib.simpleMap(brk.values, ax.d2c);
61838 for(var j = 0; j < vals.length; j++) {
61839 b0 = vals[j];
61840 b1 = b0 + brk.dvalue;
61841 addBreak(b0, b1);
61842 }
61843 }
61844 }
61845 }
61846
61847 rangebreaksOut.sort(function(a, b) { return a.min - b.min; });
61848
61849 return rangebreaksOut;
61850 };
61851
61852 // makeCalcdata: takes an x or y array and converts it
61853 // to a position on the axis object "ax"
61854 // inputs:
61855 // trace - a data object from gd.data
61856 // axLetter - a string, either 'x' or 'y', for which item
61857 // to convert (TODO: is this now always the same as
61858 // the first letter of ax._id?)
61859 // in case the expected data isn't there, make a list of
61860 // integers based on the opposite data
61861 ax.makeCalcdata = function(trace, axLetter) {
61862 var arrayIn, arrayOut, i, len;
61863
61864 var axType = ax.type;
61865 var cal = axType === 'date' && trace[axLetter + 'calendar'];
61866
61867 if(axLetter in trace) {
61868 arrayIn = trace[axLetter];
61869 len = trace._length || Lib.minRowLength(arrayIn);
61870
61871 if(Lib.isTypedArray(arrayIn) && (axType === 'linear' || axType === 'log')) {
61872 if(len === arrayIn.length) {
61873 return arrayIn;
61874 } else if(arrayIn.subarray) {
61875 return arrayIn.subarray(0, len);
61876 }
61877 }
61878
61879 if(axType === 'multicategory') {
61880 return setMultiCategoryIndex(arrayIn, len);
61881 }
61882
61883 arrayOut = new Array(len);
61884 for(i = 0; i < len; i++) {
61885 arrayOut[i] = ax.d2c(arrayIn[i], 0, cal);
61886 }
61887 } else {
61888 var v0 = ((axLetter + '0') in trace) ? ax.d2c(trace[axLetter + '0'], 0, cal) : 0;
61889 var dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1;
61890
61891 // the opposing data, for size if we have x and dx etc
61892 arrayIn = trace[{x: 'y', y: 'x'}[axLetter]];
61893 len = trace._length || arrayIn.length;
61894 arrayOut = new Array(len);
61895
61896 for(i = 0; i < len; i++) {
61897 arrayOut[i] = v0 + i * dv;
61898 }
61899 }
61900
61901 // mask (i.e. set to BADNUM) coords that fall inside rangebreaks
61902 if(ax.rangebreaks) {
61903 for(i = 0; i < len; i++) {
61904 arrayOut[i] = ax.maskBreaks(arrayOut[i]);
61905 }
61906 }
61907
61908 return arrayOut;
61909 };
61910
61911 ax.isValidRange = function(range) {
61912 return (
61913 Array.isArray(range) &&
61914 range.length === 2 &&
61915 isNumeric(ax.r2l(range[0])) &&
61916 isNumeric(ax.r2l(range[1]))
61917 );
61918 };
61919
61920 ax.isPtWithinRange = function(d, calendar) {
61921 var coord = ax.c2l(d[axLetter], null, calendar);
61922 var r0 = ax.r2l(ax.range[0]);
61923 var r1 = ax.r2l(ax.range[1]);
61924
61925 if(r0 < r1) {
61926 return r0 <= coord && coord <= r1;
61927 } else {
61928 // Reversed axis case.
61929 return r1 <= coord && coord <= r0;
61930 }
61931 };
61932
61933 // should skip if not category nor multicategory
61934 ax.clearCalc = function() {
61935 var emptyCategories = function() {
61936 ax._categories = [];
61937 ax._categoriesMap = {};
61938 };
61939
61940 var matchGroups = fullLayout._axisMatchGroups;
61941
61942 if(matchGroups && matchGroups.length) {
61943 var found = false;
61944
61945 for(var i = 0; i < matchGroups.length; i++) {
61946 var group = matchGroups[i];
61947
61948 if(group[axId]) {
61949 found = true;
61950 var categories = null;
61951 var categoriesMap = null;
61952
61953 for(var axId2 in group) {
61954 var ax2 = fullLayout[axisIds.id2name(axId2)];
61955 if(ax2._categories) {
61956 categories = ax2._categories;
61957 categoriesMap = ax2._categoriesMap;
61958 break;
61959 }
61960 }
61961
61962 if(categories && categoriesMap) {
61963 ax._categories = categories;
61964 ax._categoriesMap = categoriesMap;
61965 } else {
61966 emptyCategories();
61967 }
61968 break;
61969 }
61970 }
61971 if(!found) emptyCategories();
61972 } else {
61973 emptyCategories();
61974 }
61975
61976 if(ax._initialCategories) {
61977 for(var j = 0; j < ax._initialCategories.length; j++) {
61978 setCategoryIndex(ax._initialCategories[j]);
61979 }
61980 }
61981 };
61982
61983 // sort the axis (and all the matching ones) by _initialCategories
61984 // returns the indices of the traces affected by the reordering
61985 ax.sortByInitialCategories = function() {
61986 var affectedTraces = [];
61987 var emptyCategories = function() {
61988 ax._categories = [];
61989 ax._categoriesMap = {};
61990 };
61991
61992 emptyCategories();
61993
61994 if(ax._initialCategories) {
61995 for(var j = 0; j < ax._initialCategories.length; j++) {
61996 setCategoryIndex(ax._initialCategories[j]);
61997 }
61998 }
61999
62000 affectedTraces = affectedTraces.concat(ax._traceIndices);
62001
62002 // Propagate to matching axes
62003 var group = ax._matchGroup;
62004 for(var axId2 in group) {
62005 if(axId === axId2) continue;
62006 var ax2 = fullLayout[axisIds.id2name(axId2)];
62007 ax2._categories = ax._categories;
62008 ax2._categoriesMap = ax._categoriesMap;
62009 affectedTraces = affectedTraces.concat(ax2._traceIndices);
62010 }
62011 return affectedTraces;
62012 };
62013
62014 // Propagate localization into the axis so that
62015 // methods in Axes can use it w/o having to pass fullLayout
62016 // Default (non-d3) number formatting uses separators directly
62017 // dates and d3-formatted numbers use the d3 locale
62018 // Fall back on default format for dummy axes that don't care about formatting
62019 var locale = fullLayout._d3locale;
62020 if(ax.type === 'date') {
62021 ax._dateFormat = locale ? locale.timeFormat.utc : d3.time.format.utc;
62022 ax._extraFormat = fullLayout._extraFormat;
62023 }
62024 // occasionally we need _numFormat to pass through
62025 // even though it won't be needed by this axis
62026 ax._separators = fullLayout.separators;
62027 ax._numFormat = locale ? locale.numberFormat : d3.format;
62028
62029 // and for bar charts and box plots: reset forced minimum tick spacing
62030 delete ax._minDtick;
62031 delete ax._forceTick0;
62032};
62033
62034},{"../../constants/numerical":158,"../../lib":178,"./axis_ids":225,"./constants":228,"d3":16,"fast-isnumeric":18}],243:[function(_dereq_,module,exports){
62035/**
62036* Copyright 2012-2020, Plotly, Inc.
62037* All rights reserved.
62038*
62039* This source code is licensed under the MIT license found in the
62040* LICENSE file in the root directory of this source tree.
62041*/
62042
62043
62044'use strict';
62045
62046var Lib = _dereq_('../../lib');
62047var layoutAttributes = _dereq_('./layout_attributes');
62048var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
62049
62050module.exports = function handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, config) {
62051 if(!config || config.pass === 1) {
62052 handlePrefixSuffix(containerIn, containerOut, coerce, axType, options);
62053 }
62054
62055 if(!config || config.pass === 2) {
62056 handleOtherDefaults(containerIn, containerOut, coerce, axType, options);
62057 }
62058};
62059
62060function handlePrefixSuffix(containerIn, containerOut, coerce, axType, options) {
62061 var showAttrDflt = getShowAttrDflt(containerIn);
62062
62063 var tickPrefix = coerce('tickprefix');
62064 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
62065
62066 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
62067 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
62068}
62069
62070function handleOtherDefaults(containerIn, containerOut, coerce, axType, options) {
62071 var showAttrDflt = getShowAttrDflt(containerIn);
62072
62073 var tickPrefix = coerce('tickprefix');
62074 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
62075
62076 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
62077 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
62078
62079 var showTickLabels = coerce('showticklabels');
62080 if(showTickLabels) {
62081 var font = options.font || {};
62082 var contColor = containerOut.color;
62083 // as with titlefont.color, inherit axis.color only if one was
62084 // explicitly provided
62085 var dfltFontColor = (contColor && contColor !== layoutAttributes.color.dflt) ?
62086 contColor : font.color;
62087 Lib.coerceFont(coerce, 'tickfont', {
62088 family: font.family,
62089 size: font.size,
62090 color: dfltFontColor
62091 });
62092 coerce('tickangle');
62093
62094 if(axType !== 'category') {
62095 var tickFormat = coerce('tickformat');
62096
62097 handleArrayContainerDefaults(containerIn, containerOut, {
62098 name: 'tickformatstops',
62099 inclusionAttr: 'enabled',
62100 handleItemDefaults: tickformatstopDefaults
62101 });
62102 if(!containerOut.tickformatstops.length) {
62103 delete containerOut.tickformatstops;
62104 }
62105
62106 if(!tickFormat && axType !== 'date') {
62107 coerce('showexponent', showAttrDflt);
62108 coerce('exponentformat');
62109 coerce('separatethousands');
62110 }
62111 }
62112 }
62113}
62114
62115/*
62116 * Attributes 'showexponent', 'showtickprefix' and 'showticksuffix'
62117 * share values.
62118 *
62119 * If only 1 attribute is set,
62120 * the remaining attributes inherit that value.
62121 *
62122 * If 2 attributes are set to the same value,
62123 * the remaining attribute inherits that value.
62124 *
62125 * If 2 attributes are set to different values,
62126 * the remaining is set to its dflt value.
62127 *
62128 */
62129function getShowAttrDflt(containerIn) {
62130 var showAttrsAll = ['showexponent', 'showtickprefix', 'showticksuffix'];
62131 var showAttrs = showAttrsAll.filter(function(a) {
62132 return containerIn[a] !== undefined;
62133 });
62134 var sameVal = function(a) {
62135 return containerIn[a] === containerIn[showAttrs[0]];
62136 };
62137
62138 if(showAttrs.every(sameVal) || showAttrs.length === 1) {
62139 return containerIn[showAttrs[0]];
62140 }
62141}
62142
62143function tickformatstopDefaults(valueIn, valueOut) {
62144 function coerce(attr, dflt) {
62145 return Lib.coerce(valueIn, valueOut, layoutAttributes.tickformatstops, attr, dflt);
62146 }
62147
62148 var enabled = coerce('enabled');
62149 if(enabled) {
62150 coerce('dtickrange');
62151 coerce('value');
62152 }
62153}
62154
62155},{"../../lib":178,"../array_container_defaults":218,"./layout_attributes":236}],244:[function(_dereq_,module,exports){
62156/**
62157* Copyright 2012-2020, Plotly, Inc.
62158* All rights reserved.
62159*
62160* This source code is licensed under the MIT license found in the
62161* LICENSE file in the root directory of this source tree.
62162*/
62163
62164
62165'use strict';
62166
62167var Lib = _dereq_('../../lib');
62168
62169var layoutAttributes = _dereq_('./layout_attributes');
62170
62171
62172/**
62173 * options: inherits outerTicks from axes.handleAxisDefaults
62174 */
62175module.exports = function handleTickDefaults(containerIn, containerOut, coerce, options) {
62176 var tickLen = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'ticklen');
62177 var tickWidth = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickwidth');
62178 var tickColor = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickcolor', containerOut.color);
62179 var showTicks = coerce('ticks', (options.outerTicks || tickLen || tickWidth || tickColor) ? 'outside' : '');
62180
62181 if(!showTicks) {
62182 delete containerOut.ticklen;
62183 delete containerOut.tickwidth;
62184 delete containerOut.tickcolor;
62185 }
62186};
62187
62188},{"../../lib":178,"./layout_attributes":236}],245:[function(_dereq_,module,exports){
62189/**
62190* Copyright 2012-2020, Plotly, Inc.
62191* All rights reserved.
62192*
62193* This source code is licensed under the MIT license found in the
62194* LICENSE file in the root directory of this source tree.
62195*/
62196
62197'use strict';
62198
62199var cleanTicks = _dereq_('./clean_ticks');
62200
62201module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) {
62202 function readInput(attr) {
62203 var v = containerIn[attr];
62204 return (
62205 v !== undefined
62206 ) ? v : (containerOut._template || {})[attr];
62207 }
62208
62209 var _tick0 = readInput('tick0');
62210 var _dtick = readInput('dtick');
62211 var _tickvals = readInput('tickvals');
62212 var _tickmode = readInput('tickmode');
62213 var tickmode;
62214
62215 if(_tickmode === 'array' &&
62216 (axType === 'log' || axType === 'date')) {
62217 tickmode = containerOut.tickmode = 'auto';
62218 } else {
62219 var tickmodeDefault = Array.isArray(_tickvals) ? 'array' :
62220 _dtick ? 'linear' :
62221 'auto';
62222 tickmode = coerce('tickmode', tickmodeDefault);
62223 }
62224
62225 if(tickmode === 'auto') coerce('nticks');
62226 else if(tickmode === 'linear') {
62227 // dtick is usually a positive number, but there are some
62228 // special strings available for log or date axes
62229 // tick0 also has special logic
62230 var dtick = containerOut.dtick = cleanTicks.dtick(
62231 _dtick, axType);
62232 containerOut.tick0 = cleanTicks.tick0(
62233 _tick0, axType, containerOut.calendar, dtick);
62234 } else if(axType !== 'multicategory') {
62235 var tickvals = coerce('tickvals');
62236 if(tickvals === undefined) containerOut.tickmode = 'auto';
62237 else coerce('ticktext');
62238 }
62239};
62240
62241},{"./clean_ticks":227}],246:[function(_dereq_,module,exports){
62242/**
62243* Copyright 2012-2020, Plotly, Inc.
62244* All rights reserved.
62245*
62246* This source code is licensed under the MIT license found in the
62247* LICENSE file in the root directory of this source tree.
62248*/
62249
62250'use strict';
62251
62252var d3 = _dereq_('d3');
62253
62254var Registry = _dereq_('../../registry');
62255var Lib = _dereq_('../../lib');
62256var Drawing = _dereq_('../../components/drawing');
62257var Axes = _dereq_('./axes');
62258
62259/**
62260 * transitionAxes
62261 *
62262 * transition axes from one set of ranges to another, using a svg
62263 * transformations, similar to during panning.
62264 *
62265 * @param {DOM element | object} gd
62266 * @param {array} edits : array of 'edits', each item with
62267 * - plotinfo {object} subplot object
62268 * - xr0 {array} initial x-range
62269 * - xr1 {array} end x-range
62270 * - yr0 {array} initial y-range
62271 * - yr1 {array} end y-range
62272 * @param {object} transitionOpts
62273 * @param {function} makeOnCompleteCallback
62274 */
62275module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) {
62276 var fullLayout = gd._fullLayout;
62277
62278 // special case for redraw:false Plotly.animate that relies on this
62279 // to update axis-referenced layout components
62280 if(edits.length === 0) {
62281 Axes.redrawComponents(gd);
62282 return;
62283 }
62284
62285 function unsetSubplotTransform(subplot) {
62286 var xa = subplot.xaxis;
62287 var ya = subplot.yaxis;
62288
62289 fullLayout._defs.select('#' + subplot.clipId + '> rect')
62290 .call(Drawing.setTranslate, 0, 0)
62291 .call(Drawing.setScale, 1, 1);
62292
62293 subplot.plot
62294 .call(Drawing.setTranslate, xa._offset, ya._offset)
62295 .call(Drawing.setScale, 1, 1);
62296
62297 var traceGroups = subplot.plot.selectAll('.scatterlayer .trace');
62298
62299 // This is specifically directed at scatter traces, applying an inverse
62300 // scale to individual points to counteract the scale of the trace
62301 // as a whole:
62302 traceGroups.selectAll('.point')
62303 .call(Drawing.setPointGroupScale, 1, 1);
62304 traceGroups.selectAll('.textpoint')
62305 .call(Drawing.setTextPointsScale, 1, 1);
62306 traceGroups
62307 .call(Drawing.hideOutsideRangePoints, subplot);
62308 }
62309
62310 function updateSubplot(edit, progress) {
62311 var plotinfo = edit.plotinfo;
62312 var xa = plotinfo.xaxis;
62313 var ya = plotinfo.yaxis;
62314 var xlen = xa._length;
62315 var ylen = ya._length;
62316 var editX = !!edit.xr1;
62317 var editY = !!edit.yr1;
62318 var viewBox = [];
62319
62320 if(editX) {
62321 var xr0 = Lib.simpleMap(edit.xr0, xa.r2l);
62322 var xr1 = Lib.simpleMap(edit.xr1, xa.r2l);
62323 var dx0 = xr0[1] - xr0[0];
62324 var dx1 = xr1[1] - xr1[0];
62325 viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen;
62326 viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0);
62327 xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]);
62328 xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]);
62329 } else {
62330 viewBox[0] = 0;
62331 viewBox[2] = xlen;
62332 }
62333
62334 if(editY) {
62335 var yr0 = Lib.simpleMap(edit.yr0, ya.r2l);
62336 var yr1 = Lib.simpleMap(edit.yr1, ya.r2l);
62337 var dy0 = yr0[1] - yr0[0];
62338 var dy1 = yr1[1] - yr1[0];
62339 viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen;
62340 viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0);
62341 ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]);
62342 ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]);
62343 } else {
62344 viewBox[1] = 0;
62345 viewBox[3] = ylen;
62346 }
62347
62348 Axes.drawOne(gd, xa, {skipTitle: true});
62349 Axes.drawOne(gd, ya, {skipTitle: true});
62350 Axes.redrawComponents(gd, [xa._id, ya._id]);
62351
62352 var xScaleFactor = editX ? xlen / viewBox[2] : 1;
62353 var yScaleFactor = editY ? ylen / viewBox[3] : 1;
62354 var clipDx = editX ? viewBox[0] : 0;
62355 var clipDy = editY ? viewBox[1] : 0;
62356 var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0;
62357 var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0;
62358 var plotDx = xa._offset - fracDx;
62359 var plotDy = ya._offset - fracDy;
62360
62361 plotinfo.clipRect
62362 .call(Drawing.setTranslate, clipDx, clipDy)
62363 .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor);
62364
62365 plotinfo.plot
62366 .call(Drawing.setTranslate, plotDx, plotDy)
62367 .call(Drawing.setScale, xScaleFactor, yScaleFactor);
62368
62369 // apply an inverse scale to individual points to counteract
62370 // the scale of the trace group.
62371 Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor);
62372 Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor);
62373 }
62374
62375 var onComplete;
62376 if(makeOnCompleteCallback) {
62377 // This module makes the choice whether or not it notifies Plotly.transition
62378 // about completion:
62379 onComplete = makeOnCompleteCallback();
62380 }
62381
62382 function transitionComplete() {
62383 var aobj = {};
62384
62385 for(var i = 0; i < edits.length; i++) {
62386 var edit = edits[i];
62387 var xa = edit.plotinfo.xaxis;
62388 var ya = edit.plotinfo.yaxis;
62389 if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice();
62390 if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice();
62391 }
62392
62393 // Signal that this transition has completed:
62394 onComplete && onComplete();
62395
62396 return Registry.call('relayout', gd, aobj).then(function() {
62397 for(var i = 0; i < edits.length; i++) {
62398 unsetSubplotTransform(edits[i].plotinfo);
62399 }
62400 });
62401 }
62402
62403 function transitionInterrupt() {
62404 var aobj = {};
62405
62406 for(var i = 0; i < edits.length; i++) {
62407 var edit = edits[i];
62408 var xa = edit.plotinfo.xaxis;
62409 var ya = edit.plotinfo.yaxis;
62410 if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice();
62411 if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice();
62412 }
62413
62414 return Registry.call('relayout', gd, aobj).then(function() {
62415 for(var i = 0; i < edits.length; i++) {
62416 unsetSubplotTransform(edits[i].plotinfo);
62417 }
62418 });
62419 }
62420
62421 var t1, t2, raf;
62422 var easeFn = d3.ease(transitionOpts.easing);
62423
62424 gd._transitionData._interruptCallbacks.push(function() {
62425 window.cancelAnimationFrame(raf);
62426 raf = null;
62427 return transitionInterrupt();
62428 });
62429
62430 function doFrame() {
62431 t2 = Date.now();
62432
62433 var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration);
62434 var progress = easeFn(tInterp);
62435
62436 for(var i = 0; i < edits.length; i++) {
62437 updateSubplot(edits[i], progress);
62438 }
62439
62440 if(t2 - t1 > transitionOpts.duration) {
62441 transitionComplete();
62442 raf = window.cancelAnimationFrame(doFrame);
62443 } else {
62444 raf = window.requestAnimationFrame(doFrame);
62445 }
62446 }
62447
62448 t1 = Date.now();
62449 raf = window.requestAnimationFrame(doFrame);
62450
62451 return Promise.resolve();
62452};
62453
62454},{"../../components/drawing":74,"../../lib":178,"../../registry":269,"./axes":222,"d3":16}],247:[function(_dereq_,module,exports){
62455/**
62456* Copyright 2012-2020, Plotly, Inc.
62457* All rights reserved.
62458*
62459* This source code is licensed under the MIT license found in the
62460* LICENSE file in the root directory of this source tree.
62461*/
62462
62463'use strict';
62464
62465var traceIs = _dereq_('../../registry').traceIs;
62466var autoType = _dereq_('./axis_autotype');
62467
62468/*
62469 * data: the plot data to use in choosing auto type
62470 * name: axis object name (ie 'xaxis') if one should be stored
62471 */
62472module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
62473 var axType = coerce('type', (options.splomStash || {}).type);
62474
62475 if(axType === '-') {
62476 setAutoType(containerOut, options.data);
62477
62478 if(containerOut.type === '-') {
62479 containerOut.type = 'linear';
62480 } else {
62481 // copy autoType back to input axis
62482 // note that if this object didn't exist
62483 // in the input layout, we have to put it in
62484 // this happens in the main supplyDefaults function
62485 containerIn.type = containerOut.type;
62486 }
62487 }
62488};
62489
62490function setAutoType(ax, data) {
62491 // new logic: let people specify any type they want,
62492 // only autotype if type is '-'
62493 if(ax.type !== '-') return;
62494
62495 var id = ax._id;
62496 var axLetter = id.charAt(0);
62497 var i;
62498
62499 // support 3d
62500 if(id.indexOf('scene') !== -1) id = axLetter;
62501
62502 var d0 = getFirstNonEmptyTrace(data, id, axLetter);
62503 if(!d0) return;
62504
62505 // first check for histograms, as the count direction
62506 // should always default to a linear axis
62507 if(d0.type === 'histogram' &&
62508 axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v']
62509 ) {
62510 ax.type = 'linear';
62511 return;
62512 }
62513
62514 var calAttr = axLetter + 'calendar';
62515 var calendar = d0[calAttr];
62516 var opts = {noMultiCategory: !traceIs(d0, 'cartesian') || traceIs(d0, 'noMultiCategory')};
62517
62518 // To not confuse 2D x/y used for per-box sample points for multicategory coordinates
62519 if(d0.type === 'box' && d0._hasPreCompStats &&
62520 axLetter === {h: 'x', v: 'y'}[d0.orientation || 'v']
62521 ) {
62522 opts.noMultiCategory = true;
62523 }
62524
62525 // check all boxes on this x axis to see
62526 // if they're dates, numbers, or categories
62527 if(isBoxWithoutPositionCoords(d0, axLetter)) {
62528 var posLetter = getBoxPosLetter(d0);
62529 var boxPositions = [];
62530
62531 for(i = 0; i < data.length; i++) {
62532 var trace = data[i];
62533 if(!traceIs(trace, 'box-violin') || (trace[axLetter + 'axis'] || axLetter) !== id) continue;
62534
62535 if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]);
62536 else if(trace.name !== undefined) boxPositions.push(trace.name);
62537 else boxPositions.push('text');
62538
62539 if(trace[calAttr] !== calendar) calendar = undefined;
62540 }
62541
62542 ax.type = autoType(boxPositions, calendar, opts);
62543 } else if(d0.type === 'splom') {
62544 var dimensions = d0.dimensions;
62545 var dim = dimensions[d0._axesDim[id]];
62546 if(dim.visible) ax.type = autoType(dim.values, calendar, opts);
62547 } else {
62548 ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar, opts);
62549 }
62550}
62551
62552function getFirstNonEmptyTrace(data, id, axLetter) {
62553 for(var i = 0; i < data.length; i++) {
62554 var trace = data[i];
62555
62556 if(trace.type === 'splom' &&
62557 trace._length > 0 &&
62558 (trace['_' + axLetter + 'axes'] || {})[id]
62559 ) {
62560 return trace;
62561 }
62562
62563 if((trace[axLetter + 'axis'] || axLetter) === id) {
62564 if(isBoxWithoutPositionCoords(trace, axLetter)) {
62565 return trace;
62566 } else if((trace[axLetter] || []).length || trace[axLetter + '0']) {
62567 return trace;
62568 }
62569 }
62570 }
62571}
62572
62573function getBoxPosLetter(trace) {
62574 return {v: 'x', h: 'y'}[trace.orientation || 'v'];
62575}
62576
62577function isBoxWithoutPositionCoords(trace, axLetter) {
62578 var posLetter = getBoxPosLetter(trace);
62579 var isBox = traceIs(trace, 'box-violin');
62580 var isCandlestick = traceIs(trace._fullInput || {}, 'candlestick');
62581
62582 return (
62583 isBox &&
62584 !isCandlestick &&
62585 axLetter === posLetter &&
62586 trace[posLetter] === undefined &&
62587 trace[posLetter + '0'] === undefined
62588 );
62589}
62590
62591},{"../../registry":269,"./axis_autotype":223}],248:[function(_dereq_,module,exports){
62592/**
62593* Copyright 2012-2020, Plotly, Inc.
62594* All rights reserved.
62595*
62596* This source code is licensed under the MIT license found in the
62597* LICENSE file in the root directory of this source tree.
62598*/
62599
62600'use strict';
62601
62602var Registry = _dereq_('../registry');
62603var Lib = _dereq_('../lib');
62604
62605/*
62606 * Create or update an observer. This function is designed to be
62607 * idempotent so that it can be called over and over as the component
62608 * updates, and will attach and detach listeners as needed.
62609 *
62610 * @param {optional object} container
62611 * An object on which the observer is stored. This is the mechanism
62612 * by which it is idempotent. If it already exists, another won't be
62613 * added. Each time it's called, the value lookup table is updated.
62614 * @param {array} commandList
62615 * An array of commands, following either `buttons` of `updatemenus`
62616 * or `steps` of `sliders`.
62617 * @param {function} onchange
62618 * A listener called when the value is changed. Receives data object
62619 * with information about the new state.
62620 */
62621exports.manageCommandObserver = function(gd, container, commandList, onchange) {
62622 var ret = {};
62623 var enabled = true;
62624
62625 if(container && container._commandObserver) {
62626 ret = container._commandObserver;
62627 }
62628
62629 if(!ret.cache) {
62630 ret.cache = {};
62631 }
62632
62633 // Either create or just recompute this:
62634 ret.lookupTable = {};
62635
62636 var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
62637
62638 if(container && container._commandObserver) {
62639 if(!binding) {
62640 // If container exists and there are no longer any bindings,
62641 // remove existing:
62642 if(container._commandObserver.remove) {
62643 container._commandObserver.remove();
62644 container._commandObserver = null;
62645 return ret;
62646 }
62647 } else {
62648 // If container exists and there *are* bindings, then the lookup
62649 // table should have been updated and check is already attached,
62650 // so there's nothing to be done:
62651 return ret;
62652 }
62653 }
62654
62655 // Determine whether there's anything to do for this binding:
62656
62657 if(binding) {
62658 // Build the cache:
62659 bindingValueHasChanged(gd, binding, ret.cache);
62660
62661 ret.check = function check() {
62662 if(!enabled) return;
62663
62664 var update = bindingValueHasChanged(gd, binding, ret.cache);
62665
62666 if(update.changed && onchange) {
62667 // Disable checks for the duration of this command in order to avoid
62668 // infinite loops:
62669 if(ret.lookupTable[update.value] !== undefined) {
62670 ret.disable();
62671 Promise.resolve(onchange({
62672 value: update.value,
62673 type: binding.type,
62674 prop: binding.prop,
62675 traces: binding.traces,
62676 index: ret.lookupTable[update.value]
62677 })).then(ret.enable, ret.enable);
62678 }
62679 }
62680
62681 return update.changed;
62682 };
62683
62684 var checkEvents = [
62685 'plotly_relayout',
62686 'plotly_redraw',
62687 'plotly_restyle',
62688 'plotly_update',
62689 'plotly_animatingframe',
62690 'plotly_afterplot'
62691 ];
62692
62693 for(var i = 0; i < checkEvents.length; i++) {
62694 gd._internalOn(checkEvents[i], ret.check);
62695 }
62696
62697 ret.remove = function() {
62698 for(var i = 0; i < checkEvents.length; i++) {
62699 gd._removeInternalListener(checkEvents[i], ret.check);
62700 }
62701 };
62702 } else {
62703 // TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
62704 // is a start
62705 Lib.log('Unable to automatically bind plot updates to API command');
62706
62707 ret.lookupTable = {};
62708 ret.remove = function() {};
62709 }
62710
62711 ret.disable = function disable() {
62712 enabled = false;
62713 };
62714
62715 ret.enable = function enable() {
62716 enabled = true;
62717 };
62718
62719 if(container) {
62720 container._commandObserver = ret;
62721 }
62722
62723 return ret;
62724};
62725
62726/*
62727 * This function checks to see if an array of objects containing
62728 * method and args properties is compatible with automatic two-way
62729 * binding. The criteria right now are that
62730 *
62731 * 1. multiple traces may be affected
62732 * 2. only one property may be affected
62733 * 3. the same property must be affected by all commands
62734 */
62735exports.hasSimpleAPICommandBindings = function(gd, commandList, bindingsByValue) {
62736 var i;
62737 var n = commandList.length;
62738
62739 var refBinding;
62740
62741 for(i = 0; i < n; i++) {
62742 var binding;
62743 var command = commandList[i];
62744 var method = command.method;
62745 var args = command.args;
62746
62747 if(!Array.isArray(args)) args = [];
62748
62749 // If any command has no method, refuse to bind:
62750 if(!method) {
62751 return false;
62752 }
62753 var bindings = exports.computeAPICommandBindings(gd, method, args);
62754
62755 // Right now, handle one and *only* one property being set:
62756 if(bindings.length !== 1) {
62757 return false;
62758 }
62759
62760 if(!refBinding) {
62761 refBinding = bindings[0];
62762 if(Array.isArray(refBinding.traces)) {
62763 refBinding.traces.sort();
62764 }
62765 } else {
62766 binding = bindings[0];
62767 if(binding.type !== refBinding.type) {
62768 return false;
62769 }
62770 if(binding.prop !== refBinding.prop) {
62771 return false;
62772 }
62773 if(Array.isArray(refBinding.traces)) {
62774 if(Array.isArray(binding.traces)) {
62775 binding.traces.sort();
62776 for(var j = 0; j < refBinding.traces.length; j++) {
62777 if(refBinding.traces[j] !== binding.traces[j]) {
62778 return false;
62779 }
62780 }
62781 } else {
62782 return false;
62783 }
62784 } else {
62785 if(binding.prop !== refBinding.prop) {
62786 return false;
62787 }
62788 }
62789 }
62790
62791 binding = bindings[0];
62792 var value = binding.value;
62793 if(Array.isArray(value)) {
62794 if(value.length === 1) {
62795 value = value[0];
62796 } else {
62797 return false;
62798 }
62799 }
62800 if(bindingsByValue) {
62801 bindingsByValue[value] = i;
62802 }
62803 }
62804
62805 return refBinding;
62806};
62807
62808function bindingValueHasChanged(gd, binding, cache) {
62809 var container, value, obj;
62810 var changed = false;
62811
62812 if(binding.type === 'data') {
62813 // If it's data, we need to get a trace. Based on the limited scope
62814 // of what we cover, we can just take the first trace from the list,
62815 // or otherwise just the first trace:
62816 container = gd._fullData[binding.traces !== null ? binding.traces[0] : 0];
62817 } else if(binding.type === 'layout') {
62818 container = gd._fullLayout;
62819 } else {
62820 return false;
62821 }
62822
62823 value = Lib.nestedProperty(container, binding.prop).get();
62824
62825 obj = cache[binding.type] = cache[binding.type] || {};
62826
62827 if(obj.hasOwnProperty(binding.prop)) {
62828 if(obj[binding.prop] !== value) {
62829 changed = true;
62830 }
62831 }
62832
62833 obj[binding.prop] = value;
62834
62835 return {
62836 changed: changed,
62837 value: value
62838 };
62839}
62840
62841/*
62842 * Execute an API command. There's really not much to this; it just provides
62843 * a common hook so that implementations don't need to be synchronized across
62844 * multiple components with the ability to invoke API commands.
62845 *
62846 * @param {string} method
62847 * The name of the plotly command to execute. Must be one of 'animate',
62848 * 'restyle', 'relayout', 'update'.
62849 * @param {array} args
62850 * A list of arguments passed to the API command
62851 */
62852exports.executeAPICommand = function(gd, method, args) {
62853 if(method === 'skip') return Promise.resolve();
62854
62855 var _method = Registry.apiMethodRegistry[method];
62856 var allArgs = [gd];
62857 if(!Array.isArray(args)) args = [];
62858
62859 for(var i = 0; i < args.length; i++) {
62860 allArgs.push(args[i]);
62861 }
62862
62863 return _method.apply(null, allArgs).catch(function(err) {
62864 Lib.warn('API call to Plotly.' + method + ' rejected.', err);
62865 return Promise.reject(err);
62866 });
62867};
62868
62869exports.computeAPICommandBindings = function(gd, method, args) {
62870 var bindings;
62871
62872 if(!Array.isArray(args)) args = [];
62873
62874 switch(method) {
62875 case 'restyle':
62876 bindings = computeDataBindings(gd, args);
62877 break;
62878 case 'relayout':
62879 bindings = computeLayoutBindings(gd, args);
62880 break;
62881 case 'update':
62882 bindings = computeDataBindings(gd, [args[0], args[2]])
62883 .concat(computeLayoutBindings(gd, [args[1]]));
62884 break;
62885 case 'animate':
62886 bindings = computeAnimateBindings(gd, args);
62887 break;
62888 default:
62889 // This is the case where intelligent logic about what affects
62890 // this command is not implemented. It causes no ill effects.
62891 // For example, addFrames simply won't bind to a control component.
62892 bindings = [];
62893 }
62894 return bindings;
62895};
62896
62897function computeAnimateBindings(gd, args) {
62898 // We'll assume that the only relevant modification an animation
62899 // makes that's meaningfully tracked is the frame:
62900 if(Array.isArray(args[0]) && args[0].length === 1 && ['string', 'number'].indexOf(typeof args[0][0]) !== -1) {
62901 return [{type: 'layout', prop: '_currentFrame', value: args[0][0].toString()}];
62902 } else {
62903 return [];
62904 }
62905}
62906
62907function computeLayoutBindings(gd, args) {
62908 var bindings = [];
62909
62910 var astr = args[0];
62911 var aobj = {};
62912 if(typeof astr === 'string') {
62913 aobj[astr] = args[1];
62914 } else if(Lib.isPlainObject(astr)) {
62915 aobj = astr;
62916 } else {
62917 return bindings;
62918 }
62919
62920 crawl(aobj, function(path, attrName, attr) {
62921 bindings.push({type: 'layout', prop: path, value: attr});
62922 }, '', 0);
62923
62924 return bindings;
62925}
62926
62927function computeDataBindings(gd, args) {
62928 var traces, astr, val, aobj;
62929 var bindings = [];
62930
62931 // Logic copied from Plotly.restyle:
62932 astr = args[0];
62933 val = args[1];
62934 traces = args[2];
62935 aobj = {};
62936 if(typeof astr === 'string') {
62937 aobj[astr] = val;
62938 } else if(Lib.isPlainObject(astr)) {
62939 // the 3-arg form
62940 aobj = astr;
62941
62942 if(traces === undefined) {
62943 traces = val;
62944 }
62945 } else {
62946 return bindings;
62947 }
62948
62949 if(traces === undefined) {
62950 // Explicitly assign this to null instead of undefined:
62951 traces = null;
62952 }
62953
62954 crawl(aobj, function(path, attrName, _attr) {
62955 var thisTraces;
62956 var attr;
62957
62958 if(Array.isArray(_attr)) {
62959 attr = _attr.slice();
62960
62961 var nAttr = Math.min(attr.length, gd.data.length);
62962 if(traces) {
62963 nAttr = Math.min(nAttr, traces.length);
62964 }
62965 thisTraces = [];
62966 for(var j = 0; j < nAttr; j++) {
62967 thisTraces[j] = traces ? traces[j] : j;
62968 }
62969 } else {
62970 attr = _attr;
62971 thisTraces = traces ? traces.slice() : null;
62972 }
62973
62974 // Convert [7] to just 7 when traces is null:
62975 if(thisTraces === null) {
62976 if(Array.isArray(attr)) {
62977 attr = attr[0];
62978 }
62979 } else if(Array.isArray(thisTraces)) {
62980 if(!Array.isArray(attr)) {
62981 var tmp = attr;
62982 attr = [];
62983 for(var i = 0; i < thisTraces.length; i++) {
62984 attr[i] = tmp;
62985 }
62986 }
62987 attr.length = Math.min(thisTraces.length, attr.length);
62988 }
62989
62990 bindings.push({
62991 type: 'data',
62992 prop: path,
62993 traces: thisTraces,
62994 value: attr
62995 });
62996 }, '', 0);
62997
62998 return bindings;
62999}
63000
63001function crawl(attrs, callback, path, depth) {
63002 Object.keys(attrs).forEach(function(attrName) {
63003 var attr = attrs[attrName];
63004
63005 if(attrName[0] === '_') return;
63006
63007 var thisPath = path + (depth > 0 ? '.' : '') + attrName;
63008
63009 if(Lib.isPlainObject(attr)) {
63010 crawl(attr, callback, thisPath, depth + 1);
63011 } else {
63012 // Only execute the callback on leaf nodes:
63013 callback(thisPath, attrName, attr);
63014 }
63015 });
63016}
63017
63018},{"../lib":178,"../registry":269}],249:[function(_dereq_,module,exports){
63019/**
63020* Copyright 2012-2020, Plotly, Inc.
63021* All rights reserved.
63022*
63023* This source code is licensed under the MIT license found in the
63024* LICENSE file in the root directory of this source tree.
63025*/
63026
63027'use strict';
63028
63029var extendFlat = _dereq_('../lib/extend').extendFlat;
63030
63031/**
63032 * Make a xy domain attribute group
63033 *
63034 * @param {object} opts
63035 * @param {string}
63036 * opts.name: name to be inserted in the default description
63037 * @param {boolean}
63038 * opts.trace: set to true for trace containers
63039 * @param {string}
63040 * opts.editType: editType for all pieces
63041 * @param {boolean}
63042 * opts.noGridCell: set to true to omit `row` and `column`
63043 *
63044 * @param {object} extra
63045 * @param {string}
63046 * extra.description: extra description. N.B we use
63047 * a separate extra container to make it compatible with
63048 * the compress_attributes transform.
63049 *
63050 * @return {object} attributes object containing {x,y} as specified
63051 */
63052exports.attributes = function(opts, extra) {
63053 opts = opts || {};
63054 extra = extra || {};
63055
63056 var base = {
63057 valType: 'info_array',
63058
63059 editType: opts.editType,
63060 items: [
63061 {valType: 'number', min: 0, max: 1, editType: opts.editType},
63062 {valType: 'number', min: 0, max: 1, editType: opts.editType}
63063 ],
63064 dflt: [0, 1]
63065 };
63066
63067 var namePart = opts.name ? opts.name + ' ' : '';
63068 var contPart = opts.trace ? 'trace ' : 'subplot ';
63069 var descPart = extra.description ? ' ' + extra.description : '';
63070
63071 var out = {
63072 x: extendFlat({}, base, {
63073
63074 }),
63075 y: extendFlat({}, base, {
63076
63077 }),
63078 editType: opts.editType
63079 };
63080
63081 if(!opts.noGridCell) {
63082 out.row = {
63083 valType: 'integer',
63084 min: 0,
63085 dflt: 0,
63086
63087 editType: opts.editType,
63088
63089 };
63090 out.column = {
63091 valType: 'integer',
63092 min: 0,
63093 dflt: 0,
63094
63095 editType: opts.editType,
63096
63097 };
63098 }
63099
63100 return out;
63101};
63102
63103exports.defaults = function(containerOut, layout, coerce, dfltDomains) {
63104 var dfltX = (dfltDomains && dfltDomains.x) || [0, 1];
63105 var dfltY = (dfltDomains && dfltDomains.y) || [0, 1];
63106
63107 var grid = layout.grid;
63108 if(grid) {
63109 var column = coerce('domain.column');
63110 if(column !== undefined) {
63111 if(column < grid.columns) dfltX = grid._domains.x[column];
63112 else delete containerOut.domain.column;
63113 }
63114
63115 var row = coerce('domain.row');
63116 if(row !== undefined) {
63117 if(row < grid.rows) dfltY = grid._domains.y[row];
63118 else delete containerOut.domain.row;
63119 }
63120 }
63121
63122 var x = coerce('domain.x', dfltX);
63123 var y = coerce('domain.y', dfltY);
63124
63125 // don't accept bad input data
63126 if(!(x[0] < x[1])) containerOut.domain.x = dfltX.slice();
63127 if(!(y[0] < y[1])) containerOut.domain.y = dfltY.slice();
63128};
63129
63130},{"../lib/extend":173}],250:[function(_dereq_,module,exports){
63131/**
63132* Copyright 2012-2020, Plotly, Inc.
63133* All rights reserved.
63134*
63135* This source code is licensed under the MIT license found in the
63136* LICENSE file in the root directory of this source tree.
63137*/
63138
63139'use strict';
63140
63141/*
63142 * make a font attribute group
63143 *
63144 * @param {object} opts
63145 * @param {string}
63146 * opts.description: where & how this font is used
63147 * @param {optional bool} arrayOk:
63148 * should each part (family, size, color) be arrayOk? default false.
63149 * @param {string} editType:
63150 * the editType for all pieces of this font
63151 * @param {optional string} colorEditType:
63152 * a separate editType just for color
63153 *
63154 * @return {object} attributes object containing {family, size, color} as specified
63155 */
63156module.exports = function(opts) {
63157 var editType = opts.editType;
63158 var colorEditType = opts.colorEditType;
63159 if(colorEditType === undefined) colorEditType = editType;
63160 var attrs = {
63161 family: {
63162 valType: 'string',
63163
63164 noBlank: true,
63165 strict: true,
63166 editType: editType,
63167
63168 },
63169 size: {
63170 valType: 'number',
63171
63172 min: 1,
63173 editType: editType
63174 },
63175 color: {
63176 valType: 'color',
63177
63178 editType: colorEditType
63179 },
63180 editType: editType,
63181 // blank strings so compress_attributes can remove
63182 // TODO - that's uber hacky... better solution?
63183
63184 };
63185
63186 if(opts.arrayOk) {
63187 attrs.family.arrayOk = true;
63188 attrs.size.arrayOk = true;
63189 attrs.color.arrayOk = true;
63190 }
63191
63192 return attrs;
63193};
63194
63195},{}],251:[function(_dereq_,module,exports){
63196/**
63197* Copyright 2012-2020, Plotly, Inc.
63198* All rights reserved.
63199*
63200* This source code is licensed under the MIT license found in the
63201* LICENSE file in the root directory of this source tree.
63202*/
63203
63204'use strict';
63205
63206module.exports = {
63207 _isLinkedToArray: 'frames_entry',
63208
63209 group: {
63210 valType: 'string',
63211
63212
63213 },
63214 name: {
63215 valType: 'string',
63216
63217
63218 },
63219 traces: {
63220 valType: 'any',
63221
63222
63223 },
63224 baseframe: {
63225 valType: 'string',
63226
63227
63228 },
63229 data: {
63230 valType: 'any',
63231
63232
63233 },
63234 layout: {
63235 valType: 'any',
63236
63237
63238 }
63239};
63240
63241},{}],252:[function(_dereq_,module,exports){
63242/**
63243* Copyright 2012-2020, Plotly, Inc.
63244* All rights reserved.
63245*
63246* This source code is licensed under the MIT license found in the
63247* LICENSE file in the root directory of this source tree.
63248*/
63249
63250'use strict';
63251
63252var Registry = _dereq_('../registry');
63253var SUBPLOT_PATTERN = _dereq_('./cartesian/constants').SUBPLOT_PATTERN;
63254
63255/**
63256 * Get calcdata trace(s) associated with a given subplot
63257 *
63258 * @param {array} calcData: as in gd.calcdata
63259 * @param {string} type: subplot type
63260 * @param {string} subplotId: subplot id to look for
63261 *
63262 * @return {array} array of calcdata traces
63263 */
63264exports.getSubplotCalcData = function(calcData, type, subplotId) {
63265 var basePlotModule = Registry.subplotsRegistry[type];
63266 if(!basePlotModule) return [];
63267
63268 var attr = basePlotModule.attr;
63269 var subplotCalcData = [];
63270
63271 for(var i = 0; i < calcData.length; i++) {
63272 var calcTrace = calcData[i];
63273 var trace = calcTrace[0].trace;
63274
63275 if(trace[attr] === subplotId) subplotCalcData.push(calcTrace);
63276 }
63277
63278 return subplotCalcData;
63279};
63280/**
63281 * Get calcdata trace(s) that can be plotted with a given module
63282 * NOTE: this isn't necessarily just exactly matching trace type,
63283 * if multiple trace types use the same plotting routine, they will be
63284 * collected here.
63285 * In order to not plot the same thing multiple times, we return two arrays,
63286 * the calcdata we *will* plot with this module, and the ones we *won't*
63287 *
63288 * @param {array} calcdata: as in gd.calcdata
63289 * @param {object|string|fn} arg1:
63290 * the plotting module, or its name, or its plot method
63291 *
63292 * @return {array[array]} [foundCalcdata, remainingCalcdata]
63293 */
63294exports.getModuleCalcData = function(calcdata, arg1) {
63295 var moduleCalcData = [];
63296 var remainingCalcData = [];
63297
63298 var plotMethod;
63299 if(typeof arg1 === 'string') {
63300 plotMethod = Registry.getModule(arg1).plot;
63301 } else if(typeof arg1 === 'function') {
63302 plotMethod = arg1;
63303 } else {
63304 plotMethod = arg1.plot;
63305 }
63306 if(!plotMethod) {
63307 return [moduleCalcData, calcdata];
63308 }
63309
63310 for(var i = 0; i < calcdata.length; i++) {
63311 var cd = calcdata[i];
63312 var trace = cd[0].trace;
63313 // N.B.
63314 // - 'legendonly' traces do not make it past here
63315 // - skip over 'visible' traces that got trimmed completely during calc transforms
63316 if(trace.visible !== true || trace._length === 0) continue;
63317
63318 // group calcdata trace not by 'module' (as the name of this function
63319 // would suggest), but by 'module plot method' so that if some traces
63320 // share the same module plot method (e.g. bar and histogram), we
63321 // only call it one!
63322 if(trace._module.plot === plotMethod) {
63323 moduleCalcData.push(cd);
63324 } else {
63325 remainingCalcData.push(cd);
63326 }
63327 }
63328
63329 return [moduleCalcData, remainingCalcData];
63330};
63331
63332/**
63333 * Get the data trace(s) associated with a given subplot.
63334 *
63335 * @param {array} data plotly full data array.
63336 * @param {string} type subplot type to look for.
63337 * @param {string} subplotId subplot id to look for.
63338 *
63339 * @return {array} list of trace objects.
63340 *
63341 */
63342exports.getSubplotData = function getSubplotData(data, type, subplotId) {
63343 if(!Registry.subplotsRegistry[type]) return [];
63344
63345 var attr = Registry.subplotsRegistry[type].attr;
63346 var subplotData = [];
63347 var trace, subplotX, subplotY;
63348
63349 if(type === 'gl2d') {
63350 var spmatch = subplotId.match(SUBPLOT_PATTERN);
63351 subplotX = 'x' + spmatch[1];
63352 subplotY = 'y' + spmatch[2];
63353 }
63354
63355 for(var i = 0; i < data.length; i++) {
63356 trace = data[i];
63357
63358 if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) {
63359 if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) {
63360 subplotData.push(trace);
63361 }
63362 } else {
63363 if(trace[attr] === subplotId) subplotData.push(trace);
63364 }
63365 }
63366
63367 return subplotData;
63368};
63369
63370},{"../registry":269,"./cartesian/constants":228}],253:[function(_dereq_,module,exports){
63371/**
63372* Copyright 2012-2020, Plotly, Inc.
63373* All rights reserved.
63374*
63375* This source code is licensed under the MIT license found in the
63376* LICENSE file in the root directory of this source tree.
63377*/
63378
63379
63380'use strict';
63381
63382function xformMatrix(m, v) {
63383 var out = [0, 0, 0, 0];
63384 var i, j;
63385
63386 for(i = 0; i < 4; ++i) {
63387 for(j = 0; j < 4; ++j) {
63388 out[j] += m[4 * i + j] * v[i];
63389 }
63390 }
63391
63392 return out;
63393}
63394
63395function project(camera, v) {
63396 var p = xformMatrix(camera.projection,
63397 xformMatrix(camera.view,
63398 xformMatrix(camera.model, [v[0], v[1], v[2], 1])));
63399 return p;
63400}
63401
63402module.exports = project;
63403
63404},{}],254:[function(_dereq_,module,exports){
63405/**
63406* Copyright 2012-2020, Plotly, Inc.
63407* All rights reserved.
63408*
63409* This source code is licensed under the MIT license found in the
63410* LICENSE file in the root directory of this source tree.
63411*/
63412
63413'use strict';
63414
63415var fontAttrs = _dereq_('./font_attributes');
63416var animationAttrs = _dereq_('./animation_attributes');
63417var colorAttrs = _dereq_('../components/color/attributes');
63418var drawNewShapeAttrs = _dereq_('../components/shapes/draw_newshape/attributes');
63419var padAttrs = _dereq_('./pad_attributes');
63420var extendFlat = _dereq_('../lib/extend').extendFlat;
63421
63422var globalFont = fontAttrs({
63423 editType: 'calc',
63424
63425});
63426globalFont.family.dflt = '"Open Sans", verdana, arial, sans-serif';
63427globalFont.size.dflt = 12;
63428globalFont.color.dflt = colorAttrs.defaultLine;
63429
63430module.exports = {
63431 font: globalFont,
63432 title: {
63433 text: {
63434 valType: 'string',
63435
63436 editType: 'layoutstyle',
63437
63438 },
63439 font: fontAttrs({
63440 editType: 'layoutstyle',
63441
63442 }),
63443 xref: {
63444 valType: 'enumerated',
63445 dflt: 'container',
63446 values: ['container', 'paper'],
63447
63448 editType: 'layoutstyle',
63449
63450 },
63451 yref: {
63452 valType: 'enumerated',
63453 dflt: 'container',
63454 values: ['container', 'paper'],
63455
63456 editType: 'layoutstyle',
63457
63458 },
63459 x: {
63460 valType: 'number',
63461 min: 0,
63462 max: 1,
63463 dflt: 0.5,
63464
63465 editType: 'layoutstyle',
63466
63467 },
63468 y: {
63469 valType: 'number',
63470 min: 0,
63471 max: 1,
63472 dflt: 'auto',
63473
63474 editType: 'layoutstyle',
63475
63476 },
63477 xanchor: {
63478 valType: 'enumerated',
63479 dflt: 'auto',
63480 values: ['auto', 'left', 'center', 'right'],
63481
63482 editType: 'layoutstyle',
63483
63484 },
63485 yanchor: {
63486 valType: 'enumerated',
63487 dflt: 'auto',
63488 values: ['auto', 'top', 'middle', 'bottom'],
63489
63490 editType: 'layoutstyle',
63491
63492 },
63493 pad: extendFlat(padAttrs({editType: 'layoutstyle'}), {
63494
63495 }),
63496 editType: 'layoutstyle'
63497 },
63498 uniformtext: {
63499 mode: {
63500 valType: 'enumerated',
63501 values: [false, 'hide', 'show'],
63502 dflt: false,
63503
63504 editType: 'plot',
63505
63506 },
63507 minsize: {
63508 valType: 'number',
63509 min: 0,
63510 dflt: 0,
63511
63512 editType: 'plot',
63513
63514 },
63515 editType: 'plot'
63516 },
63517 autosize: {
63518 valType: 'boolean',
63519
63520 dflt: false,
63521 // autosize, width, and height get special editType treatment in _relayout
63522 // so we can handle noop resizes more efficiently
63523 editType: 'none',
63524
63525 },
63526 width: {
63527 valType: 'number',
63528
63529 min: 10,
63530 dflt: 700,
63531 editType: 'plot',
63532
63533 },
63534 height: {
63535 valType: 'number',
63536
63537 min: 10,
63538 dflt: 450,
63539 editType: 'plot',
63540
63541 },
63542 margin: {
63543 l: {
63544 valType: 'number',
63545
63546 min: 0,
63547 dflt: 80,
63548 editType: 'plot',
63549
63550 },
63551 r: {
63552 valType: 'number',
63553
63554 min: 0,
63555 dflt: 80,
63556 editType: 'plot',
63557
63558 },
63559 t: {
63560 valType: 'number',
63561
63562 min: 0,
63563 dflt: 100,
63564 editType: 'plot',
63565
63566 },
63567 b: {
63568 valType: 'number',
63569
63570 min: 0,
63571 dflt: 80,
63572 editType: 'plot',
63573
63574 },
63575 pad: {
63576 valType: 'number',
63577
63578 min: 0,
63579 dflt: 0,
63580 editType: 'plot',
63581
63582 },
63583 autoexpand: {
63584 valType: 'boolean',
63585
63586 dflt: true,
63587 editType: 'plot',
63588
63589 },
63590 editType: 'plot'
63591 },
63592 paper_bgcolor: {
63593 valType: 'color',
63594
63595 dflt: colorAttrs.background,
63596 editType: 'plot',
63597
63598 },
63599 plot_bgcolor: {
63600 // defined here, but set in cartesian.supplyLayoutDefaults
63601 // because it needs to know if there are (2D) axes or not
63602 valType: 'color',
63603
63604 dflt: colorAttrs.background,
63605 editType: 'layoutstyle',
63606
63607 },
63608 separators: {
63609 valType: 'string',
63610
63611 editType: 'plot',
63612
63613 },
63614 hidesources: {
63615 valType: 'boolean',
63616
63617 dflt: false,
63618 editType: 'plot',
63619
63620 },
63621 showlegend: {
63622 // handled in legend.supplyLayoutDefaults
63623 // but included here because it's not in the legend object
63624 valType: 'boolean',
63625
63626 editType: 'legend',
63627
63628 },
63629 colorway: {
63630 valType: 'colorlist',
63631 dflt: colorAttrs.defaults,
63632
63633 editType: 'calc',
63634
63635 },
63636 datarevision: {
63637 valType: 'any',
63638
63639 editType: 'calc',
63640
63641 },
63642 uirevision: {
63643 valType: 'any',
63644
63645 editType: 'none',
63646
63647 },
63648 editrevision: {
63649 valType: 'any',
63650
63651 editType: 'none',
63652
63653 },
63654 selectionrevision: {
63655 valType: 'any',
63656
63657 editType: 'none',
63658
63659 },
63660 template: {
63661 valType: 'any',
63662
63663 editType: 'calc',
63664
63665 },
63666 modebar: {
63667 orientation: {
63668 valType: 'enumerated',
63669 values: ['v', 'h'],
63670 dflt: 'h',
63671
63672 editType: 'modebar',
63673
63674 },
63675 bgcolor: {
63676 valType: 'color',
63677
63678 editType: 'modebar',
63679
63680 },
63681 color: {
63682 valType: 'color',
63683
63684 editType: 'modebar',
63685
63686 },
63687 activecolor: {
63688 valType: 'color',
63689
63690 editType: 'modebar',
63691
63692 },
63693 uirevision: {
63694 valType: 'any',
63695
63696 editType: 'none',
63697
63698 },
63699 editType: 'modebar'
63700 },
63701
63702 newshape: drawNewShapeAttrs.newshape,
63703 activeshape: drawNewShapeAttrs.activeshape,
63704
63705 meta: {
63706 valType: 'any',
63707 arrayOk: true,
63708
63709 editType: 'plot',
63710
63711 },
63712
63713 transition: extendFlat({}, animationAttrs.transition, {
63714
63715 editType: 'none'
63716 }),
63717 _deprecated: {
63718 title: {
63719 valType: 'string',
63720
63721 editType: 'layoutstyle',
63722
63723 },
63724 titlefont: fontAttrs({
63725 editType: 'layoutstyle',
63726
63727 })
63728 }
63729};
63730
63731},{"../components/color/attributes":51,"../components/shapes/draw_newshape/attributes":134,"../lib/extend":173,"./animation_attributes":217,"./font_attributes":250,"./pad_attributes":255}],255:[function(_dereq_,module,exports){
63732/**
63733* Copyright 2012-2020, Plotly, Inc.
63734* All rights reserved.
63735*
63736* This source code is licensed under the MIT license found in the
63737* LICENSE file in the root directory of this source tree.
63738*/
63739
63740'use strict';
63741
63742/**
63743 * Creates a set of padding attributes.
63744 *
63745 * @param {object} opts
63746 * @param {string} editType:
63747 * the editType for all pieces of this padding definition
63748 *
63749 * @return {object} attributes object containing {t, r, b, l} as specified
63750 */
63751module.exports = function(opts) {
63752 var editType = opts.editType;
63753 return {
63754 t: {
63755 valType: 'number',
63756 dflt: 0,
63757
63758 editType: editType,
63759
63760 },
63761 r: {
63762 valType: 'number',
63763 dflt: 0,
63764
63765 editType: editType,
63766
63767 },
63768 b: {
63769 valType: 'number',
63770 dflt: 0,
63771
63772 editType: editType,
63773
63774 },
63775 l: {
63776 valType: 'number',
63777 dflt: 0,
63778
63779 editType: editType,
63780
63781 },
63782 editType: editType
63783 };
63784};
63785
63786},{}],256:[function(_dereq_,module,exports){
63787/**
63788* Copyright 2012-2020, Plotly, Inc.
63789* All rights reserved.
63790*
63791* This source code is licensed under the MIT license found in the
63792* LICENSE file in the root directory of this source tree.
63793*/
63794
63795'use strict';
63796
63797var d3 = _dereq_('d3');
63798var isNumeric = _dereq_('fast-isnumeric');
63799
63800var Registry = _dereq_('../registry');
63801var PlotSchema = _dereq_('../plot_api/plot_schema');
63802var Template = _dereq_('../plot_api/plot_template');
63803var Lib = _dereq_('../lib');
63804var Color = _dereq_('../components/color');
63805var BADNUM = _dereq_('../constants/numerical').BADNUM;
63806
63807var axisIDs = _dereq_('./cartesian/axis_ids');
63808var clearSelect = _dereq_('./cartesian/handle_outline').clearSelect;
63809
63810var animationAttrs = _dereq_('./animation_attributes');
63811var frameAttrs = _dereq_('./frame_attributes');
63812
63813var getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData;
63814
63815var relinkPrivateKeys = Lib.relinkPrivateKeys;
63816var _ = Lib._;
63817
63818var plots = module.exports = {};
63819
63820// Expose registry methods on Plots for backward-compatibility
63821Lib.extendFlat(plots, Registry);
63822
63823plots.attributes = _dereq_('./attributes');
63824plots.attributes.type.values = plots.allTypes;
63825plots.fontAttrs = _dereq_('./font_attributes');
63826plots.layoutAttributes = _dereq_('./layout_attributes');
63827
63828// TODO make this a plot attribute?
63829plots.fontWeight = 'normal';
63830
63831var transformsRegistry = plots.transformsRegistry;
63832
63833var commandModule = _dereq_('./command');
63834plots.executeAPICommand = commandModule.executeAPICommand;
63835plots.computeAPICommandBindings = commandModule.computeAPICommandBindings;
63836plots.manageCommandObserver = commandModule.manageCommandObserver;
63837plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings;
63838
63839// in some cases the browser doesn't seem to know how big
63840// the text is at first, so it needs to draw it,
63841// then wait a little, then draw it again
63842plots.redrawText = function(gd) {
63843 gd = Lib.getGraphDiv(gd);
63844
63845 var fullLayout = gd._fullLayout || {};
63846 var hasPolar = fullLayout._has && fullLayout._has('polar');
63847 var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r;
63848
63849 // do not work if polar is present
63850 if(hasLegacyPolar) return;
63851
63852 return new Promise(function(resolve) {
63853 setTimeout(function() {
63854 Registry.getComponentMethod('annotations', 'draw')(gd);
63855 Registry.getComponentMethod('legend', 'draw')(gd);
63856 Registry.getComponentMethod('colorbar', 'draw')(gd);
63857 resolve(plots.previousPromises(gd));
63858 }, 300);
63859 });
63860};
63861
63862// resize plot about the container size
63863plots.resize = function(gd) {
63864 gd = Lib.getGraphDiv(gd);
63865
63866 var resolveLastResize;
63867 var p = new Promise(function(resolve, reject) {
63868 if(!gd || Lib.isHidden(gd)) {
63869 reject(new Error('Resize must be passed a displayed plot div element.'));
63870 }
63871
63872 if(gd._redrawTimer) clearTimeout(gd._redrawTimer);
63873 if(gd._resolveResize) resolveLastResize = gd._resolveResize;
63874 gd._resolveResize = resolve;
63875
63876 gd._redrawTimer = setTimeout(function() {
63877 // return if there is nothing to resize or is hidden
63878 if(!gd.layout || (gd.layout.width && gd.layout.height) || Lib.isHidden(gd)) {
63879 resolve(gd);
63880 return;
63881 }
63882
63883 delete gd.layout.width;
63884 delete gd.layout.height;
63885
63886 // autosizing doesn't count as a change that needs saving
63887 var oldchanged = gd.changed;
63888
63889 // nor should it be included in the undo queue
63890 gd.autoplay = true;
63891
63892 Registry.call('relayout', gd, {autosize: true}).then(function() {
63893 gd.changed = oldchanged;
63894 // Only resolve if a new call hasn't been made!
63895 if(gd._resolveResize === resolve) {
63896 delete gd._resolveResize;
63897 resolve(gd);
63898 }
63899 });
63900 }, 100);
63901 });
63902
63903 if(resolveLastResize) resolveLastResize(p);
63904 return p;
63905};
63906
63907
63908// for use in Lib.syncOrAsync, check if there are any
63909// pending promises in this plot and wait for them
63910plots.previousPromises = function(gd) {
63911 if((gd._promises || []).length) {
63912 return Promise.all(gd._promises)
63913 .then(function() { gd._promises = []; });
63914 }
63915};
63916
63917/**
63918 * Adds the 'Edit chart' link.
63919 * Note that now Plotly.plot() calls this so it can regenerate whenever it replots
63920 *
63921 * Add source links to your graph inside the 'showSources' config argument.
63922 */
63923plots.addLinks = function(gd) {
63924 // Do not do anything if showLink and showSources are not set to true in config
63925 if(!gd._context.showLink && !gd._context.showSources) return;
63926
63927 var fullLayout = gd._fullLayout;
63928
63929 var linkContainer = Lib.ensureSingle(fullLayout._paper, 'text', 'js-plot-link-container', function(s) {
63930 s.style({
63931 'font-family': '"Open Sans", Arial, sans-serif',
63932 'font-size': '12px',
63933 'fill': Color.defaultLine,
63934 'pointer-events': 'all'
63935 })
63936 .each(function() {
63937 var links = d3.select(this);
63938 links.append('tspan').classed('js-link-to-tool', true);
63939 links.append('tspan').classed('js-link-spacer', true);
63940 links.append('tspan').classed('js-sourcelinks', true);
63941 });
63942 });
63943
63944 // The text node inside svg
63945 var text = linkContainer.node();
63946 var attrs = {y: fullLayout._paper.attr('height') - 9};
63947
63948 // If text's width is bigger than the layout
63949 // Check that text is a child node or document.body
63950 // because otherwise IE/Edge might throw an exception
63951 // when calling getComputedTextLength().
63952 // Apparently offsetParent is null for invisibles.
63953 if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) {
63954 // Align the text at the left
63955 attrs['text-anchor'] = 'start';
63956 attrs.x = 5;
63957 } else {
63958 // Align the text at the right
63959 attrs['text-anchor'] = 'end';
63960 attrs.x = fullLayout._paper.attr('width') - 7;
63961 }
63962
63963 linkContainer.attr(attrs);
63964
63965 var toolspan = linkContainer.select('.js-link-to-tool');
63966 var spacespan = linkContainer.select('.js-link-spacer');
63967 var sourcespan = linkContainer.select('.js-sourcelinks');
63968
63969 if(gd._context.showSources) gd._context.showSources(gd);
63970
63971 // 'view in plotly' link for embedded plots
63972 if(gd._context.showLink) positionPlayWithData(gd, toolspan);
63973
63974 // separator if we have both sources and tool link
63975 spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');
63976};
63977
63978// note that now this function is only adding the brand in
63979// iframes and 3rd-party apps
63980function positionPlayWithData(gd, container) {
63981 container.text('');
63982 var link = container.append('a')
63983 .attr({
63984 'xlink:xlink:href': '#',
63985 'class': 'link--impt link--embedview',
63986 'font-weight': 'bold'
63987 })
63988 .text(gd._context.linkText + ' ' + String.fromCharCode(187));
63989
63990 if(gd._context.sendData) {
63991 link.on('click', function() {
63992 plots.sendDataToCloud(gd);
63993 });
63994 } else {
63995 var path = window.location.pathname.split('/');
63996 var query = window.location.search;
63997 link.attr({
63998 'xlink:xlink:show': 'new',
63999 'xlink:xlink:href': '/' + path[2].split('.')[0] + '/' + path[1] + query
64000 });
64001 }
64002}
64003
64004plots.sendDataToCloud = function(gd) {
64005 var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL;
64006 if(!baseUrl) return;
64007
64008 gd.emit('plotly_beforeexport');
64009
64010 var hiddenformDiv = d3.select(gd)
64011 .append('div')
64012 .attr('id', 'hiddenform')
64013 .style('display', 'none');
64014
64015 var hiddenform = hiddenformDiv
64016 .append('form')
64017 .attr({
64018 action: baseUrl + '/external',
64019 method: 'post',
64020 target: '_blank'
64021 });
64022
64023 var hiddenformInput = hiddenform
64024 .append('input')
64025 .attr({
64026 type: 'text',
64027 name: 'data'
64028 });
64029
64030 hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
64031 hiddenform.node().submit();
64032 hiddenformDiv.remove();
64033
64034 gd.emit('plotly_afterexport');
64035 return false;
64036};
64037
64038var d3FormatKeys = [
64039 'days', 'shortDays', 'months', 'shortMonths', 'periods',
64040 'dateTime', 'date', 'time',
64041 'decimal', 'thousands', 'grouping', 'currency'
64042];
64043
64044var extraFormatKeys = [
64045 'year', 'month', 'dayMonth', 'dayMonthYear'
64046];
64047
64048/*
64049 * Fill in default values
64050 * @param {DOM element} gd
64051 * @param {object} opts
64052 * @param {boolean} opts.skipUpdateCalc: normally if the existing gd.calcdata looks
64053 * compatible with the new gd._fullData we finish by linking the new _fullData traces
64054 * to the old gd.calcdata, so it's correctly set if we're not going to recalc. But also,
64055 * if there are calcTransforms on the trace, we first remap data arrays from the old full
64056 * trace into the new one. Use skipUpdateCalc to defer this (needed by Plotly.react)
64057 *
64058 * gd.data, gd.layout:
64059 * are precisely what the user specified (except as modified by cleanData/cleanLayout),
64060 * these fields shouldn't be modified (except for filling in some auto values)
64061 * nor used directly after the supply defaults step.
64062 *
64063 * gd._fullData, gd._fullLayout:
64064 * are complete descriptions of how to draw the plot,
64065 * use these fields in all required computations.
64066 *
64067 * gd._fullLayout._modules
64068 * is a list of all the trace modules required to draw the plot.
64069 *
64070 * gd._fullLayout._visibleModules
64071 * subset of _modules, a list of modules corresponding to visible:true traces.
64072 *
64073 * gd._fullLayout._basePlotModules
64074 * is a list of all the plot modules required to draw the plot.
64075 *
64076 * gd._fullLayout._transformModules
64077 * is a list of all the transform modules invoked.
64078 *
64079 */
64080plots.supplyDefaults = function(gd, opts) {
64081 var skipUpdateCalc = opts && opts.skipUpdateCalc;
64082 var oldFullLayout = gd._fullLayout || {};
64083
64084 if(oldFullLayout._skipDefaults) {
64085 delete oldFullLayout._skipDefaults;
64086 return;
64087 }
64088
64089 var newFullLayout = gd._fullLayout = {};
64090 var newLayout = gd.layout || {};
64091
64092 var oldFullData = gd._fullData || [];
64093 var newFullData = gd._fullData = [];
64094 var newData = gd.data || [];
64095
64096 var oldCalcdata = gd.calcdata || [];
64097
64098 var context = gd._context || {};
64099
64100 var i;
64101
64102 // Create all the storage space for frames, but only if doesn't already exist
64103 if(!gd._transitionData) plots.createTransitionData(gd);
64104
64105 // So we only need to do this once (and since we have gd here)
64106 // get the translated placeholder titles.
64107 // These ones get used as default values so need to be known at supplyDefaults
64108 // others keep their blank defaults but render the placeholder as desired later
64109 // TODO: make these work the same way, only inserting the placeholder text at draw time?
64110 // The challenge is that this has slightly different behavior right now in editable mode:
64111 // using the placeholder as default makes this text permanently (but lightly) visible,
64112 // but explicit '' for these titles gives you a placeholder that's hidden until you mouse
64113 // over it - so you're not distracted by it if you really don't want a title, but if you do
64114 // and you're new to plotly you may not be able to find it.
64115 // When editable=false the two behave the same, no title is drawn.
64116 newFullLayout._dfltTitle = {
64117 plot: _(gd, 'Click to enter Plot title'),
64118 x: _(gd, 'Click to enter X axis title'),
64119 y: _(gd, 'Click to enter Y axis title'),
64120 colorbar: _(gd, 'Click to enter Colorscale title'),
64121 annotation: _(gd, 'new text')
64122 };
64123 newFullLayout._traceWord = _(gd, 'trace');
64124
64125 var formatObj = getFormatObj(gd, d3FormatKeys);
64126
64127 // stash the token from context so mapbox subplots can use it as default
64128 newFullLayout._mapboxAccessToken = context.mapboxAccessToken;
64129
64130 // first fill in what we can of layout without looking at data
64131 // because fullData needs a few things from layout
64132 if(oldFullLayout._initialAutoSizeIsDone) {
64133 // coerce the updated layout while preserving width and height
64134 var oldWidth = oldFullLayout.width;
64135 var oldHeight = oldFullLayout.height;
64136
64137 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
64138
64139 if(!newLayout.width) newFullLayout.width = oldWidth;
64140 if(!newLayout.height) newFullLayout.height = oldHeight;
64141 plots.sanitizeMargins(newFullLayout);
64142 } else {
64143 // coerce the updated layout and autosize if needed
64144 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
64145
64146 var missingWidthOrHeight = (!newLayout.width || !newLayout.height);
64147 var autosize = newFullLayout.autosize;
64148 var autosizable = context.autosizable;
64149 var initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
64150
64151 if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
64152 else if(missingWidthOrHeight) plots.sanitizeMargins(newFullLayout);
64153
64154 // for backwards-compatibility with Plotly v1.x.x
64155 if(!autosize && missingWidthOrHeight) {
64156 newLayout.width = newFullLayout.width;
64157 newLayout.height = newFullLayout.height;
64158 }
64159 }
64160
64161 newFullLayout._d3locale = getFormatter(formatObj, newFullLayout.separators);
64162 newFullLayout._extraFormat = getFormatObj(gd, extraFormatKeys);
64163
64164 newFullLayout._initialAutoSizeIsDone = true;
64165
64166 // keep track of how many traces are inputted
64167 newFullLayout._dataLength = newData.length;
64168
64169 // clear the lists of trace and baseplot modules, and subplots
64170 newFullLayout._modules = [];
64171 newFullLayout._visibleModules = [];
64172 newFullLayout._basePlotModules = [];
64173 var subplots = newFullLayout._subplots = emptySubplotLists();
64174
64175 // initialize axis and subplot hash objects for splom-generated grids
64176 var splomAxes = newFullLayout._splomAxes = {x: {}, y: {}};
64177 var splomSubplots = newFullLayout._splomSubplots = {};
64178 // initialize splom grid defaults
64179 newFullLayout._splomGridDflt = {};
64180
64181 // for stacked area traces to share config across traces
64182 newFullLayout._scatterStackOpts = {};
64183 // for the first scatter trace on each subplot (so it knows tonext->tozero)
64184 newFullLayout._firstScatter = {};
64185 // for grouped bar/box/violin trace to share config across traces
64186 newFullLayout._alignmentOpts = {};
64187 // track color axes referenced in the data
64188 newFullLayout._colorAxes = {};
64189
64190 // for traces to request a default rangeslider on their x axes
64191 // eg set `_requestRangeslider.x2 = true` for xaxis2
64192 newFullLayout._requestRangeslider = {};
64193
64194 // pull uids from old data to use as new defaults
64195 newFullLayout._traceUids = getTraceUids(oldFullData, newData);
64196
64197 // then do the data
64198 newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
64199 plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
64200
64201 // redo grid size defaults with info about splom x/y axes,
64202 // and fill in generated cartesian axes and subplots
64203 var splomXa = Object.keys(splomAxes.x);
64204 var splomYa = Object.keys(splomAxes.y);
64205 if(splomXa.length > 1 && splomYa.length > 1) {
64206 Registry.getComponentMethod('grid', 'sizeDefaults')(newLayout, newFullLayout);
64207
64208 for(i = 0; i < splomXa.length; i++) {
64209 Lib.pushUnique(subplots.xaxis, splomXa[i]);
64210 }
64211 for(i = 0; i < splomYa.length; i++) {
64212 Lib.pushUnique(subplots.yaxis, splomYa[i]);
64213 }
64214 for(var k in splomSubplots) {
64215 Lib.pushUnique(subplots.cartesian, k);
64216 }
64217 }
64218
64219 // attach helper method to check whether a plot type is present on graph
64220 newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
64221
64222 if(oldFullData.length === newFullData.length) {
64223 for(i = 0; i < newFullData.length; i++) {
64224 relinkPrivateKeys(newFullData[i], oldFullData[i]);
64225 }
64226 }
64227
64228 // finally, fill in the pieces of layout that may need to look at data
64229 plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
64230
64231 // Special cases that introduce interactions between traces.
64232 // This is after relinkPrivateKeys so we can use those in crossTraceDefaults
64233 // and after layout module defaults, so we can use eg barmode
64234 var _modules = newFullLayout._visibleModules;
64235 var crossTraceDefaultsFuncs = [];
64236 for(i = 0; i < _modules.length; i++) {
64237 var funci = _modules[i].crossTraceDefaults;
64238 // some trace types share crossTraceDefaults (ie histogram2d, histogram2dcontour)
64239 if(funci) Lib.pushUnique(crossTraceDefaultsFuncs, funci);
64240 }
64241 for(i = 0; i < crossTraceDefaultsFuncs.length; i++) {
64242 crossTraceDefaultsFuncs[i](newFullData, newFullLayout);
64243 }
64244
64245 // turn on flag to optimize large splom-only graphs
64246 // mostly by omitting SVG layers during Cartesian.drawFramework
64247 newFullLayout._hasOnlyLargeSploms = (
64248 newFullLayout._basePlotModules.length === 1 &&
64249 newFullLayout._basePlotModules[0].name === 'splom' &&
64250 splomXa.length > 15 &&
64251 splomYa.length > 15 &&
64252 newFullLayout.shapes.length === 0 &&
64253 newFullLayout.images.length === 0
64254 );
64255
64256 // TODO remove in v2.0.0
64257 // add has-plot-type refs to fullLayout for backward compatibility
64258 newFullLayout._hasCartesian = newFullLayout._has('cartesian');
64259 newFullLayout._hasGeo = newFullLayout._has('geo');
64260 newFullLayout._hasGL3D = newFullLayout._has('gl3d');
64261 newFullLayout._hasGL2D = newFullLayout._has('gl2d');
64262 newFullLayout._hasTernary = newFullLayout._has('ternary');
64263 newFullLayout._hasPie = newFullLayout._has('pie');
64264
64265 // relink / initialize subplot axis objects
64266 plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
64267
64268 // clean subplots and other artifacts from previous plot calls
64269 plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
64270
64271 // clear selection outline until we implement persistent selection,
64272 // don't clear them though when drag handlers (e.g. listening to
64273 // `plotly_selecting`) update the graph.
64274 // we should try to come up with a better solution when implementing
64275 // https://github.com/plotly/plotly.js/issues/1851
64276 if(oldFullLayout._zoomlayer && !gd._dragging) {
64277 clearSelect({ // mock old gd
64278 _fullLayout: oldFullLayout
64279 });
64280 }
64281
64282
64283 // fill in meta helpers
64284 fillMetaTextHelpers(newFullData, newFullLayout);
64285
64286 // relink functions and _ attributes to promote consistency between plots
64287 relinkPrivateKeys(newFullLayout, oldFullLayout);
64288
64289 // colorscale crossTraceDefaults needs newFullLayout with relinked keys
64290 Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);
64291
64292 // For persisting GUI-driven changes in layout
64293 // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys
64294 if(!newFullLayout._preGUI) newFullLayout._preGUI = {};
64295 // track trace GUI changes by uid rather than by trace index
64296 if(!newFullLayout._tracePreGUI) newFullLayout._tracePreGUI = {};
64297 var tracePreGUI = newFullLayout._tracePreGUI;
64298 var uids = {};
64299 var uid;
64300 for(uid in tracePreGUI) uids[uid] = 'old';
64301 for(i = 0; i < newFullData.length; i++) {
64302 uid = newFullData[i]._fullInput.uid;
64303 if(!uids[uid]) tracePreGUI[uid] = {};
64304 uids[uid] = 'new';
64305 }
64306 for(uid in uids) {
64307 if(uids[uid] === 'old') delete tracePreGUI[uid];
64308 }
64309
64310 // set up containers for margin calculations
64311 initMargins(newFullLayout);
64312
64313 // collect and do some initial calculations for rangesliders
64314 Registry.getComponentMethod('rangeslider', 'makeData')(newFullLayout);
64315
64316 // update object references in calcdata
64317 if(!skipUpdateCalc && oldCalcdata.length === newFullData.length) {
64318 plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData);
64319 }
64320};
64321
64322plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) {
64323 for(var i = 0; i < newFullData.length; i++) {
64324 var newTrace = newFullData[i];
64325 var cd0 = (oldCalcdata[i] || [])[0];
64326 if(cd0 && cd0.trace) {
64327 var oldTrace = cd0.trace;
64328 if(oldTrace._hasCalcTransform) {
64329 var arrayAttrs = oldTrace._arrayAttrs;
64330 var j, astr, oldArrayVal;
64331
64332 for(j = 0; j < arrayAttrs.length; j++) {
64333 astr = arrayAttrs[j];
64334 oldArrayVal = Lib.nestedProperty(oldTrace, astr).get().slice();
64335 Lib.nestedProperty(newTrace, astr).set(oldArrayVal);
64336 }
64337 }
64338 cd0.trace = newTrace;
64339 }
64340 }
64341};
64342
64343/**
64344 * Create a list of uid strings satisfying (in this order of importance):
64345 * 1. all unique, all strings
64346 * 2. matches input uids if provided
64347 * 3. matches previous data uids
64348 */
64349function getTraceUids(oldFullData, newData) {
64350 var len = newData.length;
64351 var oldFullInput = [];
64352 var i, prevFullInput;
64353 for(i = 0; i < oldFullData.length; i++) {
64354 var thisFullInput = oldFullData[i]._fullInput;
64355 if(thisFullInput !== prevFullInput) oldFullInput.push(thisFullInput);
64356 prevFullInput = thisFullInput;
64357 }
64358 var oldLen = oldFullInput.length;
64359 var out = new Array(len);
64360 var seenUids = {};
64361
64362 function setUid(uid, i) {
64363 out[i] = uid;
64364 seenUids[uid] = 1;
64365 }
64366
64367 function tryUid(uid, i) {
64368 if(uid && typeof uid === 'string' && !seenUids[uid]) {
64369 setUid(uid, i);
64370 return true;
64371 }
64372 }
64373
64374 for(i = 0; i < len; i++) {
64375 var newUid = newData[i].uid;
64376 if(typeof newUid === 'number') newUid = String(newUid);
64377
64378 if(tryUid(newUid, i)) continue;
64379 if(i < oldLen && tryUid(oldFullInput[i].uid, i)) continue;
64380 setUid(Lib.randstr(seenUids), i);
64381 }
64382
64383 return out;
64384}
64385
64386/**
64387 * Make a container for collecting subplots we need to display.
64388 *
64389 * Finds all subplot types we need to enumerate once and caches it,
64390 * but makes a new output object each time.
64391 * Single-trace subplots (which have no `id`) such as pie, table, etc
64392 * do not need to be collected because we just draw all visible traces.
64393 */
64394function emptySubplotLists() {
64395 var collectableSubplotTypes = Registry.collectableSubplotTypes;
64396 var out = {};
64397 var i, j;
64398
64399 if(!collectableSubplotTypes) {
64400 collectableSubplotTypes = [];
64401
64402 var subplotsRegistry = Registry.subplotsRegistry;
64403
64404 for(var subplotType in subplotsRegistry) {
64405 var subplotModule = subplotsRegistry[subplotType];
64406 var subplotAttr = subplotModule.attr;
64407
64408 if(subplotAttr) {
64409 collectableSubplotTypes.push(subplotType);
64410
64411 // special case, currently just for cartesian:
64412 // we need to enumerate axes, not just subplots
64413 if(Array.isArray(subplotAttr)) {
64414 for(j = 0; j < subplotAttr.length; j++) {
64415 Lib.pushUnique(collectableSubplotTypes, subplotAttr[j]);
64416 }
64417 }
64418 }
64419 }
64420 }
64421
64422 for(i = 0; i < collectableSubplotTypes.length; i++) {
64423 out[collectableSubplotTypes[i]] = [];
64424 }
64425 return out;
64426}
64427
64428/**
64429 * getFormatObj: use _context to get the format object from locale.
64430 * Used to get d3.locale argument object and extraFormat argument object
64431 *
64432 * Regarding d3.locale argument :
64433 * decimal and thousands can be overridden later by layout.separators
64434 * grouping and currency are not presently used by our automatic number
64435 * formatting system but can be used by custom formats.
64436 *
64437 * @returns {object} d3.locale format object
64438 */
64439function getFormatObj(gd, formatKeys) {
64440 var locale = gd._context.locale;
64441 if(!locale) locale === 'en-US';
64442
64443 var formatDone = false;
64444 var formatObj = {};
64445
64446 function includeFormat(newFormat) {
64447 var formatFinished = true;
64448 for(var i = 0; i < formatKeys.length; i++) {
64449 var formatKey = formatKeys[i];
64450 if(!formatObj[formatKey]) {
64451 if(newFormat[formatKey]) {
64452 formatObj[formatKey] = newFormat[formatKey];
64453 } else formatFinished = false;
64454 }
64455 }
64456 if(formatFinished) formatDone = true;
64457 }
64458
64459 // same as localize, look for format parts in each format spec in the chain
64460 for(var i = 0; i < 2; i++) {
64461 var locales = gd._context.locales;
64462 for(var j = 0; j < 2; j++) {
64463 var formatj = (locales[locale] || {}).format;
64464 if(formatj) {
64465 includeFormat(formatj);
64466 if(formatDone) break;
64467 }
64468 locales = Registry.localeRegistry;
64469 }
64470
64471 var baseLocale = locale.split('-')[0];
64472 if(formatDone || baseLocale === locale) break;
64473 locale = baseLocale;
64474 }
64475
64476 // lastly pick out defaults from english (non-US, as DMY is so much more common)
64477 if(!formatDone) includeFormat(Registry.localeRegistry.en.format);
64478
64479 return formatObj;
64480}
64481
64482/**
64483 * getFormatter: combine the final separators with the locale formatting object
64484 * we pulled earlier to generate number and time formatters
64485 * TODO: remove separators in v2, only use locale, so we don't need this step?
64486 *
64487 * @param {object} formatObj: d3.locale format object
64488 * @param {string} separators: length-2 string to override decimal and thousands
64489 * separators in number formatting
64490 *
64491 * @returns {object} {numberFormat, timeFormat} d3 formatter factory functions
64492 * for numbers and time
64493 */
64494function getFormatter(formatObj, separators) {
64495 formatObj.decimal = separators.charAt(0);
64496 formatObj.thousands = separators.charAt(1);
64497
64498 return d3.locale(formatObj);
64499}
64500
64501function fillMetaTextHelpers(newFullData, newFullLayout) {
64502 var _meta;
64503 var meta4data = [];
64504
64505 if(newFullLayout.meta) {
64506 _meta = newFullLayout._meta = {
64507 meta: newFullLayout.meta,
64508 layout: {meta: newFullLayout.meta}
64509 };
64510 }
64511
64512 for(var i = 0; i < newFullData.length; i++) {
64513 var trace = newFullData[i];
64514
64515 if(trace.meta) {
64516 meta4data[trace.index] = trace._meta = {meta: trace.meta};
64517 } else if(newFullLayout.meta) {
64518 trace._meta = {meta: newFullLayout.meta};
64519 }
64520 if(newFullLayout.meta) {
64521 trace._meta.layout = {meta: newFullLayout.meta};
64522 }
64523 }
64524
64525 if(meta4data.length) {
64526 if(!_meta) {
64527 _meta = newFullLayout._meta = {};
64528 }
64529 _meta.data = meta4data;
64530 }
64531}
64532
64533// Create storage for all of the data related to frames and transitions:
64534plots.createTransitionData = function(gd) {
64535 // Set up the default keyframe if it doesn't exist:
64536 if(!gd._transitionData) {
64537 gd._transitionData = {};
64538 }
64539
64540 if(!gd._transitionData._frames) {
64541 gd._transitionData._frames = [];
64542 }
64543
64544 if(!gd._transitionData._frameHash) {
64545 gd._transitionData._frameHash = {};
64546 }
64547
64548 if(!gd._transitionData._counter) {
64549 gd._transitionData._counter = 0;
64550 }
64551
64552 if(!gd._transitionData._interruptCallbacks) {
64553 gd._transitionData._interruptCallbacks = [];
64554 }
64555};
64556
64557// helper function to be bound to fullLayout to check
64558// whether a certain plot type is present on plot
64559// or trace has a category
64560plots._hasPlotType = function(category) {
64561 var i;
64562
64563 // check base plot modules
64564 var basePlotModules = this._basePlotModules || [];
64565 for(i = 0; i < basePlotModules.length; i++) {
64566 if(basePlotModules[i].name === category) return true;
64567 }
64568
64569 // check trace modules (including non-visible:true)
64570 var modules = this._modules || [];
64571 for(i = 0; i < modules.length; i++) {
64572 var name = modules[i].name;
64573 if(name === category) return true;
64574 // N.B. this is modules[i] along with 'categories' as a hash object
64575 var _module = Registry.modules[name];
64576 if(_module && _module.categories[category]) return true;
64577 }
64578
64579 return false;
64580};
64581
64582plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
64583 var i, j;
64584
64585 var basePlotModules = oldFullLayout._basePlotModules || [];
64586 for(i = 0; i < basePlotModules.length; i++) {
64587 var _module = basePlotModules[i];
64588
64589 if(_module.clean) {
64590 _module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
64591 }
64592 }
64593
64594 var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
64595 var hasGl = newFullLayout._has && newFullLayout._has('gl');
64596
64597 if(hadGl && !hasGl) {
64598 if(oldFullLayout._glcontainer !== undefined) {
64599 oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
64600 oldFullLayout._glcontainer.selectAll('.no-webgl').remove();
64601 oldFullLayout._glcanvas = null;
64602 }
64603 }
64604
64605 var hasInfoLayer = !!oldFullLayout._infolayer;
64606
64607 oldLoop:
64608 for(i = 0; i < oldFullData.length; i++) {
64609 var oldTrace = oldFullData[i];
64610 var oldUid = oldTrace.uid;
64611
64612 for(j = 0; j < newFullData.length; j++) {
64613 var newTrace = newFullData[j];
64614
64615 if(oldUid === newTrace.uid) continue oldLoop;
64616 }
64617
64618 // clean old colorbars
64619 if(hasInfoLayer) {
64620 oldFullLayout._infolayer.select('.cb' + oldUid).remove();
64621 }
64622 }
64623};
64624
64625plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
64626 var i, j;
64627
64628 var oldSubplots = oldFullLayout._plots || {};
64629 var newSubplots = newFullLayout._plots = {};
64630 var newSubplotList = newFullLayout._subplots;
64631
64632 var mockGd = {
64633 _fullData: newFullData,
64634 _fullLayout: newFullLayout
64635 };
64636
64637 var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []);
64638
64639 for(i = 0; i < ids.length; i++) {
64640 var id = ids[i];
64641 var oldSubplot = oldSubplots[id];
64642 var xaxis = axisIDs.getFromId(mockGd, id, 'x');
64643 var yaxis = axisIDs.getFromId(mockGd, id, 'y');
64644 var plotinfo;
64645
64646 // link or create subplot object
64647 if(oldSubplot) {
64648 plotinfo = newSubplots[id] = oldSubplot;
64649 } else {
64650 plotinfo = newSubplots[id] = {};
64651 plotinfo.id = id;
64652 }
64653
64654 // add these axis ids to each others' subplot lists
64655 xaxis._counterAxes.push(yaxis._id);
64656 yaxis._counterAxes.push(xaxis._id);
64657 xaxis._subplotsWith.push(id);
64658 yaxis._subplotsWith.push(id);
64659
64660 // update x and y axis layout object refs
64661 plotinfo.xaxis = xaxis;
64662 plotinfo.yaxis = yaxis;
64663
64664 // By default, we clip at the subplot level,
64665 // but if one trace on a given subplot has *cliponaxis* set to false,
64666 // we need to clip at the trace module layer level;
64667 // find this out here, once of for all.
64668 plotinfo._hasClipOnAxisFalse = false;
64669
64670 for(j = 0; j < newFullData.length; j++) {
64671 var trace = newFullData[j];
64672
64673 if(
64674 trace.xaxis === plotinfo.xaxis._id &&
64675 trace.yaxis === plotinfo.yaxis._id &&
64676 trace.cliponaxis === false
64677 ) {
64678 plotinfo._hasClipOnAxisFalse = true;
64679 break;
64680 }
64681 }
64682 }
64683
64684 // while we're at it, link overlaying axes to their main axes and
64685 // anchored axes to the axes they're anchored to
64686 var axList = axisIDs.list(mockGd, null, true);
64687 var ax;
64688 for(i = 0; i < axList.length; i++) {
64689 ax = axList[i];
64690 var mainAx = null;
64691
64692 if(ax.overlaying) {
64693 mainAx = axisIDs.getFromId(mockGd, ax.overlaying);
64694
64695 // you cannot overlay an axis that's already overlaying another
64696 if(mainAx && mainAx.overlaying) {
64697 ax.overlaying = false;
64698 mainAx = null;
64699 }
64700 }
64701 ax._mainAxis = mainAx || ax;
64702
64703 /*
64704 * For now force overlays to overlay completely... so they
64705 * can drag together correctly and share backgrounds.
64706 * Later perhaps we make separate axis domain and
64707 * tick/line domain or something, so they can still share
64708 * the (possibly larger) dragger and background but don't
64709 * have to both be drawn over that whole domain
64710 */
64711 if(mainAx) ax.domain = mainAx.domain.slice();
64712
64713 ax._anchorAxis = ax.anchor === 'free' ?
64714 null :
64715 axisIDs.getFromId(mockGd, ax.anchor);
64716 }
64717
64718 // finally, we can find the main subplot for each axis
64719 // (on which the ticks & labels are drawn)
64720 for(i = 0; i < axList.length; i++) {
64721 ax = axList[i];
64722 ax._counterAxes.sort(axisIDs.idSort);
64723 ax._subplotsWith.sort(Lib.subplotSort);
64724 ax._mainSubplot = findMainSubplot(ax, newFullLayout);
64725
64726 // find "full" domain span of counter axes,
64727 // this loop can be costly, so only compute it when required
64728 if(ax._counterAxes.length && (
64729 (ax.spikemode && ax.spikemode.indexOf('across') !== -1) ||
64730 (ax.automargin && ax.mirror && ax.anchor !== 'free') ||
64731 Registry.getComponentMethod('rangeslider', 'isVisible')(ax)
64732 )) {
64733 var min = 1;
64734 var max = 0;
64735 for(j = 0; j < ax._counterAxes.length; j++) {
64736 var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]);
64737 min = Math.min(min, ax2.domain[0]);
64738 max = Math.max(max, ax2.domain[1]);
64739 }
64740 if(min < max) {
64741 ax._counterDomainMin = min;
64742 ax._counterDomainMax = max;
64743 }
64744 }
64745 }
64746};
64747
64748function findMainSubplot(ax, fullLayout) {
64749 var mockGd = {_fullLayout: fullLayout};
64750
64751 var isX = ax._id.charAt(0) === 'x';
64752 var anchorAx = ax._mainAxis._anchorAxis;
64753 var mainSubplotID = '';
64754 var nextBestMainSubplotID = '';
64755 var anchorID = '';
64756
64757 // First try the main ID with the anchor
64758 if(anchorAx) {
64759 anchorID = anchorAx._mainAxis._id;
64760 mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id);
64761 }
64762
64763 // Then look for a subplot with the counteraxis overlaying the anchor
64764 // If that fails just use the first subplot including this axis
64765 if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) {
64766 mainSubplotID = '';
64767
64768 var counterIDs = ax._counterAxes;
64769 for(var j = 0; j < counterIDs.length; j++) {
64770 var counterPart = counterIDs[j];
64771 var id = isX ? (ax._id + counterPart) : (counterPart + ax._id);
64772 if(!nextBestMainSubplotID) nextBestMainSubplotID = id;
64773 var counterAx = axisIDs.getFromId(mockGd, counterPart);
64774 if(anchorID && counterAx.overlaying === anchorID) {
64775 mainSubplotID = id;
64776 break;
64777 }
64778 }
64779 }
64780
64781 return mainSubplotID || nextBestMainSubplotID;
64782}
64783
64784// This function clears any trace attributes with valType: color and
64785// no set dflt filed in the plot schema. This is needed because groupby (which
64786// is the only transform for which this currently applies) supplies parent
64787// trace defaults, then expanded trace defaults. The result is that `null`
64788// colors are default-supplied and inherited as a color instead of a null.
64789// The result is that expanded trace default colors have no effect, with
64790// the final result that groups are indistinguishable. This function clears
64791// those colors so that individual groupby groups get unique colors.
64792plots.clearExpandedTraceDefaultColors = function(trace) {
64793 var colorAttrs, path, i;
64794
64795 // This uses weird closure state in order to satisfy the linter rule
64796 // that we can't create functions in a loop.
64797 function locateColorAttrs(attr, attrName, attrs, level) {
64798 path[level] = attrName;
64799 path.length = level + 1;
64800 if(attr.valType === 'color' && attr.dflt === undefined) {
64801 colorAttrs.push(path.join('.'));
64802 }
64803 }
64804
64805 path = [];
64806
64807 // Get the cached colorAttrs:
64808 colorAttrs = trace._module._colorAttrs;
64809
64810 // Or else compute and cache the colorAttrs on the module:
64811 if(!colorAttrs) {
64812 trace._module._colorAttrs = colorAttrs = [];
64813 PlotSchema.crawl(
64814 trace._module.attributes,
64815 locateColorAttrs
64816 );
64817 }
64818
64819 for(i = 0; i < colorAttrs.length; i++) {
64820 var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]);
64821
64822 if(!origprop.get()) {
64823 Lib.nestedProperty(trace, colorAttrs[i]).set(null);
64824 }
64825 }
64826};
64827
64828
64829plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
64830 var modules = fullLayout._modules;
64831 var visibleModules = fullLayout._visibleModules;
64832 var basePlotModules = fullLayout._basePlotModules;
64833 var cnt = 0;
64834 var colorCnt = 0;
64835
64836 var i, fullTrace, trace;
64837
64838 fullLayout._transformModules = [];
64839
64840 function pushModule(fullTrace) {
64841 dataOut.push(fullTrace);
64842
64843 var _module = fullTrace._module;
64844 if(!_module) return;
64845
64846 Lib.pushUnique(modules, _module);
64847 if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module);
64848 Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
64849 cnt++;
64850
64851 // TODO: do we really want color not to increment for explicitly invisible traces?
64852 // This logic is weird, but matches previous behavior: traces that you explicitly
64853 // set to visible:false do not increment the color, but traces WE determine to be
64854 // empty or invalid (and thus set to visible:false) DO increment color.
64855 // I kind of think we should just let all traces increment color, visible or not.
64856 // see mock: axes-autotype-empty vs. a test of restyling visible: false that
64857 // I can't find right now...
64858 if(fullTrace._input.visible !== false) colorCnt++;
64859 }
64860
64861 var carpetIndex = {};
64862 var carpetDependents = [];
64863 var dataTemplate = (layout.template || {}).data || {};
64864 var templater = Template.traceTemplater(dataTemplate);
64865
64866 for(i = 0; i < dataIn.length; i++) {
64867 trace = dataIn[i];
64868
64869 // reuse uid we may have pulled out of oldFullData
64870 // Note: templater supplies trace type
64871 fullTrace = templater.newTrace(trace);
64872 fullTrace.uid = fullLayout._traceUids[i];
64873 plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);
64874
64875 fullTrace.index = i;
64876 fullTrace._input = trace;
64877 fullTrace._expandedIndex = cnt;
64878
64879 if(fullTrace.transforms && fullTrace.transforms.length) {
64880 var sdInvisible = trace.visible !== false && fullTrace.visible === false;
64881
64882 var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
64883
64884 for(var j = 0; j < expandedTraces.length; j++) {
64885 var expandedTrace = expandedTraces[j];
64886
64887 // No further templating during transforms.
64888 var fullExpandedTrace = {
64889 _template: fullTrace._template,
64890 type: fullTrace.type,
64891 // set uid using parent uid and expanded index
64892 // to promote consistency between update calls
64893 uid: fullTrace.uid + j
64894 };
64895
64896 // If the first supplyDefaults created `visible: false`,
64897 // clear it before running supplyDefaults a second time,
64898 // because sometimes there are items we still want to coerce
64899 // inside trace modules before determining that the trace is
64900 // again `visible: false`, for example partial visibilities
64901 // in `splom` traces.
64902 if(sdInvisible && expandedTrace.visible === false) {
64903 delete expandedTrace.visible;
64904 }
64905
64906 plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i);
64907
64908 // relink private (i.e. underscore) keys expanded trace to full expanded trace so
64909 // that transform supply-default methods can set _ keys for future use.
64910 relinkPrivateKeys(fullExpandedTrace, expandedTrace);
64911
64912 // add info about parent data trace
64913 fullExpandedTrace.index = i;
64914 fullExpandedTrace._input = trace;
64915 fullExpandedTrace._fullInput = fullTrace;
64916
64917 // add info about the expanded data
64918 fullExpandedTrace._expandedIndex = cnt;
64919 fullExpandedTrace._expandedInput = expandedTrace;
64920
64921 pushModule(fullExpandedTrace);
64922 }
64923 } else {
64924 // add identify refs for consistency with transformed traces
64925 fullTrace._fullInput = fullTrace;
64926 fullTrace._expandedInput = fullTrace;
64927
64928 pushModule(fullTrace);
64929 }
64930
64931 if(Registry.traceIs(fullTrace, 'carpetAxis')) {
64932 carpetIndex[fullTrace.carpet] = fullTrace;
64933 }
64934
64935 if(Registry.traceIs(fullTrace, 'carpetDependent')) {
64936 carpetDependents.push(i);
64937 }
64938 }
64939
64940 for(i = 0; i < carpetDependents.length; i++) {
64941 fullTrace = dataOut[carpetDependents[i]];
64942
64943 if(!fullTrace.visible) continue;
64944
64945 var carpetAxis = carpetIndex[fullTrace.carpet];
64946 fullTrace._carpet = carpetAxis;
64947
64948 if(!carpetAxis || !carpetAxis.visible) {
64949 fullTrace.visible = false;
64950 continue;
64951 }
64952
64953 fullTrace.xaxis = carpetAxis.xaxis;
64954 fullTrace.yaxis = carpetAxis.yaxis;
64955 }
64956};
64957
64958plots.supplyAnimationDefaults = function(opts) {
64959 opts = opts || {};
64960 var i;
64961 var optsOut = {};
64962
64963 function coerce(attr, dflt) {
64964 return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt);
64965 }
64966
64967 coerce('mode');
64968 coerce('direction');
64969 coerce('fromcurrent');
64970
64971 if(Array.isArray(opts.frame)) {
64972 optsOut.frame = [];
64973 for(i = 0; i < opts.frame.length; i++) {
64974 optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
64975 }
64976 } else {
64977 optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
64978 }
64979
64980 if(Array.isArray(opts.transition)) {
64981 optsOut.transition = [];
64982 for(i = 0; i < opts.transition.length; i++) {
64983 optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {});
64984 }
64985 } else {
64986 optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
64987 }
64988
64989 return optsOut;
64990};
64991
64992plots.supplyAnimationFrameDefaults = function(opts) {
64993 var optsOut = {};
64994
64995 function coerce(attr, dflt) {
64996 return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt);
64997 }
64998
64999 coerce('duration');
65000 coerce('redraw');
65001
65002 return optsOut;
65003};
65004
65005plots.supplyAnimationTransitionDefaults = function(opts) {
65006 var optsOut = {};
65007
65008 function coerce(attr, dflt) {
65009 return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt);
65010 }
65011
65012 coerce('duration');
65013 coerce('easing');
65014
65015 return optsOut;
65016};
65017
65018plots.supplyFrameDefaults = function(frameIn) {
65019 var frameOut = {};
65020
65021 function coerce(attr, dflt) {
65022 return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt);
65023 }
65024
65025 coerce('group');
65026 coerce('name');
65027 coerce('traces');
65028 coerce('baseframe');
65029 coerce('data');
65030 coerce('layout');
65031
65032 return frameOut;
65033};
65034
65035plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) {
65036 var colorway = layout.colorway || Color.defaults;
65037 var defaultColor = colorway[colorIndex % colorway.length];
65038
65039 var i;
65040
65041 function coerce(attr, dflt) {
65042 return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt);
65043 }
65044
65045 var visible = coerce('visible');
65046
65047 coerce('type');
65048 coerce('name', layout._traceWord + ' ' + traceInIndex);
65049
65050 coerce('uirevision', layout.uirevision);
65051
65052 // we want even invisible traces to make their would-be subplots visible
65053 // so coerce the subplot id(s) now no matter what
65054 var _module = plots.getModule(traceOut);
65055
65056 traceOut._module = _module;
65057 if(_module) {
65058 var basePlotModule = _module.basePlotModule;
65059 var subplotAttr = basePlotModule.attr;
65060 var subplotAttrs = basePlotModule.attributes;
65061 if(subplotAttr && subplotAttrs) {
65062 var subplots = layout._subplots;
65063 var subplotId = '';
65064
65065 if(
65066 visible ||
65067 basePlotModule.name !== 'gl2d' // for now just drop empty gl2d subplots
65068 // TODO - currently if we draw an empty gl2d subplot, it draws
65069 // nothing then gets stuck and you can't get it back without newPlot
65070 // sort this out in the regl refactor?
65071 ) {
65072 if(Array.isArray(subplotAttr)) {
65073 for(i = 0; i < subplotAttr.length; i++) {
65074 var attri = subplotAttr[i];
65075 var vali = Lib.coerce(traceIn, traceOut, subplotAttrs, attri);
65076
65077 if(subplots[attri]) Lib.pushUnique(subplots[attri], vali);
65078 subplotId += vali;
65079 }
65080 } else {
65081 subplotId = Lib.coerce(traceIn, traceOut, subplotAttrs, subplotAttr);
65082 }
65083
65084 if(subplots[basePlotModule.name]) {
65085 Lib.pushUnique(subplots[basePlotModule.name], subplotId);
65086 }
65087 }
65088 }
65089 }
65090
65091 if(visible) {
65092 coerce('customdata');
65093 coerce('ids');
65094 coerce('meta');
65095
65096 if(Registry.traceIs(traceOut, 'showLegend')) {
65097 Lib.coerce(traceIn, traceOut,
65098 _module.attributes.showlegend ? _module.attributes : plots.attributes,
65099 'showlegend'
65100 );
65101
65102 coerce('legendgroup');
65103
65104 traceOut._dfltShowLegend = true;
65105 } else {
65106 traceOut._dfltShowLegend = false;
65107 }
65108
65109 if(_module) {
65110 _module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
65111 }
65112
65113 if(!Registry.traceIs(traceOut, 'noOpacity')) {
65114 coerce('opacity');
65115 }
65116
65117 if(Registry.traceIs(traceOut, 'notLegendIsolatable')) {
65118 // This clears out the legendonly state for traces like carpet that
65119 // cannot be isolated in the legend
65120 traceOut.visible = !!traceOut.visible;
65121 }
65122
65123 if(!Registry.traceIs(traceOut, 'noHover')) {
65124 if(!traceOut.hovertemplate) Lib.coerceHoverinfo(traceIn, traceOut, layout);
65125
65126 // parcats support hover, but not hoverlabel stylings (yet)
65127 if(traceOut.type !== 'parcats') {
65128 Registry.getComponentMethod('fx', 'supplyDefaults')(traceIn, traceOut, defaultColor, layout);
65129 }
65130 }
65131
65132 if(_module && _module.selectPoints) {
65133 coerce('selectedpoints');
65134 }
65135
65136 plots.supplyTransformDefaults(traceIn, traceOut, layout);
65137 }
65138
65139 return traceOut;
65140};
65141
65142/**
65143 * hasMakesDataTransform: does this trace have a transform that makes its own
65144 * data, either by grabbing it from somewhere else or by creating it from input
65145 * parameters? If so, we should still keep going with supplyDefaults
65146 * even if the trace is invisible, which may just be because it has no data yet.
65147 */
65148function hasMakesDataTransform(trace) {
65149 var transforms = trace.transforms;
65150 if(Array.isArray(transforms) && transforms.length) {
65151 for(var i = 0; i < transforms.length; i++) {
65152 var ti = transforms[i];
65153 var _module = ti._module || transformsRegistry[ti.type];
65154 if(_module && _module.makesData) return true;
65155 }
65156 }
65157 return false;
65158}
65159
65160plots.hasMakesDataTransform = hasMakesDataTransform;
65161
65162plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
65163 // For now we only allow transforms on 1D traces, ie those that specify a _length.
65164 // If we were to implement 2D transforms, we'd need to have each transform
65165 // describe its own applicability and disable itself when it doesn't apply.
65166 // Also allow transforms that make their own data, but not in globalTransforms
65167 if(!(traceOut._length || hasMakesDataTransform(traceIn))) return;
65168
65169 var globalTransforms = layout._globalTransforms || [];
65170 var transformModules = layout._transformModules || [];
65171
65172 if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
65173
65174 var containerIn = traceIn.transforms || [];
65175 var transformList = globalTransforms.concat(containerIn);
65176 var containerOut = traceOut.transforms = [];
65177
65178 for(var i = 0; i < transformList.length; i++) {
65179 var transformIn = transformList[i];
65180 var type = transformIn.type;
65181 var _module = transformsRegistry[type];
65182 var transformOut;
65183
65184 /*
65185 * Supply defaults may run twice. First pass runs all supply defaults steps
65186 * and adds the _module to any output transforms.
65187 * If transforms exist another pass is run so that any generated traces also
65188 * go through supply defaults. This has the effect of rerunning
65189 * supplyTransformDefaults. If the transform does not have a `transform`
65190 * function it could not have generated any new traces and the second stage
65191 * is unnecessary. We detect this case with the following variables.
65192 */
65193 var isFirstStage = !(transformIn._module && transformIn._module === _module);
65194 var doLaterStages = _module && typeof _module.transform === 'function';
65195
65196 if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
65197
65198 if(_module && _module.supplyDefaults && (isFirstStage || doLaterStages)) {
65199 transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
65200 transformOut.type = type;
65201 transformOut._module = _module;
65202
65203 Lib.pushUnique(transformModules, _module);
65204 } else {
65205 transformOut = Lib.extendFlat({}, transformIn);
65206 }
65207
65208 containerOut.push(transformOut);
65209 }
65210};
65211
65212function applyTransforms(fullTrace, fullData, layout, fullLayout) {
65213 var container = fullTrace.transforms;
65214 var dataOut = [fullTrace];
65215
65216 for(var i = 0; i < container.length; i++) {
65217 var transform = container[i];
65218 var _module = transformsRegistry[transform.type];
65219
65220 if(_module && _module.transform) {
65221 dataOut = _module.transform(dataOut, {
65222 transform: transform,
65223 fullTrace: fullTrace,
65224 fullData: fullData,
65225 layout: layout,
65226 fullLayout: fullLayout,
65227 transformIndex: i
65228 });
65229 }
65230 }
65231
65232 return dataOut;
65233}
65234
65235plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
65236 function coerce(attr, dflt) {
65237 return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
65238 }
65239
65240 var template = layoutIn.template;
65241 if(Lib.isPlainObject(template)) {
65242 layoutOut.template = template;
65243 layoutOut._template = template.layout;
65244 layoutOut._dataTemplate = template.data;
65245 }
65246
65247 var globalFont = Lib.coerceFont(coerce, 'font');
65248
65249 coerce('title.text', layoutOut._dfltTitle.plot);
65250
65251 Lib.coerceFont(coerce, 'title.font', {
65252 family: globalFont.family,
65253 size: Math.round(globalFont.size * 1.4),
65254 color: globalFont.color
65255 });
65256
65257 coerce('title.xref');
65258 coerce('title.yref');
65259 coerce('title.x');
65260 coerce('title.y');
65261 coerce('title.xanchor');
65262 coerce('title.yanchor');
65263 coerce('title.pad.t');
65264 coerce('title.pad.r');
65265 coerce('title.pad.b');
65266 coerce('title.pad.l');
65267
65268 var uniformtextMode = coerce('uniformtext.mode');
65269 if(uniformtextMode) {
65270 coerce('uniformtext.minsize');
65271 }
65272
65273 // Make sure that autosize is defaulted to *true*
65274 // on layouts with no set width and height for backward compatibly,
65275 // in particular https://plotly.com/javascript/responsive-fluid-layout/
65276 //
65277 // Before https://github.com/plotly/plotly.js/pull/635 ,
65278 // layouts with no set width and height were set temporary set to 'initial'
65279 // to pass through the autosize routine
65280 //
65281 // This behavior is subject to change in v2.
65282 coerce('autosize', !(layoutIn.width && layoutIn.height));
65283
65284 coerce('width');
65285 coerce('height');
65286 coerce('margin.l');
65287 coerce('margin.r');
65288 coerce('margin.t');
65289 coerce('margin.b');
65290 coerce('margin.pad');
65291 coerce('margin.autoexpand');
65292
65293 if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut);
65294
65295 Registry.getComponentMethod('grid', 'sizeDefaults')(layoutIn, layoutOut);
65296
65297 coerce('paper_bgcolor');
65298
65299 coerce('separators', formatObj.decimal + formatObj.thousands);
65300 coerce('hidesources');
65301
65302 coerce('colorway');
65303
65304 coerce('datarevision');
65305 var uirevision = coerce('uirevision');
65306 coerce('editrevision', uirevision);
65307 coerce('selectionrevision', uirevision);
65308
65309 coerce('modebar.orientation');
65310 coerce('modebar.bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5));
65311 var modebarDefaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor));
65312 coerce('modebar.color', Color.addOpacity(modebarDefaultColor, 0.3));
65313 coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7));
65314 coerce('modebar.uirevision', uirevision);
65315
65316 Registry.getComponentMethod(
65317 'shapes',
65318 'supplyDrawNewShapeDefaults'
65319 )(layoutIn, layoutOut, coerce);
65320
65321 coerce('meta');
65322
65323 // do not include defaults in fullLayout when users do not set transition
65324 if(Lib.isPlainObject(layoutIn.transition)) {
65325 coerce('transition.duration');
65326 coerce('transition.easing');
65327 coerce('transition.ordering');
65328 }
65329
65330 Registry.getComponentMethod(
65331 'calendars',
65332 'handleDefaults'
65333 )(layoutIn, layoutOut, 'calendar');
65334
65335 Registry.getComponentMethod(
65336 'fx',
65337 'supplyLayoutGlobalDefaults'
65338 )(layoutIn, layoutOut, coerce);
65339};
65340
65341plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) {
65342 var context = gd._context || {};
65343 var frameMargins = context.frameMargins;
65344 var newWidth;
65345 var newHeight;
65346
65347 var isPlotDiv = Lib.isPlotDiv(gd);
65348
65349 if(isPlotDiv) gd.emit('plotly_autosize');
65350
65351 // embedded in an iframe - just take the full iframe size
65352 // if we get to this point, with no aspect ratio restrictions
65353 if(context.fillFrame) {
65354 newWidth = window.innerWidth;
65355 newHeight = window.innerHeight;
65356
65357 // somehow we get a few extra px height sometimes...
65358 // just hide it
65359 document.body.style.overflow = 'hidden';
65360 } else {
65361 // plotly.js - let the developers do what they want, either
65362 // provide height and width for the container div,
65363 // specify size in layout, or take the defaults,
65364 // but don't enforce any ratio restrictions
65365 var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {};
65366
65367 newWidth = parseFloat(computedStyle.width) || parseFloat(computedStyle.maxWidth) || fullLayout.width;
65368 newHeight = parseFloat(computedStyle.height) || parseFloat(computedStyle.maxHeight) || fullLayout.height;
65369
65370 if(isNumeric(frameMargins) && frameMargins > 0) {
65371 var factor = 1 - 2 * frameMargins;
65372 newWidth = Math.round(factor * newWidth);
65373 newHeight = Math.round(factor * newHeight);
65374 }
65375 }
65376
65377 var minWidth = plots.layoutAttributes.width.min;
65378 var minHeight = plots.layoutAttributes.height.min;
65379 if(newWidth < minWidth) newWidth = minWidth;
65380 if(newHeight < minHeight) newHeight = minHeight;
65381
65382 var widthHasChanged = !layout.width &&
65383 (Math.abs(fullLayout.width - newWidth) > 1);
65384 var heightHasChanged = !layout.height &&
65385 (Math.abs(fullLayout.height - newHeight) > 1);
65386
65387 if(heightHasChanged || widthHasChanged) {
65388 if(widthHasChanged) fullLayout.width = newWidth;
65389 if(heightHasChanged) fullLayout.height = newHeight;
65390 }
65391
65392 // cache initial autosize value, used in relayout when
65393 // width or height values are set to null
65394 if(!gd._initialAutoSize) {
65395 gd._initialAutoSize = { width: newWidth, height: newHeight };
65396 }
65397
65398 plots.sanitizeMargins(fullLayout);
65399};
65400
65401plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
65402 var componentsRegistry = Registry.componentsRegistry;
65403 var basePlotModules = layoutOut._basePlotModules;
65404 var component, i, _module;
65405
65406 var Cartesian = Registry.subplotsRegistry.cartesian;
65407
65408 // check if any components need to add more base plot modules
65409 // that weren't captured by traces
65410 for(component in componentsRegistry) {
65411 _module = componentsRegistry[component];
65412
65413 if(_module.includeBasePlot) {
65414 _module.includeBasePlot(layoutIn, layoutOut);
65415 }
65416 }
65417
65418 // make sure we *at least* have some cartesian axes
65419 if(!basePlotModules.length) {
65420 basePlotModules.push(Cartesian);
65421 }
65422
65423 // ensure all cartesian axes have at least one subplot
65424 if(layoutOut._has('cartesian')) {
65425 Registry.getComponentMethod('grid', 'contentDefaults')(layoutIn, layoutOut);
65426 Cartesian.finalizeSubplots(layoutIn, layoutOut);
65427 }
65428
65429 // sort subplot lists
65430 for(var subplotType in layoutOut._subplots) {
65431 layoutOut._subplots[subplotType].sort(Lib.subplotSort);
65432 }
65433
65434 // base plot module layout defaults
65435 for(i = 0; i < basePlotModules.length; i++) {
65436 _module = basePlotModules[i];
65437
65438 // e.g. pie does not have a layout-defaults step
65439 if(_module.supplyLayoutDefaults) {
65440 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
65441 }
65442 }
65443
65444 // trace module layout defaults
65445 // use _modules rather than _visibleModules so that even
65446 // legendonly traces can include settings - eg barmode, which affects
65447 // legend.traceorder default value.
65448 var modules = layoutOut._modules;
65449 for(i = 0; i < modules.length; i++) {
65450 _module = modules[i];
65451
65452 if(_module.supplyLayoutDefaults) {
65453 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
65454 }
65455 }
65456
65457 // transform module layout defaults
65458 var transformModules = layoutOut._transformModules;
65459 for(i = 0; i < transformModules.length; i++) {
65460 _module = transformModules[i];
65461
65462 if(_module.supplyLayoutDefaults) {
65463 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData);
65464 }
65465 }
65466
65467 for(component in componentsRegistry) {
65468 _module = componentsRegistry[component];
65469
65470 if(_module.supplyLayoutDefaults) {
65471 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
65472 }
65473 }
65474};
65475
65476// Remove all plotly attributes from a div so it can be replotted fresh
65477// TODO: these really need to be encapsulated into a much smaller set...
65478plots.purge = function(gd) {
65479 // note: we DO NOT remove _context because it doesn't change when we insert
65480 // a new plot, and may have been set outside of our scope.
65481
65482 var fullLayout = gd._fullLayout || {};
65483 if(fullLayout._glcontainer !== undefined) {
65484 fullLayout._glcontainer.selectAll('.gl-canvas').remove();
65485 fullLayout._glcontainer.remove();
65486 fullLayout._glcanvas = null;
65487 }
65488
65489 // remove modebar
65490 if(fullLayout._modeBar) fullLayout._modeBar.destroy();
65491
65492 if(gd._transitionData) {
65493 // Ensure any dangling callbacks are simply dropped if the plot is purged.
65494 // This is more or less only actually important for testing.
65495 if(gd._transitionData._interruptCallbacks) {
65496 gd._transitionData._interruptCallbacks.length = 0;
65497 }
65498
65499 if(gd._transitionData._animationRaf) {
65500 window.cancelAnimationFrame(gd._transitionData._animationRaf);
65501 }
65502 }
65503
65504 // remove any planned throttles
65505 Lib.clearThrottle();
65506
65507 // remove responsive handler
65508 Lib.clearResponsive(gd);
65509
65510 // data and layout
65511 delete gd.data;
65512 delete gd.layout;
65513 delete gd._fullData;
65514 delete gd._fullLayout;
65515 delete gd.calcdata;
65516 delete gd.framework;
65517 delete gd.empty;
65518
65519 delete gd.fid;
65520
65521 delete gd.undoqueue; // action queue
65522 delete gd.undonum;
65523 delete gd.autoplay; // are we doing an action that doesn't go in undo queue?
65524 delete gd.changed;
65525
65526 // these get recreated on Plotly.plot anyway, but just to be safe
65527 // (and to have a record of them...)
65528 delete gd._promises;
65529 delete gd._redrawTimer;
65530 delete gd._hmlumcount;
65531 delete gd._hmpixcount;
65532 delete gd._transitionData;
65533 delete gd._transitioning;
65534 delete gd._initialAutoSize;
65535 delete gd._transitioningWithDuration;
65536
65537 // created during certain events, that *should* clean them up
65538 // themselves, but may not if there was an error
65539 delete gd._dragging;
65540 delete gd._dragged;
65541 delete gd._dragdata;
65542 delete gd._hoverdata;
65543 delete gd._snapshotInProgress;
65544 delete gd._editing;
65545 delete gd._mouseDownTime;
65546 delete gd._legendMouseDownTime;
65547
65548 // remove all event listeners
65549 if(gd.removeAllListeners) gd.removeAllListeners();
65550};
65551
65552plots.style = function(gd) {
65553 var _modules = gd._fullLayout._visibleModules;
65554 var styleModules = [];
65555 var i;
65556
65557 // some trace modules reuse the same style method,
65558 // make sure to not unnecessary call them multiple times.
65559
65560 for(i = 0; i < _modules.length; i++) {
65561 var _module = _modules[i];
65562 if(_module.style) {
65563 Lib.pushUnique(styleModules, _module.style);
65564 }
65565 }
65566
65567 for(i = 0; i < styleModules.length; i++) {
65568 styleModules[i](gd);
65569 }
65570};
65571
65572plots.sanitizeMargins = function(fullLayout) {
65573 // polar doesn't do margins...
65574 if(!fullLayout || !fullLayout.margin) return;
65575
65576 var width = fullLayout.width;
65577 var height = fullLayout.height;
65578 var margin = fullLayout.margin;
65579 var plotWidth = width - (margin.l + margin.r);
65580 var plotHeight = height - (margin.t + margin.b);
65581 var correction;
65582
65583 // if margin.l + margin.r = 0 then plotWidth > 0
65584 // as width >= 10 by supplyDefaults
65585 // similarly for margin.t + margin.b
65586
65587 if(plotWidth < 0) {
65588 correction = (width - 1) / (margin.l + margin.r);
65589 margin.l = Math.floor(correction * margin.l);
65590 margin.r = Math.floor(correction * margin.r);
65591 }
65592
65593 if(plotHeight < 0) {
65594 correction = (height - 1) / (margin.t + margin.b);
65595 margin.t = Math.floor(correction * margin.t);
65596 margin.b = Math.floor(correction * margin.b);
65597 }
65598};
65599
65600plots.clearAutoMarginIds = function(gd) {
65601 gd._fullLayout._pushmarginIds = {};
65602};
65603
65604plots.allowAutoMargin = function(gd, id) {
65605 gd._fullLayout._pushmarginIds[id] = 1;
65606};
65607
65608function initMargins(fullLayout) {
65609 var margin = fullLayout.margin;
65610
65611 if(!fullLayout._size) {
65612 var gs = fullLayout._size = {
65613 l: Math.round(margin.l),
65614 r: Math.round(margin.r),
65615 t: Math.round(margin.t),
65616 b: Math.round(margin.b),
65617 p: Math.round(margin.pad)
65618 };
65619 gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
65620 gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
65621 }
65622 if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
65623 if(!fullLayout._pushmarginIds) fullLayout._pushmarginIds = {};
65624}
65625
65626/**
65627 * autoMargin: called by components that may need to expand the margins to
65628 * be rendered on-plot.
65629 *
65630 * @param {DOM element} gd
65631 * @param {string} id - an identifier unique (within this plot) to this object,
65632 * so we can remove a previous margin expansion from the same object.
65633 * @param {object} o - the margin requirements of this object, or omit to delete
65634 * this entry (like if it's hidden). Keys are:
65635 * x, y: plot fraction of the anchor point.
65636 * xl, xr, yt, yb: if the object has an extent defined in plot fraction,
65637 * you can specify both edges as plot fractions in each dimension
65638 * l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b]
65639 * pad: extra pixels to add in all directions, default 12 (why?)
65640 */
65641plots.autoMargin = function(gd, id, o) {
65642 var fullLayout = gd._fullLayout;
65643
65644 var pushMargin = fullLayout._pushmargin;
65645 var pushMarginIds = fullLayout._pushmarginIds;
65646
65647 if(fullLayout.margin.autoexpand !== false) {
65648 if(!o) {
65649 delete pushMargin[id];
65650 delete pushMarginIds[id];
65651 } else {
65652 var pad = o.pad;
65653 if(pad === undefined) {
65654 var margin = fullLayout.margin;
65655 // if no explicit pad is given, use 12px unless there's a
65656 // specified margin that's smaller than that
65657 pad = Math.min(12, margin.l, margin.r, margin.t, margin.b);
65658 }
65659
65660 // if the item is too big, just give it enough automargin to
65661 // make sure you can still grab it and bring it back
65662 if(o.l + o.r > fullLayout.width * 0.5) {
65663 Lib.log('Margin push', id, 'is too big in x, dropping');
65664 o.l = o.r = 0;
65665 }
65666 if(o.b + o.t > fullLayout.height * 0.5) {
65667 Lib.log('Margin push', id, 'is too big in y, dropping');
65668 o.b = o.t = 0;
65669 }
65670
65671 var xl = o.xl !== undefined ? o.xl : o.x;
65672 var xr = o.xr !== undefined ? o.xr : o.x;
65673 var yt = o.yt !== undefined ? o.yt : o.y;
65674 var yb = o.yb !== undefined ? o.yb : o.y;
65675
65676 pushMargin[id] = {
65677 l: {val: xl, size: o.l + pad},
65678 r: {val: xr, size: o.r + pad},
65679 b: {val: yb, size: o.b + pad},
65680 t: {val: yt, size: o.t + pad}
65681 };
65682 pushMarginIds[id] = 1;
65683 }
65684
65685 if(!fullLayout._replotting) {
65686 return plots.doAutoMargin(gd);
65687 }
65688 }
65689};
65690
65691plots.doAutoMargin = function(gd) {
65692 var fullLayout = gd._fullLayout;
65693 if(!fullLayout._size) fullLayout._size = {};
65694 initMargins(fullLayout);
65695
65696 var gs = fullLayout._size;
65697 var margin = fullLayout.margin;
65698 var oldMargins = Lib.extendFlat({}, gs);
65699
65700 // adjust margins for outside components
65701 // fullLayout.margin is the requested margin,
65702 // fullLayout._size has margins and plotsize after adjustment
65703 var ml = margin.l;
65704 var mr = margin.r;
65705 var mt = margin.t;
65706 var mb = margin.b;
65707 var width = fullLayout.width;
65708 var height = fullLayout.height;
65709 var pushMargin = fullLayout._pushmargin;
65710 var pushMarginIds = fullLayout._pushmarginIds;
65711
65712 if(fullLayout.margin.autoexpand !== false) {
65713 for(var k in pushMargin) {
65714 if(!pushMarginIds[k]) delete pushMargin[k];
65715 }
65716
65717 // fill in the requested margins
65718 pushMargin.base = {
65719 l: {val: 0, size: ml},
65720 r: {val: 1, size: mr},
65721 t: {val: 1, size: mt},
65722 b: {val: 0, size: mb}
65723 };
65724
65725 // now cycle through all the combinations of l and r
65726 // (and t and b) to find the required margins
65727
65728 for(var k1 in pushMargin) {
65729 var pushleft = pushMargin[k1].l || {};
65730 var pushbottom = pushMargin[k1].b || {};
65731 var fl = pushleft.val;
65732 var pl = pushleft.size;
65733 var fb = pushbottom.val;
65734 var pb = pushbottom.size;
65735
65736 for(var k2 in pushMargin) {
65737 if(isNumeric(pl) && pushMargin[k2].r) {
65738 var fr = pushMargin[k2].r.val;
65739 var pr = pushMargin[k2].r.size;
65740
65741 if(fr > fl) {
65742 var newL = (pl * fr + (pr - width) * fl) / (fr - fl);
65743 var newR = (pr * (1 - fl) + (pl - width) * (1 - fr)) / (fr - fl);
65744 if(newL >= 0 && newR >= 0 && width - (newL + newR) > 0 && newL + newR > ml + mr) {
65745 ml = newL;
65746 mr = newR;
65747 }
65748 }
65749 }
65750
65751 if(isNumeric(pb) && pushMargin[k2].t) {
65752 var ft = pushMargin[k2].t.val;
65753 var pt = pushMargin[k2].t.size;
65754
65755 if(ft > fb) {
65756 var newB = (pb * ft + (pt - height) * fb) / (ft - fb);
65757 var newT = (pt * (1 - fb) + (pb - height) * (1 - ft)) / (ft - fb);
65758 if(newB >= 0 && newT >= 0 && height - (newT + newB) > 0 && newB + newT > mb + mt) {
65759 mb = newB;
65760 mt = newT;
65761 }
65762 }
65763 }
65764 }
65765 }
65766 }
65767
65768 gs.l = Math.round(ml);
65769 gs.r = Math.round(mr);
65770 gs.t = Math.round(mt);
65771 gs.b = Math.round(mb);
65772 gs.p = Math.round(margin.pad);
65773 gs.w = Math.round(width) - gs.l - gs.r;
65774 gs.h = Math.round(height) - gs.t - gs.b;
65775
65776 // if things changed and we're not already redrawing, trigger a redraw
65777 if(!fullLayout._replotting && plots.didMarginChange(oldMargins, gs)) {
65778 if('_redrawFromAutoMarginCount' in fullLayout) {
65779 fullLayout._redrawFromAutoMarginCount++;
65780 } else {
65781 fullLayout._redrawFromAutoMarginCount = 1;
65782 }
65783
65784 // Always allow at least one redraw and give each margin-push
65785 // call 3 loops to converge. Of course, for most cases this way too many,
65786 // but let's keep things on the safe side until we fix our
65787 // auto-margin pipeline problems:
65788 // https://github.com/plotly/plotly.js/issues/2704
65789 var maxNumberOfRedraws = 3 * (1 + Object.keys(pushMarginIds).length);
65790
65791 if(fullLayout._redrawFromAutoMarginCount < maxNumberOfRedraws) {
65792 return Registry.call('plot', gd);
65793 } else {
65794 Lib.warn('Too many auto-margin redraws.');
65795 }
65796 }
65797};
65798
65799var marginKeys = ['l', 'r', 't', 'b', 'p', 'w', 'h'];
65800
65801plots.didMarginChange = function(margin0, margin1) {
65802 for(var i = 0; i < marginKeys.length; i++) {
65803 var k = marginKeys[i];
65804 var m0 = margin0[k];
65805 var m1 = margin1[k];
65806 // use 1px tolerance in case we old/new differ only
65807 // by rounding errors, which can lead to infinite loops
65808 if(!isNumeric(m0) || Math.abs(m1 - m0) > 1) {
65809 return true;
65810 }
65811 }
65812 return false;
65813};
65814
65815/**
65816 * JSONify the graph data and layout
65817 *
65818 * This function needs to recurse because some src can be inside
65819 * sub-objects.
65820 *
65821 * It also strips out functions and private (starts with _) elements.
65822 * Therefore, we can add temporary things to data and layout that don't
65823 * get saved.
65824 *
65825 * @param gd The graphDiv
65826 * @param {Boolean} dataonly If true, don't return layout.
65827 * @param {'keepref'|'keepdata'|'keepall'} [mode='keepref'] Filter what's kept
65828 * keepref: remove data for which there's a src present
65829 * eg if there's xsrc present (and xsrc is well-formed,
65830 * ie has : and some chars before it), strip out x
65831 * keepdata: remove all src tags, don't remove the data itself
65832 * keepall: keep data and src
65833 * @param {String} output If you specify 'object', the result will not be stringified
65834 * @param {Boolean} useDefaults If truthy, use _fullLayout and _fullData
65835 * @param {Boolean} includeConfig If truthy, include _context
65836 * @returns {Object|String}
65837 */
65838plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfig) {
65839 // if the defaults aren't supplied yet, we need to do that...
65840 if((useDefaults && dataonly && !gd._fullData) ||
65841 (useDefaults && !dataonly && !gd._fullLayout)) {
65842 plots.supplyDefaults(gd);
65843 }
65844
65845 var data = (useDefaults) ? gd._fullData : gd.data;
65846 var layout = (useDefaults) ? gd._fullLayout : gd.layout;
65847 var frames = (gd._transitionData || {})._frames;
65848
65849 function stripObj(d, keepFunction) {
65850 if(typeof d === 'function') {
65851 return keepFunction ? '_function_' : null;
65852 }
65853 if(Lib.isPlainObject(d)) {
65854 var o = {};
65855 var src;
65856 Object.keys(d).sort().forEach(function(v) {
65857 // remove private elements and functions
65858 // _ is for private, [ is a mistake ie [object Object]
65859 if(['_', '['].indexOf(v.charAt(0)) !== -1) return;
65860
65861 // if a function, add if necessary then move on
65862 if(typeof d[v] === 'function') {
65863 if(keepFunction) o[v] = '_function';
65864 return;
65865 }
65866
65867 // look for src/data matches and remove the appropriate one
65868 if(mode === 'keepdata') {
65869 // keepdata: remove all ...src tags
65870 if(v.substr(v.length - 3) === 'src') {
65871 return;
65872 }
65873 } else if(mode === 'keepstream') {
65874 // keep sourced data if it's being streamed.
65875 // similar to keepref, but if the 'stream' object exists
65876 // in a trace, we will keep the data array.
65877 src = d[v + 'src'];
65878 if(typeof src === 'string' && src.indexOf(':') > 0) {
65879 if(!Lib.isPlainObject(d.stream)) {
65880 return;
65881 }
65882 }
65883 } else if(mode !== 'keepall') {
65884 // keepref: remove sourced data but only
65885 // if the source tag is well-formed
65886 src = d[v + 'src'];
65887 if(typeof src === 'string' && src.indexOf(':') > 0) {
65888 return;
65889 }
65890 }
65891
65892 // OK, we're including this... recurse into it
65893 o[v] = stripObj(d[v], keepFunction);
65894 });
65895 return o;
65896 }
65897
65898 if(Array.isArray(d)) {
65899 return d.map(function(x) {return stripObj(x, keepFunction);});
65900 }
65901
65902 if(Lib.isTypedArray(d)) {
65903 return Lib.simpleMap(d, Lib.identity);
65904 }
65905
65906 // convert native dates to date strings...
65907 // mostly for external users exporting to plotly
65908 if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d);
65909
65910 return d;
65911 }
65912
65913 var obj = {
65914 data: (data || []).map(function(v) {
65915 var d = stripObj(v);
65916 // fit has some little arrays in it that don't contain data,
65917 // just fit params and meta
65918 if(dataonly) { delete d.fit; }
65919 return d;
65920 })
65921 };
65922 if(!dataonly) { obj.layout = stripObj(layout); }
65923
65924 if(gd.framework && gd.framework.isPolar) obj = gd.framework.getConfig();
65925
65926 if(frames) obj.frames = stripObj(frames);
65927
65928 if(includeConfig) obj.config = stripObj(gd._context, true);
65929
65930 return (output === 'object') ? obj : JSON.stringify(obj);
65931};
65932
65933/**
65934 * Modify a keyframe using a list of operations:
65935 *
65936 * @param {array of objects} operations
65937 * Sequence of operations to be performed on the keyframes
65938 */
65939plots.modifyFrames = function(gd, operations) {
65940 var i, op, frame;
65941 var _frames = gd._transitionData._frames;
65942 var _frameHash = gd._transitionData._frameHash;
65943
65944 for(i = 0; i < operations.length; i++) {
65945 op = operations[i];
65946
65947 switch(op.type) {
65948 // No reason this couldn't exist, but is currently unused/untested:
65949 /* case 'rename':
65950 frame = _frames[op.index];
65951 delete _frameHash[frame.name];
65952 _frameHash[op.name] = frame;
65953 frame.name = op.name;
65954 break;*/
65955 case 'replace':
65956 frame = op.value;
65957 var oldName = (_frames[op.index] || {}).name;
65958 var newName = frame.name;
65959 _frames[op.index] = _frameHash[newName] = frame;
65960
65961 if(newName !== oldName) {
65962 // If name has changed in addition to replacement, then update
65963 // the lookup table:
65964 delete _frameHash[oldName];
65965 _frameHash[newName] = frame;
65966 }
65967
65968 break;
65969 case 'insert':
65970 frame = op.value;
65971 _frameHash[frame.name] = frame;
65972 _frames.splice(op.index, 0, frame);
65973 break;
65974 case 'delete':
65975 frame = _frames[op.index];
65976 delete _frameHash[frame.name];
65977 _frames.splice(op.index, 1);
65978 break;
65979 }
65980 }
65981
65982 return Promise.resolve();
65983};
65984
65985/*
65986 * Compute a keyframe. Merge a keyframe into its base frame(s) and
65987 * expand properties.
65988 *
65989 * @param {object} frameLookup
65990 * An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
65991 * @param {string} frame
65992 * The name of the keyframe to be computed
65993 *
65994 * Returns: a new object with the merged content
65995 */
65996plots.computeFrame = function(gd, frameName) {
65997 var frameLookup = gd._transitionData._frameHash;
65998 var i, traceIndices, traceIndex, destIndex;
65999
66000 // Null or undefined will fail on .toString(). We'll allow numbers since we
66001 // make it clear frames must be given string names, but we'll allow numbers
66002 // here since they're otherwise fine for looking up frames as long as they're
66003 // properly cast to strings. We really just want to ensure here that this
66004 // 1) doesn't fail, and
66005 // 2) doens't give an incorrect answer (which String(frameName) would)
66006 if(!frameName) {
66007 throw new Error('computeFrame must be given a string frame name');
66008 }
66009
66010 var framePtr = frameLookup[frameName.toString()];
66011
66012 // Return false if the name is invalid:
66013 if(!framePtr) {
66014 return false;
66015 }
66016
66017 var frameStack = [framePtr];
66018 var frameNameStack = [framePtr.name];
66019
66020 // Follow frame pointers:
66021 while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) {
66022 // Avoid infinite loops:
66023 if(frameNameStack.indexOf(framePtr.name) !== -1) break;
66024
66025 frameStack.push(framePtr);
66026 frameNameStack.push(framePtr.name);
66027 }
66028
66029 // A new object for the merged result:
66030 var result = {};
66031
66032 // Merge, starting with the last and ending with the desired frame:
66033 while((framePtr = frameStack.pop())) {
66034 if(framePtr.layout) {
66035 result.layout = plots.extendLayout(result.layout, framePtr.layout);
66036 }
66037
66038 if(framePtr.data) {
66039 if(!result.data) {
66040 result.data = [];
66041 }
66042 traceIndices = framePtr.traces;
66043
66044 if(!traceIndices) {
66045 // If not defined, assume serial order starting at zero
66046 traceIndices = [];
66047 for(i = 0; i < framePtr.data.length; i++) {
66048 traceIndices[i] = i;
66049 }
66050 }
66051
66052 if(!result.traces) {
66053 result.traces = [];
66054 }
66055
66056 for(i = 0; i < framePtr.data.length; i++) {
66057 // Loop through this frames data, find out where it should go,
66058 // and merge it!
66059 traceIndex = traceIndices[i];
66060 if(traceIndex === undefined || traceIndex === null) {
66061 continue;
66062 }
66063
66064 destIndex = result.traces.indexOf(traceIndex);
66065 if(destIndex === -1) {
66066 destIndex = result.data.length;
66067 result.traces[destIndex] = traceIndex;
66068 }
66069
66070 result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]);
66071 }
66072 }
66073 }
66074
66075 return result;
66076};
66077
66078/*
66079 * Recompute the lookup table that maps frame name -> frame object. addFrames/
66080 * deleteFrames already manages this data one at a time, so the only time this
66081 * is necessary is if you poke around manually in `gd._transitionData._frames`
66082 * and create and haven't updated the lookup table.
66083 */
66084plots.recomputeFrameHash = function(gd) {
66085 var hash = gd._transitionData._frameHash = {};
66086 var frames = gd._transitionData._frames;
66087 for(var i = 0; i < frames.length; i++) {
66088 var frame = frames[i];
66089 if(frame && frame.name) {
66090 hash[frame.name] = frame;
66091 }
66092 }
66093};
66094
66095/**
66096 * Extend an object, treating container arrays very differently by extracting
66097 * their contents and merging them separately.
66098 *
66099 * This exists so that we can extendDeepNoArrays and avoid stepping into data
66100 * arrays without knowledge of the plot schema, but so that we may also manually
66101 * recurse into known container arrays, such as transforms.
66102 *
66103 * See extendTrace and extendLayout below for usage.
66104 */
66105plots.extendObjectWithContainers = function(dest, src, containerPaths) {
66106 var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer;
66107 var copy = Lib.extendDeepNoArrays({}, src || {});
66108 var expandedObj = Lib.expandObjectPaths(copy);
66109 var containerObj = {};
66110
66111 // Step through and extract any container properties. Otherwise extendDeepNoArrays
66112 // will clobber any existing properties with an empty array and then supplyDefaults
66113 // will reset everything to defaults.
66114 if(containerPaths && containerPaths.length) {
66115 for(i = 0; i < containerPaths.length; i++) {
66116 containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]);
66117 containerVal = containerProp.get();
66118
66119 if(containerVal === undefined) {
66120 Lib.nestedProperty(containerObj, containerPaths[i]).set(null);
66121 } else {
66122 containerProp.set(null);
66123 Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal);
66124 }
66125 }
66126 }
66127
66128 dest = Lib.extendDeepNoArrays(dest || {}, expandedObj);
66129
66130 if(containerPaths && containerPaths.length) {
66131 for(i = 0; i < containerPaths.length; i++) {
66132 srcProp = Lib.nestedProperty(containerObj, containerPaths[i]);
66133 srcContainer = srcProp.get();
66134
66135 if(!srcContainer) continue;
66136
66137 destProp = Lib.nestedProperty(dest, containerPaths[i]);
66138 destContainer = destProp.get();
66139
66140 if(!Array.isArray(destContainer)) {
66141 destContainer = [];
66142 destProp.set(destContainer);
66143 }
66144
66145 for(j = 0; j < srcContainer.length; j++) {
66146 var srcObj = srcContainer[j];
66147
66148 if(srcObj === null) destContainer[j] = null;
66149 else {
66150 destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj);
66151 }
66152 }
66153
66154 destProp.set(destContainer);
66155 }
66156 }
66157
66158 return dest;
66159};
66160
66161plots.dataArrayContainers = ['transforms', 'dimensions'];
66162plots.layoutArrayContainers = Registry.layoutArrayContainers;
66163
66164/*
66165 * Extend a trace definition. This method:
66166 *
66167 * 1. directly transfers any array references
66168 * 2. manually recurses into container arrays like transforms
66169 *
66170 * The result is the original object reference with the new contents merged in.
66171 */
66172plots.extendTrace = function(destTrace, srcTrace) {
66173 return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers);
66174};
66175
66176/*
66177 * Extend a layout definition. This method:
66178 *
66179 * 1. directly transfers any array references (not critically important for
66180 * layout since there aren't really data arrays)
66181 * 2. manually recurses into container arrays like annotations
66182 *
66183 * The result is the original object reference with the new contents merged in.
66184 */
66185plots.extendLayout = function(destLayout, srcLayout) {
66186 return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers);
66187};
66188
66189/**
66190 * Transition to a set of new data and layout properties from Plotly.animate
66191 *
66192 * @param {DOM element} gd
66193 * @param {Object[]} data
66194 * an array of data objects following the normal Plotly data definition format
66195 * @param {Object} layout
66196 * a layout object, following normal Plotly layout format
66197 * @param {Number[]} traces
66198 * indices of the corresponding traces specified in `data`
66199 * @param {Object} frameOpts
66200 * options for the frame (i.e. whether to redraw post-transition)
66201 * @param {Object} transitionOpts
66202 * options for the transition
66203 */
66204plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) {
66205 var opts = {redraw: frameOpts.redraw};
66206 var transitionedTraces = {};
66207 var axEdits = [];
66208
66209 opts.prepareFn = function() {
66210 var dataLength = Array.isArray(data) ? data.length : 0;
66211 var traceIndices = traces.slice(0, dataLength);
66212
66213 for(var i = 0; i < traceIndices.length; i++) {
66214 var traceIdx = traceIndices[i];
66215 var trace = gd._fullData[traceIdx];
66216 var _module = trace._module;
66217
66218 // There's nothing to do if this module is not defined:
66219 if(!_module) continue;
66220
66221 // Don't register the trace as transitioned if it doesn't know what to do.
66222 // If it *is* registered, it will receive a callback that it's responsible
66223 // for calling in order to register the transition as having completed.
66224 if(_module.animatable) {
66225 var n = _module.basePlotModule.name;
66226 if(!transitionedTraces[n]) transitionedTraces[n] = [];
66227 transitionedTraces[n].push(traceIdx);
66228 }
66229
66230 gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
66231 }
66232
66233 // Follow the same procedure. Clone it so we don't mangle the input, then
66234 // expand any object paths so we can merge deep into gd.layout:
66235 var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout));
66236
66237 // Before merging though, we need to modify the incoming layout. We only
66238 // know how to *transition* layout ranges, so it's imperative that a new
66239 // range not be sent to the layout before the transition has started. So
66240 // we must remove the things we can transition:
66241 var axisAttrRe = /^[xy]axis[0-9]*$/;
66242 for(var attr in layoutUpdate) {
66243 if(!axisAttrRe.test(attr)) continue;
66244 delete layoutUpdate[attr].range;
66245 }
66246
66247 plots.extendLayout(gd.layout, layoutUpdate);
66248
66249 // Supply defaults after applying the incoming properties. Note that any attempt
66250 // to simplify this step and reduce the amount of work resulted in the reconstruction
66251 // of essentially the whole supplyDefaults step, so that it seems sensible to just use
66252 // supplyDefaults even though it's heavier than would otherwise be desired for
66253 // transitions:
66254
66255 // first delete calcdata so supplyDefaults knows a calc step is coming
66256 delete gd.calcdata;
66257
66258 plots.supplyDefaults(gd);
66259 plots.doCalcdata(gd);
66260
66261 var newLayout = Lib.expandObjectPaths(layout);
66262
66263 if(newLayout) {
66264 var subplots = gd._fullLayout._plots;
66265
66266 for(var k in subplots) {
66267 var plotinfo = subplots[k];
66268 var xa = plotinfo.xaxis;
66269 var ya = plotinfo.yaxis;
66270 var xr0 = xa.range.slice();
66271 var yr0 = ya.range.slice();
66272
66273 var xr1 = null;
66274 var yr1 = null;
66275 var editX = null;
66276 var editY = null;
66277
66278 if(Array.isArray(newLayout[xa._name + '.range'])) {
66279 xr1 = newLayout[xa._name + '.range'].slice();
66280 } else if(Array.isArray((newLayout[xa._name] || {}).range)) {
66281 xr1 = newLayout[xa._name].range.slice();
66282 }
66283 if(Array.isArray(newLayout[ya._name + '.range'])) {
66284 yr1 = newLayout[ya._name + '.range'].slice();
66285 } else if(Array.isArray((newLayout[ya._name] || {}).range)) {
66286 yr1 = newLayout[ya._name].range.slice();
66287 }
66288
66289 if(xr0 && xr1 &&
66290 (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1]))
66291 ) {
66292 editX = {xr0: xr0, xr1: xr1};
66293 }
66294 if(yr0 && yr1 &&
66295 (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1]))
66296 ) {
66297 editY = {yr0: yr0, yr1: yr1};
66298 }
66299
66300 if(editX || editY) {
66301 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
66302 }
66303 }
66304 }
66305
66306 return Promise.resolve();
66307 };
66308
66309 opts.runFn = function(makeCallback) {
66310 var traceTransitionOpts;
66311 var basePlotModules = gd._fullLayout._basePlotModules;
66312 var hasAxisTransition = axEdits.length;
66313 var i;
66314
66315 if(layout) {
66316 for(i = 0; i < basePlotModules.length; i++) {
66317 if(basePlotModules[i].transitionAxes) {
66318 basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback);
66319 }
66320 }
66321 }
66322
66323 // Here handle the exception that we refuse to animate scales and axes at the same
66324 // time. In other words, if there's an axis transition, then set the data transition
66325 // to instantaneous.
66326 if(hasAxisTransition) {
66327 traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
66328 traceTransitionOpts.duration = 0;
66329 // This means do not transition cartesian traces,
66330 // this happens on layout-only (e.g. axis range) animations
66331 delete transitionedTraces.cartesian;
66332 } else {
66333 traceTransitionOpts = transitionOpts;
66334 }
66335
66336 // Note that we pass a callback to *create* the callback that must be invoked on completion.
66337 // This is since not all traces know about transitions, so it greatly simplifies matters if
66338 // the trace is responsible for creating a callback, if needed, and then executing it when
66339 // the time is right.
66340 for(var n in transitionedTraces) {
66341 var traceIndices = transitionedTraces[n];
66342 var _module = gd._fullData[traceIndices[0]]._module;
66343 _module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback);
66344 }
66345 };
66346
66347 return _transition(gd, transitionOpts, opts);
66348};
66349
66350/**
66351 * Transition to a set of new data and layout properties from Plotly.react
66352 *
66353 * @param {DOM element} gd
66354 * @param {object} restyleFlags
66355 * - anim {'all'|'some'}
66356 * @param {object} relayoutFlags
66357 * - anim {'all'|'some'}
66358 * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout
66359 */
66360plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) {
66361 var fullLayout = gd._fullLayout;
66362 var transitionOpts = fullLayout.transition;
66363 var opts = {};
66364 var axEdits = [];
66365
66366 opts.prepareFn = function() {
66367 var subplots = fullLayout._plots;
66368
66369 // no need to redraw at end of transition,
66370 // if all changes are animatable
66371 opts.redraw = false;
66372 if(restyleFlags.anim === 'some') opts.redraw = true;
66373 if(relayoutFlags.anim === 'some') opts.redraw = true;
66374
66375 for(var k in subplots) {
66376 var plotinfo = subplots[k];
66377 var xa = plotinfo.xaxis;
66378 var ya = plotinfo.yaxis;
66379 var xr0 = oldFullLayout[xa._name].range.slice();
66380 var yr0 = oldFullLayout[ya._name].range.slice();
66381 var xr1 = xa.range.slice();
66382 var yr1 = ya.range.slice();
66383
66384 xa.setScale();
66385 ya.setScale();
66386
66387 var editX = null;
66388 var editY = null;
66389
66390 if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) {
66391 editX = {xr0: xr0, xr1: xr1};
66392 }
66393 if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) {
66394 editY = {yr0: yr0, yr1: yr1};
66395 }
66396
66397 if(editX || editY) {
66398 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
66399 }
66400 }
66401
66402 return Promise.resolve();
66403 };
66404
66405 opts.runFn = function(makeCallback) {
66406 var fullData = gd._fullData;
66407 var fullLayout = gd._fullLayout;
66408 var basePlotModules = fullLayout._basePlotModules;
66409
66410 var axisTransitionOpts;
66411 var traceTransitionOpts;
66412 var transitionedTraces;
66413
66414 var allTraceIndices = [];
66415 for(var i = 0; i < fullData.length; i++) {
66416 allTraceIndices.push(i);
66417 }
66418
66419 function transitionAxes() {
66420 for(var j = 0; j < basePlotModules.length; j++) {
66421 if(basePlotModules[j].transitionAxes) {
66422 basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback);
66423 }
66424 }
66425 }
66426
66427 function transitionTraces() {
66428 for(var j = 0; j < basePlotModules.length; j++) {
66429 basePlotModules[j].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
66430 }
66431 }
66432
66433 if(axEdits.length && restyleFlags.anim) {
66434 if(transitionOpts.ordering === 'traces first') {
66435 axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
66436 transitionedTraces = allTraceIndices;
66437 traceTransitionOpts = transitionOpts;
66438 setTimeout(transitionAxes, transitionOpts.duration);
66439 transitionTraces();
66440 } else {
66441 axisTransitionOpts = transitionOpts;
66442 transitionedTraces = null;
66443 traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
66444 setTimeout(transitionTraces, axisTransitionOpts.duration);
66445 transitionAxes();
66446 }
66447 } else if(axEdits.length) {
66448 axisTransitionOpts = transitionOpts;
66449 transitionAxes();
66450 } else if(restyleFlags.anim) {
66451 transitionedTraces = allTraceIndices;
66452 traceTransitionOpts = transitionOpts;
66453 transitionTraces();
66454 }
66455 };
66456
66457 return _transition(gd, transitionOpts, opts);
66458};
66459
66460/**
66461 * trace/layout transition wrapper that works
66462 * for transitions initiated by Plotly.animate and Plotly.react.
66463 *
66464 * @param {DOM element} gd
66465 * @param {object} transitionOpts
66466 * @param {object} opts
66467 * - redraw {boolean}
66468 * - prepareFn {function} *should return a Promise*
66469 * - runFn {function} ran inside executeTransitions
66470 */
66471function _transition(gd, transitionOpts, opts) {
66472 var aborted = false;
66473
66474 function executeCallbacks(list) {
66475 var p = Promise.resolve();
66476 if(!list) return p;
66477 while(list.length) {
66478 p = p.then((list.shift()));
66479 }
66480 return p;
66481 }
66482
66483 function flushCallbacks(list) {
66484 if(!list) return;
66485 while(list.length) {
66486 list.shift();
66487 }
66488 }
66489
66490 function executeTransitions() {
66491 gd.emit('plotly_transitioning', []);
66492
66493 return new Promise(function(resolve) {
66494 // This flag is used to disabled things like autorange:
66495 gd._transitioning = true;
66496
66497 // When instantaneous updates are coming through quickly, it's too much to simply disable
66498 // all interaction, so store this flag so we can disambiguate whether mouse interactions
66499 // should be fully disabled or not:
66500 if(transitionOpts.duration > 0) {
66501 gd._transitioningWithDuration = true;
66502 }
66503
66504 // If another transition is triggered, this callback will be executed simply because it's
66505 // in the interruptCallbacks queue. If this transition completes, it will instead flush
66506 // that queue and forget about this callback.
66507 gd._transitionData._interruptCallbacks.push(function() {
66508 aborted = true;
66509 });
66510
66511 if(opts.redraw) {
66512 gd._transitionData._interruptCallbacks.push(function() {
66513 return Registry.call('redraw', gd);
66514 });
66515 }
66516
66517 // Emit this and make sure it happens last:
66518 gd._transitionData._interruptCallbacks.push(function() {
66519 gd.emit('plotly_transitioninterrupted', []);
66520 });
66521
66522 // Construct callbacks that are executed on transition end. This ensures the d3 transitions
66523 // are *complete* before anything else is done.
66524 var numCallbacks = 0;
66525 var numCompleted = 0;
66526 function makeCallback() {
66527 numCallbacks++;
66528 return function() {
66529 numCompleted++;
66530 // When all are complete, perform a redraw:
66531 if(!aborted && numCompleted === numCallbacks) {
66532 completeTransition(resolve);
66533 }
66534 };
66535 }
66536
66537 opts.runFn(makeCallback);
66538
66539 // If nothing else creates a callback, then this will trigger the completion in the next tick:
66540 setTimeout(makeCallback());
66541 });
66542 }
66543
66544 function completeTransition(callback) {
66545 // This a simple workaround for tests which purge the graph before animations
66546 // have completed. That's not a very common case, so this is the simplest
66547 // fix.
66548 if(!gd._transitionData) return;
66549
66550 flushCallbacks(gd._transitionData._interruptCallbacks);
66551
66552 return Promise.resolve().then(function() {
66553 if(opts.redraw) {
66554 return Registry.call('redraw', gd);
66555 }
66556 }).then(function() {
66557 // Set transitioning false again once the redraw has occurred. This is used, for example,
66558 // to prevent the trailing redraw from autoranging:
66559 gd._transitioning = false;
66560 gd._transitioningWithDuration = false;
66561
66562 gd.emit('plotly_transitioned', []);
66563 }).then(callback);
66564 }
66565
66566 function interruptPreviousTransitions() {
66567 // Fail-safe against purged plot:
66568 if(!gd._transitionData) return;
66569
66570 // If a transition is interrupted, set this to false. At the moment, the only thing that would
66571 // interrupt a transition is another transition, so that it will momentarily be set to true
66572 // again, but this determines whether autorange or dragbox work, so it's for the sake of
66573 // cleanliness:
66574 gd._transitioning = false;
66575
66576 return executeCallbacks(gd._transitionData._interruptCallbacks);
66577 }
66578
66579 var seq = [
66580 plots.previousPromises,
66581 interruptPreviousTransitions,
66582 opts.prepareFn,
66583 plots.rehover,
66584 executeTransitions
66585 ];
66586
66587 var transitionStarting = Lib.syncOrAsync(seq, gd);
66588
66589 if(!transitionStarting || !transitionStarting.then) {
66590 transitionStarting = Promise.resolve();
66591 }
66592
66593 return transitionStarting.then(function() { return gd; });
66594}
66595
66596plots.doCalcdata = function(gd, traces) {
66597 var axList = axisIDs.list(gd);
66598 var fullData = gd._fullData;
66599 var fullLayout = gd._fullLayout;
66600
66601 var trace, _module, i, j;
66602
66603 // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
66604 // *all* needing doCalcdata:
66605 var calcdata = new Array(fullData.length);
66606 var oldCalcdata = (gd.calcdata || []).slice();
66607 gd.calcdata = calcdata;
66608
66609 // extra helper variables
66610
66611 // how many box/violins plots do we have (in case they're grouped)
66612 fullLayout._numBoxes = 0;
66613 fullLayout._numViolins = 0;
66614
66615 // initialize violin per-scale-group stats container
66616 fullLayout._violinScaleGroupStats = {};
66617
66618 // for calculating avg luminosity of heatmaps
66619 gd._hmpixcount = 0;
66620 gd._hmlumcount = 0;
66621
66622 // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend)
66623 fullLayout._piecolormap = {};
66624 fullLayout._sunburstcolormap = {};
66625 fullLayout._treemapcolormap = {};
66626 fullLayout._funnelareacolormap = {};
66627
66628 // If traces were specified and this trace was not included,
66629 // then transfer it over from the old calcdata:
66630 for(i = 0; i < fullData.length; i++) {
66631 if(Array.isArray(traces) && traces.indexOf(i) === -1) {
66632 calcdata[i] = oldCalcdata[i];
66633 continue;
66634 }
66635 }
66636
66637 for(i = 0; i < fullData.length; i++) {
66638 trace = fullData[i];
66639
66640 trace._arrayAttrs = PlotSchema.findArrayAttributes(trace);
66641
66642 // keep track of trace extremes (for autorange) in here
66643 trace._extremes = {};
66644 }
66645
66646 // add polar axes to axis list
66647 var polarIds = fullLayout._subplots.polar || [];
66648 for(i = 0; i < polarIds.length; i++) {
66649 axList.push(
66650 fullLayout[polarIds[i]].radialaxis,
66651 fullLayout[polarIds[i]].angularaxis
66652 );
66653 }
66654
66655 // clear relinked cmin/cmax values in shared axes to start aggregation from scratch
66656 for(var k in fullLayout._colorAxes) {
66657 var cOpts = fullLayout[k];
66658 if(cOpts.cauto !== false) {
66659 delete cOpts.cmin;
66660 delete cOpts.cmax;
66661 }
66662 }
66663
66664 var hasCalcTransform = false;
66665
66666 function transformCalci(i) {
66667 trace = fullData[i];
66668 _module = trace._module;
66669
66670 if(trace.visible === true && trace.transforms) {
66671 // we need one round of trace module calc before
66672 // the calc transform to 'fill in' the categories list
66673 // used for example in the data-to-coordinate method
66674 if(_module && _module.calc) {
66675 var cdi = _module.calc(gd, trace);
66676
66677 // must clear scene 'batches', so that 2nd
66678 // _module.calc call starts from scratch
66679 if(cdi[0] && cdi[0].t && cdi[0].t._scene) {
66680 delete cdi[0].t._scene.dirty;
66681 }
66682 }
66683
66684 for(j = 0; j < trace.transforms.length; j++) {
66685 var transform = trace.transforms[j];
66686
66687 _module = transformsRegistry[transform.type];
66688 if(_module && _module.calcTransform) {
66689 trace._hasCalcTransform = true;
66690 hasCalcTransform = true;
66691 _module.calcTransform(gd, trace, transform);
66692 }
66693 }
66694 }
66695 }
66696
66697 function calci(i, isContainer) {
66698 trace = fullData[i];
66699 _module = trace._module;
66700
66701 if(!!_module.isContainer !== isContainer) return;
66702
66703 var cd = [];
66704
66705 if(trace.visible === true && trace._length !== 0) {
66706 // clear existing ref in case it got relinked
66707 delete trace._indexToPoints;
66708 // keep ref of index-to-points map object of the *last* enabled transform,
66709 // this index-to-points map object is required to determine the calcdata indices
66710 // that correspond to input indices (e.g. from 'selectedpoints')
66711 var transforms = trace.transforms || [];
66712 for(j = transforms.length - 1; j >= 0; j--) {
66713 if(transforms[j].enabled) {
66714 trace._indexToPoints = transforms[j]._indexToPoints;
66715 break;
66716 }
66717 }
66718
66719 if(_module && _module.calc) {
66720 cd = _module.calc(gd, trace);
66721 }
66722 }
66723
66724 // Make sure there is a first point.
66725 //
66726 // This ensures there is a calcdata item for every trace,
66727 // even if cartesian logic doesn't handle it (for things like legends).
66728 if(!Array.isArray(cd) || !cd[0]) {
66729 cd = [{x: BADNUM, y: BADNUM}];
66730 }
66731
66732 // add the trace-wide properties to the first point,
66733 // per point properties to every point
66734 // t is the holder for trace-wide properties
66735 if(!cd[0].t) cd[0].t = {};
66736 cd[0].trace = trace;
66737
66738 calcdata[i] = cd;
66739 }
66740
66741 setupAxisCategories(axList, fullData, fullLayout);
66742
66743 // 'transform' loop - must calc container traces first
66744 // so that if their dependent traces can get transform properly
66745 for(i = 0; i < fullData.length; i++) calci(i, true);
66746 for(i = 0; i < fullData.length; i++) transformCalci(i);
66747
66748 // clear stuff that should recomputed in 'regular' loop
66749 if(hasCalcTransform) setupAxisCategories(axList, fullData, fullLayout);
66750
66751 // 'regular' loop - make sure container traces (eg carpet) calc before
66752 // contained traces (eg contourcarpet)
66753 for(i = 0; i < fullData.length; i++) calci(i, true);
66754 for(i = 0; i < fullData.length; i++) calci(i, false);
66755
66756 doCrossTraceCalc(gd);
66757
66758 // Sort axis categories per value if specified
66759 var sorted = sortAxisCategoriesByValue(axList, gd);
66760 if(sorted.length) {
66761 // how many box/violins plots do we have (in case they're grouped)
66762 fullLayout._numBoxes = 0;
66763 fullLayout._numViolins = 0;
66764 // If a sort operation was performed, run calc() again
66765 for(i = 0; i < sorted.length; i++) calci(sorted[i], true);
66766 for(i = 0; i < sorted.length; i++) calci(sorted[i], false);
66767 doCrossTraceCalc(gd);
66768 }
66769
66770 Registry.getComponentMethod('fx', 'calc')(gd);
66771 Registry.getComponentMethod('errorbars', 'calc')(gd);
66772};
66773
66774var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|median) (ascending|descending)/;
66775
66776function sortAxisCategoriesByValue(axList, gd) {
66777 var affectedTraces = [];
66778 var i, j, k, l, o;
66779
66780 function zMapCategory(type, ax, value) {
66781 var axLetter = ax._id.charAt(0);
66782 if(type === 'histogram2dcontour') {
66783 var counterAxLetter = ax._counterAxes[0];
66784 var counterAx = axisIDs.getFromId(gd, counterAxLetter);
66785
66786 var xCategorical = axLetter === 'x' || (counterAxLetter === 'x' && counterAx.type === 'category');
66787 var yCategorical = axLetter === 'y' || (counterAxLetter === 'y' && counterAx.type === 'category');
66788
66789 return function(o, l) {
66790 if(o === 0 || l === 0) return -1; // Skip first row and column
66791 if(xCategorical && o === value[l].length - 1) return -1;
66792 if(yCategorical && l === value.length - 1) return -1;
66793
66794 return (axLetter === 'y' ? l : o) - 1;
66795 };
66796 } else {
66797 return function(o, l) {
66798 return axLetter === 'y' ? l : o;
66799 };
66800 }
66801 }
66802
66803 var aggFn = {
66804 'min': function(values) {return Lib.aggNums(Math.min, null, values);},
66805 'max': function(values) {return Lib.aggNums(Math.max, null, values);},
66806 'sum': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
66807 'total': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
66808 'mean': function(values) {return Lib.mean(values);},
66809 'median': function(values) {return Lib.median(values);}
66810 };
66811
66812 for(i = 0; i < axList.length; i++) {
66813 var ax = axList[i];
66814 if(ax.type !== 'category') continue;
66815
66816 // Order by value
66817 var match = ax.categoryorder.match(sortAxisCategoriesByValueRegex);
66818 if(match) {
66819 var aggregator = match[1];
66820 var order = match[2];
66821
66822 // Store values associated with each category
66823 var categoriesValue = [];
66824 for(j = 0; j < ax._categories.length; j++) {
66825 categoriesValue.push([ax._categories[j], []]);
66826 }
66827
66828 // Collect values across traces
66829 for(j = 0; j < ax._traceIndices.length; j++) {
66830 var traceIndex = ax._traceIndices[j];
66831 var fullTrace = gd._fullData[traceIndex];
66832 var axLetter = ax._id.charAt(0);
66833
66834 // Skip over invisible traces
66835 if(fullTrace.visible !== true) continue;
66836
66837 var type = fullTrace.type;
66838 if(Registry.traceIs(fullTrace, 'histogram')) {
66839 delete fullTrace._xautoBinFinished;
66840 delete fullTrace._yautoBinFinished;
66841 }
66842
66843 var cd = gd.calcdata[traceIndex];
66844 for(k = 0; k < cd.length; k++) {
66845 var cdi = cd[k];
66846 var cat, catIndex, value;
66847
66848 if(type === 'splom') {
66849 // If `splom`, collect values across dimensions
66850 // Find which dimension the current axis is representing
66851 var currentDimensionIndex = fullTrace._axesDim[ax._id];
66852
66853 // Apply logic to associated x axis if it's defined
66854 if(axLetter === 'y') {
66855 var associatedXAxisID = fullTrace._diag[currentDimensionIndex][0];
66856 if(associatedXAxisID) ax = gd._fullLayout[axisIDs.id2name(associatedXAxisID)];
66857 }
66858
66859 var categories = cdi.trace.dimensions[currentDimensionIndex].values;
66860 for(l = 0; l < categories.length; l++) {
66861 cat = categories[l];
66862 catIndex = ax._categoriesMap[cat];
66863
66864 // Collect associated values at index `l` over all other dimensions
66865 for(o = 0; o < cdi.trace.dimensions.length; o++) {
66866 if(o === currentDimensionIndex) continue;
66867 var dimension = cdi.trace.dimensions[o];
66868 categoriesValue[catIndex][1].push(dimension.values[l]);
66869 }
66870 }
66871 } else if(type === 'scattergl') {
66872 // If `scattergl`, collect all values stashed under cdi.t
66873 for(l = 0; l < cdi.t.x.length; l++) {
66874 if(axLetter === 'x') {
66875 cat = cdi.t.x[l];
66876 catIndex = cat;
66877 value = cdi.t.y[l];
66878 }
66879
66880 if(axLetter === 'y') {
66881 cat = cdi.t.y[l];
66882 catIndex = cat;
66883 value = cdi.t.x[l];
66884 }
66885 categoriesValue[catIndex][1].push(value);
66886 }
66887 // must clear scene 'batches', so that 2nd
66888 // _module.calc call starts from scratch
66889 if(cdi.t && cdi.t._scene) {
66890 delete cdi.t._scene.dirty;
66891 }
66892 } else if(cdi.hasOwnProperty('z')) {
66893 // If 2dMap, collect values in `z`
66894 value = cdi.z;
66895 var mapping = zMapCategory(fullTrace.type, ax, value);
66896
66897 for(l = 0; l < value.length; l++) {
66898 for(o = 0; o < value[l].length; o++) {
66899 catIndex = mapping(o, l);
66900 if(catIndex + 1) categoriesValue[catIndex][1].push(value[l][o]);
66901 }
66902 }
66903 } else {
66904 // For all other 2d cartesian traces
66905 if(axLetter === 'x') {
66906 cat = cdi.p + 1 ? cdi.p : cdi.x;
66907 value = cdi.s || cdi.v || cdi.y;
66908 } else if(axLetter === 'y') {
66909 cat = cdi.p + 1 ? cdi.p : cdi.y;
66910 value = cdi.s || cdi.v || cdi.x;
66911 }
66912 if(!Array.isArray(value)) value = [value];
66913 for(l = 0; l < value.length; l++) {
66914 categoriesValue[cat][1].push(value[l]);
66915 }
66916 }
66917 }
66918 }
66919
66920 ax._categoriesValue = categoriesValue;
66921
66922 var categoriesAggregatedValue = [];
66923 for(j = 0; j < categoriesValue.length; j++) {
66924 categoriesAggregatedValue.push([
66925 categoriesValue[j][0],
66926 aggFn[aggregator](categoriesValue[j][1])
66927 ]);
66928 }
66929
66930 // Sort by aggregated value
66931 categoriesAggregatedValue.sort(function(a, b) {
66932 return a[1] - b[1];
66933 });
66934
66935 ax._categoriesAggregatedValue = categoriesAggregatedValue;
66936
66937 // Set new category order
66938 ax._initialCategories = categoriesAggregatedValue.map(function(c) {
66939 return c[0];
66940 });
66941
66942 // Reverse if descending
66943 if(order === 'descending') {
66944 ax._initialCategories.reverse();
66945 }
66946
66947 // Sort all matching axes
66948 affectedTraces = affectedTraces.concat(ax.sortByInitialCategories());
66949 }
66950 }
66951 return affectedTraces;
66952}
66953
66954function setupAxisCategories(axList, fullData, fullLayout) {
66955 var axLookup = {};
66956 var i, ax, axId;
66957
66958 for(i = 0; i < axList.length; i++) {
66959 ax = axList[i];
66960 axId = ax._id;
66961
66962 ax.clearCalc();
66963 if(ax.type === 'multicategory') {
66964 ax.setupMultiCategory(fullData);
66965 }
66966
66967 axLookup[ax._id] = 1;
66968 }
66969
66970 // look into match groups for 'missing' axes
66971 var matchGroups = fullLayout._axisMatchGroups || [];
66972 for(i = 0; i < matchGroups.length; i++) {
66973 for(axId in matchGroups[i]) {
66974 if(!axLookup[axId]) {
66975 ax = fullLayout[axisIDs.id2name(axId)];
66976 ax.clearCalc();
66977 }
66978 }
66979 }
66980}
66981
66982function doCrossTraceCalc(gd) {
66983 var fullLayout = gd._fullLayout;
66984 var modules = fullLayout._visibleModules;
66985 var hash = {};
66986 var i, j, k;
66987
66988 // position and range calculations for traces that
66989 // depend on each other ie bars (stacked or grouped)
66990 // and boxes (grouped) push each other out of the way
66991
66992 for(j = 0; j < modules.length; j++) {
66993 var _module = modules[j];
66994 var fn = _module.crossTraceCalc;
66995 if(fn) {
66996 var spType = _module.basePlotModule.name;
66997 if(hash[spType]) {
66998 Lib.pushUnique(hash[spType], fn);
66999 } else {
67000 hash[spType] = [fn];
67001 }
67002 }
67003 }
67004
67005 for(k in hash) {
67006 var methods = hash[k];
67007 var subplots = fullLayout._subplots[k];
67008
67009 if(Array.isArray(subplots)) {
67010 for(i = 0; i < subplots.length; i++) {
67011 var sp = subplots[i];
67012 var spInfo = k === 'cartesian' ?
67013 fullLayout._plots[sp] :
67014 fullLayout[sp];
67015
67016 for(j = 0; j < methods.length; j++) {
67017 methods[j](gd, spInfo, sp);
67018 }
67019 }
67020 } else {
67021 for(j = 0; j < methods.length; j++) {
67022 methods[j](gd);
67023 }
67024 }
67025 }
67026}
67027
67028plots.rehover = function(gd) {
67029 if(gd._fullLayout._rehover) {
67030 gd._fullLayout._rehover();
67031 }
67032};
67033
67034plots.redrag = function(gd) {
67035 if(gd._fullLayout._redrag) {
67036 gd._fullLayout._redrag();
67037 }
67038};
67039
67040plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) {
67041 var traceHashOld = subplot.traceHash;
67042 var traceHash = {};
67043 var i;
67044
67045 // build up moduleName -> calcData hash
67046 for(i = 0; i < subplotCalcData.length; i++) {
67047 var calcTraces = subplotCalcData[i];
67048 var trace = calcTraces[0].trace;
67049
67050 // skip over visible === false traces
67051 // as they don't have `_module` ref
67052 if(trace.visible) {
67053 traceHash[trace.type] = traceHash[trace.type] || [];
67054 traceHash[trace.type].push(calcTraces);
67055 }
67056 }
67057
67058 // when a trace gets deleted, make sure that its module's
67059 // plot method is called so that it is properly
67060 // removed from the DOM.
67061 for(var moduleNameOld in traceHashOld) {
67062 if(!traceHash[moduleNameOld]) {
67063 var fakeCalcTrace = traceHashOld[moduleNameOld][0];
67064 var fakeTrace = fakeCalcTrace[0].trace;
67065
67066 fakeTrace.visible = false;
67067 traceHash[moduleNameOld] = [fakeCalcTrace];
67068 }
67069 }
67070
67071 // call module plot method
67072 for(var moduleName in traceHash) {
67073 var moduleCalcData = traceHash[moduleName];
67074 var _module = moduleCalcData[0][0].trace._module;
67075
67076 _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
67077 }
67078
67079 // update moduleName -> calcData hash
67080 subplot.traceHash = traceHash;
67081};
67082
67083plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) {
67084 var _module = Registry.getModule(desiredType);
67085 var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
67086 _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
67087};
67088
67089plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) {
67090 var had = (oldFullLayout._has && oldFullLayout._has(desiredType));
67091 var has = (newFullLayout._has && newFullLayout._has(desiredType));
67092
67093 if(had && !has) {
67094 oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove();
67095 }
67096};
67097
67098},{"../components/color":52,"../constants/numerical":158,"../lib":178,"../plot_api/plot_schema":211,"../plot_api/plot_template":212,"../plots/get_data":252,"../registry":269,"./animation_attributes":217,"./attributes":219,"./cartesian/axis_ids":225,"./cartesian/handle_outline":232,"./command":248,"./font_attributes":250,"./frame_attributes":251,"./layout_attributes":254,"d3":16,"fast-isnumeric":18}],257:[function(_dereq_,module,exports){
67099/**
67100* Copyright 2012-2020, Plotly, Inc.
67101* All rights reserved.
67102*
67103* This source code is licensed under the MIT license found in the
67104* LICENSE file in the root directory of this source tree.
67105*/
67106
67107'use strict';
67108
67109var scatterAttrs = _dereq_('../../../traces/scatter/attributes');
67110var scatterMarkerAttrs = scatterAttrs.marker;
67111var extendFlat = _dereq_('../../../lib/extend').extendFlat;
67112
67113var deprecationWarning = [
67114 'Area traces are deprecated!',
67115 'Please switch to the *barpolar* trace type.'
67116].join(' ');
67117
67118module.exports = {
67119 r: extendFlat({}, scatterAttrs.r, {
67120
67121 }),
67122 t: extendFlat({}, scatterAttrs.t, {
67123
67124 }),
67125 marker: {
67126 color: extendFlat({}, scatterMarkerAttrs.color, {
67127
67128 }),
67129 size: extendFlat({}, scatterMarkerAttrs.size, {
67130
67131 }),
67132 symbol: extendFlat({}, scatterMarkerAttrs.symbol, {
67133
67134 }),
67135 opacity: extendFlat({}, scatterMarkerAttrs.opacity, {
67136
67137 }),
67138 editType: 'calc'
67139 }
67140};
67141
67142},{"../../../lib/extend":173,"../../../traces/scatter/attributes":389}],258:[function(_dereq_,module,exports){
67143/**
67144* Copyright 2012-2020, Plotly, Inc.
67145* All rights reserved.
67146*
67147* This source code is licensed under the MIT license found in the
67148* LICENSE file in the root directory of this source tree.
67149*/
67150
67151
67152'use strict';
67153
67154var axesAttrs = _dereq_('../../cartesian/layout_attributes');
67155var extendFlat = _dereq_('../../../lib/extend').extendFlat;
67156var overrideAll = _dereq_('../../../plot_api/edit_types').overrideAll;
67157
67158var deprecationWarning = [
67159 'Legacy polar charts are deprecated!',
67160 'Please switch to *polar* subplots.'
67161].join(' ');
67162
67163var domainAttr = extendFlat({}, axesAttrs.domain, {
67164
67165});
67166
67167function mergeAttrs(axisName, nonCommonAttrs) {
67168 var commonAttrs = {
67169 showline: {
67170 valType: 'boolean',
67171
67172
67173 },
67174 showticklabels: {
67175 valType: 'boolean',
67176
67177
67178 },
67179 tickorientation: {
67180 valType: 'enumerated',
67181 values: ['horizontal', 'vertical'],
67182
67183
67184 },
67185 ticklen: {
67186 valType: 'number',
67187 min: 0,
67188
67189
67190 },
67191 tickcolor: {
67192 valType: 'color',
67193
67194
67195 },
67196 ticksuffix: {
67197 valType: 'string',
67198
67199
67200 },
67201 endpadding: {
67202 valType: 'number',
67203
67204 description: deprecationWarning,
67205 },
67206 visible: {
67207 valType: 'boolean',
67208
67209
67210 }
67211 };
67212
67213 return extendFlat({}, nonCommonAttrs, commonAttrs);
67214}
67215
67216module.exports = overrideAll({
67217 radialaxis: mergeAttrs('radial', {
67218 range: {
67219 valType: 'info_array',
67220
67221 items: [
67222 { valType: 'number' },
67223 { valType: 'number' }
67224 ],
67225
67226 },
67227 domain: domainAttr,
67228 orientation: {
67229 valType: 'number',
67230
67231
67232 }
67233 }),
67234
67235 angularaxis: mergeAttrs('angular', {
67236 range: {
67237 valType: 'info_array',
67238
67239 items: [
67240 { valType: 'number', dflt: 0 },
67241 { valType: 'number', dflt: 360 }
67242 ],
67243
67244 },
67245 domain: domainAttr
67246 }),
67247
67248 // attributes that appear at layout root
67249 layout: {
67250 direction: {
67251 valType: 'enumerated',
67252 values: ['clockwise', 'counterclockwise'],
67253
67254
67255 },
67256 orientation: {
67257 valType: 'angle',
67258
67259
67260 }
67261 }
67262}, 'plot', 'nested');
67263
67264},{"../../../lib/extend":173,"../../../plot_api/edit_types":205,"../../cartesian/layout_attributes":236}],259:[function(_dereq_,module,exports){
67265/**
67266* Copyright 2012-2020, Plotly, Inc.
67267* All rights reserved.
67268*
67269* This source code is licensed under the MIT license found in the
67270* LICENSE file in the root directory of this source tree.
67271*/
67272
67273'use strict';
67274
67275var Polar = module.exports = _dereq_('./micropolar');
67276
67277Polar.manager = _dereq_('./micropolar_manager');
67278
67279},{"./micropolar":260,"./micropolar_manager":261}],260:[function(_dereq_,module,exports){
67280/**
67281* Copyright 2012-2020, Plotly, Inc.
67282* All rights reserved.
67283*
67284* This source code is licensed under the MIT license found in the
67285* LICENSE file in the root directory of this source tree.
67286*/
67287
67288var d3 = _dereq_('d3');
67289var Lib = _dereq_('../../../lib');
67290var extendDeepAll = Lib.extendDeepAll;
67291var MID_SHIFT = _dereq_('../../../constants/alignment').MID_SHIFT;
67292
67293var µ = module.exports = { version: '0.2.2' };
67294
67295µ.Axis = function module() {
67296 var config = {
67297 data: [],
67298 layout: {}
67299 }, inputConfig = {}, liveConfig = {};
67300 var svg, container, dispatch = d3.dispatch('hover'), radialScale, angularScale;
67301 var exports = {};
67302 function render(_container) {
67303 container = _container || container;
67304 var data = config.data;
67305 var axisConfig = config.layout;
67306 if (typeof container == 'string' || container.nodeName) container = d3.select(container);
67307 container.datum(data).each(function(_data, _index) {
67308 var dataOriginal = _data.slice();
67309 liveConfig = {
67310 data: µ.util.cloneJson(dataOriginal),
67311 layout: µ.util.cloneJson(axisConfig)
67312 };
67313 var colorIndex = 0;
67314 dataOriginal.forEach(function(d, i) {
67315 if (!d.color) {
67316 d.color = axisConfig.defaultColorRange[colorIndex];
67317 colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length;
67318 }
67319 if (!d.strokeColor) {
67320 d.strokeColor = d.geometry === 'LinePlot' ? d.color : d3.rgb(d.color).darker().toString();
67321 }
67322 liveConfig.data[i].color = d.color;
67323 liveConfig.data[i].strokeColor = d.strokeColor;
67324 liveConfig.data[i].strokeDash = d.strokeDash;
67325 liveConfig.data[i].strokeSize = d.strokeSize;
67326 });
67327 var data = dataOriginal.filter(function(d, i) {
67328 var visible = d.visible;
67329 return typeof visible === 'undefined' || visible === true;
67330 });
67331 var isStacked = false;
67332 var dataWithGroupId = data.map(function(d, i) {
67333 isStacked = isStacked || typeof d.groupId !== 'undefined';
67334 return d;
67335 });
67336 if (isStacked) {
67337 var grouped = d3.nest().key(function(d, i) {
67338 return typeof d.groupId != 'undefined' ? d.groupId : 'unstacked';
67339 }).entries(dataWithGroupId);
67340 var dataYStack = [];
67341 var stacked = grouped.map(function(d, i) {
67342 if (d.key === 'unstacked') return d.values; else {
67343 var prevArray = d.values[0].r.map(function(d, i) {
67344 return 0;
67345 });
67346 d.values.forEach(function(d, i, a) {
67347 d.yStack = [ prevArray ];
67348 dataYStack.push(prevArray);
67349 prevArray = µ.util.sumArrays(d.r, prevArray);
67350 });
67351 return d.values;
67352 }
67353 });
67354 data = d3.merge(stacked);
67355 }
67356 data.forEach(function(d, i) {
67357 d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ];
67358 d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ];
67359 });
67360 var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
67361 radius = Math.max(10, radius);
67362 var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
67363 var extent;
67364 if (isStacked) {
67365 var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack)));
67366 extent = [ 0, highestStackedValue ];
67367 } else extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) {
67368 return d.r;
67369 })));
67370 if (axisConfig.radialAxis.domain != µ.DATAEXTENT) extent[0] = 0;
67371 radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain != µ.DATAEXTENT && axisConfig.radialAxis.domain ? axisConfig.radialAxis.domain : extent).range([ 0, radius ]);
67372 liveConfig.layout.radialAxis.domain = radialScale.domain();
67373 var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) {
67374 return d.t;
67375 }));
67376 var isOrdinal = typeof angularDataMerged[0] === 'string';
67377 var ticks;
67378 if (isOrdinal) {
67379 angularDataMerged = µ.util.deduplicate(angularDataMerged);
67380 ticks = angularDataMerged.slice();
67381 angularDataMerged = d3.range(angularDataMerged.length);
67382 data = data.map(function(d, i) {
67383 var result = d;
67384 d.t = [ angularDataMerged ];
67385 if (isStacked) result.yStack = d.yStack;
67386 return result;
67387 });
67388 }
67389 var hasOnlyLineOrDotPlot = data.filter(function(d, i) {
67390 return d.geometry === 'LinePlot' || d.geometry === 'DotPlot';
67391 }).length === data.length;
67392 var needsEndSpacing = axisConfig.needsEndSpacing === null ? isOrdinal || !hasOnlyLineOrDotPlot : axisConfig.needsEndSpacing;
67393 var useProvidedDomain = axisConfig.angularAxis.domain && axisConfig.angularAxis.domain != µ.DATAEXTENT && !isOrdinal && axisConfig.angularAxis.domain[0] >= 0;
67394 var angularDomain = useProvidedDomain ? axisConfig.angularAxis.domain : d3.extent(angularDataMerged);
67395 var angularDomainStep = Math.abs(angularDataMerged[1] - angularDataMerged[0]);
67396 if (hasOnlyLineOrDotPlot && !isOrdinal) angularDomainStep = 0;
67397 var angularDomainWithPadding = angularDomain.slice();
67398 if (needsEndSpacing && isOrdinal) angularDomainWithPadding[1] += angularDomainStep;
67399 var tickCount = axisConfig.angularAxis.ticksCount || 4;
67400 if (tickCount > 8) tickCount = tickCount / (tickCount / 8) + tickCount % 8;
67401 if (axisConfig.angularAxis.ticksStep) {
67402 tickCount = (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / tickCount;
67403 }
67404 var angularTicksStep = axisConfig.angularAxis.ticksStep || (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / (tickCount * (axisConfig.minorTicks + 1));
67405 if (ticks) angularTicksStep = Math.max(Math.round(angularTicksStep), 1);
67406 if (!angularDomainWithPadding[2]) angularDomainWithPadding[2] = angularTicksStep;
67407 var angularAxisRange = d3.range.apply(this, angularDomainWithPadding);
67408 angularAxisRange = angularAxisRange.map(function(d, i) {
67409 return parseFloat(d.toPrecision(12));
67410 });
67411 angularScale = d3.scale.linear().domain(angularDomainWithPadding.slice(0, 2)).range(axisConfig.direction === 'clockwise' ? [ 0, 360 ] : [ 360, 0 ]);
67412 liveConfig.layout.angularAxis.domain = angularScale.domain();
67413 liveConfig.layout.angularAxis.endPadding = needsEndSpacing ? angularDomainStep : 0;
67414 svg = d3.select(this).select('svg.chart-root');
67415 if (typeof svg === 'undefined' || svg.empty()) {
67416 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>";
67417 var doc = new DOMParser().parseFromString(skeleton, 'application/xml');
67418 var newSvg = this.appendChild(this.ownerDocument.importNode(doc.documentElement, true));
67419 svg = d3.select(newSvg);
67420 }
67421 svg.select('.guides-group').style({
67422 'pointer-events': 'none'
67423 });
67424 svg.select('.angular.axis-group').style({
67425 'pointer-events': 'none'
67426 });
67427 svg.select('.radial.axis-group').style({
67428 'pointer-events': 'none'
67429 });
67430 var chartGroup = svg.select('.chart-group');
67431 var lineStyle = {
67432 fill: 'none',
67433 stroke: axisConfig.tickColor
67434 };
67435 var fontStyle = {
67436 'font-size': axisConfig.font.size,
67437 'font-family': axisConfig.font.family,
67438 fill: axisConfig.font.color,
67439 'text-shadow': [ '-1px 0px', '1px -1px', '-1px 1px', '1px 1px' ].map(function(d, i) {
67440 return ' ' + d + ' 0 ' + axisConfig.font.outlineColor;
67441 }).join(',')
67442 };
67443 var legendContainer;
67444 if (axisConfig.showLegend) {
67445 legendContainer = svg.select('.legend-group').attr({
67446 transform: 'translate(' + [ radius, axisConfig.margin.top ] + ')'
67447 }).style({
67448 display: 'block'
67449 });
67450 var elements = data.map(function(d, i) {
67451 var datumClone = µ.util.cloneJson(d);
67452 datumClone.symbol = d.geometry === 'DotPlot' ? d.dotType || 'circle' : d.geometry != 'LinePlot' ? 'square' : 'line';
67453 datumClone.visibleInLegend = typeof d.visibleInLegend === 'undefined' || d.visibleInLegend;
67454 datumClone.color = d.geometry === 'LinePlot' ? d.strokeColor : d.color;
67455 return datumClone;
67456 });
67457
67458 µ.Legend().config({
67459 data: data.map(function(d, i) {
67460 return d.name || 'Element' + i;
67461 }),
67462 legendConfig: extendDeepAll({},
67463 µ.Legend.defaultConfig().legendConfig,
67464 {
67465 container: legendContainer,
67466 elements: elements,
67467 reverseOrder: axisConfig.legend.reverseOrder
67468 }
67469 )
67470 })();
67471
67472 var legendBBox = legendContainer.node().getBBox();
67473 radius = Math.min(axisConfig.width - legendBBox.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
67474 radius = Math.max(10, radius);
67475 chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
67476 radialScale.range([ 0, radius ]);
67477 liveConfig.layout.radialAxis.domain = radialScale.domain();
67478 legendContainer.attr('transform', 'translate(' + [ chartCenter[0] + radius, chartCenter[1] - radius ] + ')');
67479 } else {
67480 legendContainer = svg.select('.legend-group').style({
67481 display: 'none'
67482 });
67483 }
67484 svg.attr({
67485 width: axisConfig.width,
67486 height: axisConfig.height
67487 }).style({
67488 opacity: axisConfig.opacity
67489 });
67490 chartGroup.attr('transform', 'translate(' + chartCenter + ')').style({
67491 cursor: 'crosshair'
67492 });
67493 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 ];
67494 centeringOffset[0] = Math.max(0, centeringOffset[0]);
67495 centeringOffset[1] = Math.max(0, centeringOffset[1]);
67496 svg.select('.outer-group').attr('transform', 'translate(' + centeringOffset + ')');
67497 if (axisConfig.title && axisConfig.title.text) {
67498 var title = svg.select('g.title-group text').style(fontStyle).text(axisConfig.title.text);
67499 var titleBBox = title.node().getBBox();
67500 title.attr({
67501 x: chartCenter[0] - titleBBox.width / 2,
67502 y: chartCenter[1] - radius - 20
67503 });
67504 }
67505 var radialAxis = svg.select('.radial.axis-group');
67506 if (axisConfig.radialAxis.gridLinesVisible) {
67507 var gridCircles = radialAxis.selectAll('circle.grid-circle').data(radialScale.ticks(5));
67508 gridCircles.enter().append('circle').attr({
67509 'class': 'grid-circle'
67510 }).style(lineStyle);
67511 gridCircles.attr('r', radialScale);
67512 gridCircles.exit().remove();
67513 }
67514 radialAxis.select('circle.outside-circle').attr({
67515 r: radius
67516 }).style(lineStyle);
67517 var backgroundCircle = svg.select('circle.background-circle').attr({
67518 r: radius
67519 }).style({
67520 fill: axisConfig.backgroundColor,
67521 stroke: axisConfig.stroke
67522 });
67523 function currentAngle(d, i) {
67524 return angularScale(d) % 360 + axisConfig.orientation;
67525 }
67526 if (axisConfig.radialAxis.visible) {
67527 var axis = d3.svg.axis().scale(radialScale).ticks(5).tickSize(5);
67528 radialAxis.call(axis).attr({
67529 transform: 'rotate(' + axisConfig.radialAxis.orientation + ')'
67530 });
67531 radialAxis.selectAll('.domain').style(lineStyle);
67532 radialAxis.selectAll('g>text').text(function(d, i) {
67533 return this.textContent + axisConfig.radialAxis.ticksSuffix;
67534 }).style(fontStyle).style({
67535 'text-anchor': 'start'
67536 }).attr({
67537 x: 0,
67538 y: 0,
67539 dx: 0,
67540 dy: 0,
67541 transform: function(d, i) {
67542 if (axisConfig.radialAxis.tickOrientation === 'horizontal') {
67543 return 'rotate(' + -axisConfig.radialAxis.orientation + ') translate(' + [ 0, fontStyle['font-size'] ] + ')';
67544 } else return 'translate(' + [ 0, fontStyle['font-size'] ] + ')';
67545 }
67546 });
67547 radialAxis.selectAll('g>line').style({
67548 stroke: 'black'
67549 });
67550 }
67551 var angularAxis = svg.select('.angular.axis-group').selectAll('g.angular-tick').data(angularAxisRange);
67552 var angularAxisEnter = angularAxis.enter().append('g').classed('angular-tick', true);
67553 angularAxis.attr({
67554 transform: function(d, i) {
67555 return 'rotate(' + currentAngle(d, i) + ')';
67556 }
67557 }).style({
67558 display: axisConfig.angularAxis.visible ? 'block' : 'none'
67559 });
67560 angularAxis.exit().remove();
67561 angularAxisEnter.append('line').classed('grid-line', true).classed('major', function(d, i) {
67562 return i % (axisConfig.minorTicks + 1) == 0;
67563 }).classed('minor', function(d, i) {
67564 return !(i % (axisConfig.minorTicks + 1) == 0);
67565 }).style(lineStyle);
67566 angularAxisEnter.selectAll('.minor').style({
67567 stroke: axisConfig.minorTickColor
67568 });
67569 angularAxis.select('line.grid-line').attr({
67570 x1: axisConfig.tickLength ? radius - axisConfig.tickLength : 0,
67571 x2: radius
67572 }).style({
67573 display: axisConfig.angularAxis.gridLinesVisible ? 'block' : 'none'
67574 });
67575 angularAxisEnter.append('text').classed('axis-text', true).style(fontStyle);
67576 var ticksText = angularAxis.select('text.axis-text').attr({
67577 x: radius + axisConfig.labelOffset,
67578 dy: MID_SHIFT + 'em',
67579 transform: function(d, i) {
67580 var angle = currentAngle(d, i);
67581 var rad = radius + axisConfig.labelOffset;
67582 var orient = axisConfig.angularAxis.tickOrientation;
67583 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)';
67584 }
67585 }).style({
67586 'text-anchor': 'middle',
67587 display: axisConfig.angularAxis.labelsVisible ? 'block' : 'none'
67588 }).text(function(d, i) {
67589 if (i % (axisConfig.minorTicks + 1) != 0) return '';
67590 if (ticks) {
67591 return ticks[d] + axisConfig.angularAxis.ticksSuffix;
67592 } else return d + axisConfig.angularAxis.ticksSuffix;
67593 }).style(fontStyle);
67594 if (axisConfig.angularAxis.rewriteTicks) ticksText.text(function(d, i) {
67595 if (i % (axisConfig.minorTicks + 1) != 0) return '';
67596 return axisConfig.angularAxis.rewriteTicks(this.textContent, i);
67597 });
67598 var rightmostTickEndX = d3.max(chartGroup.selectAll('.angular-tick text')[0].map(function(d, i) {
67599 return d.getCTM().e + d.getBBox().width;
67600 }));
67601 legendContainer.attr({
67602 transform: 'translate(' + [ radius + rightmostTickEndX, axisConfig.margin.top ] + ')'
67603 });
67604 var hasGeometry = svg.select('g.geometry-group').selectAll('g').size() > 0;
67605 var geometryContainer = svg.select('g.geometry-group').selectAll('g.geometry').data(data);
67606 geometryContainer.enter().append('g').attr({
67607 'class': function(d, i) {
67608 return 'geometry geometry' + i;
67609 }
67610 });
67611 geometryContainer.exit().remove();
67612 if (data[0] || hasGeometry) {
67613 var geometryConfigs = [];
67614 data.forEach(function(d, i) {
67615 var geometryConfig = {};
67616 geometryConfig.radialScale = radialScale;
67617 geometryConfig.angularScale = angularScale;
67618 geometryConfig.container = geometryContainer.filter(function(dB, iB) {
67619 return iB == i;
67620 });
67621 geometryConfig.geometry = d.geometry;
67622 geometryConfig.orientation = axisConfig.orientation;
67623 geometryConfig.direction = axisConfig.direction;
67624 geometryConfig.index = i;
67625 geometryConfigs.push({
67626 data: d,
67627 geometryConfig: geometryConfig
67628 });
67629 });
67630 var geometryConfigsGrouped = d3.nest().key(function(d, i) {
67631 return typeof d.data.groupId != 'undefined' || 'unstacked';
67632 }).entries(geometryConfigs);
67633 var geometryConfigsGrouped2 = [];
67634 geometryConfigsGrouped.forEach(function(d, i) {
67635 if (d.key === 'unstacked') geometryConfigsGrouped2 = geometryConfigsGrouped2.concat(d.values.map(function(d, i) {
67636 return [ d ];
67637 })); else geometryConfigsGrouped2.push(d.values);
67638 });
67639 geometryConfigsGrouped2.forEach(function(d, i) {
67640 var geometry;
67641 if (Array.isArray(d)) geometry = d[0].geometryConfig.geometry; else geometry = d.geometryConfig.geometry;
67642 var finalGeometryConfig = d.map(function(dB, iB) {
67643 return extendDeepAll(µ[geometry].defaultConfig(), dB);
67644 });
67645 µ[geometry]().config(finalGeometryConfig)();
67646 });
67647 }
67648 var guides = svg.select('.guides-group');
67649 var tooltipContainer = svg.select('.tooltips-group');
67650 var angularTooltip = µ.tooltipPanel().config({
67651 container: tooltipContainer,
67652 fontSize: 8
67653 })();
67654 var radialTooltip = µ.tooltipPanel().config({
67655 container: tooltipContainer,
67656 fontSize: 8
67657 })();
67658 var geometryTooltip = µ.tooltipPanel().config({
67659 container: tooltipContainer,
67660 hasTick: true
67661 })();
67662 var angularValue, radialValue;
67663 if (!isOrdinal) {
67664 var angularGuideLine = guides.select('line').attr({
67665 x1: 0,
67666 y1: 0,
67667 y2: 0
67668 }).style({
67669 stroke: 'grey',
67670 'pointer-events': 'none'
67671 });
67672 chartGroup.on('mousemove.angular-guide', function(d, i) {
67673 var mouseAngle = µ.util.getMousePos(backgroundCircle).angle;
67674 angularGuideLine.attr({
67675 x2: -radius,
67676 transform: 'rotate(' + mouseAngle + ')'
67677 }).style({
67678 opacity: .5
67679 });
67680 var angleWithOriginOffset = (mouseAngle + 180 + 360 - axisConfig.orientation) % 360;
67681 angularValue = angularScale.invert(angleWithOriginOffset);
67682 var pos = µ.util.convertToCartesian(radius + 12, mouseAngle + 180);
67683 angularTooltip.text(µ.util.round(angularValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
67684 }).on('mouseout.angular-guide', function(d, i) {
67685 guides.select('line').style({
67686 opacity: 0
67687 });
67688 });
67689 }
67690 var angularGuideCircle = guides.select('circle').style({
67691 stroke: 'grey',
67692 fill: 'none'
67693 });
67694 chartGroup.on('mousemove.radial-guide', function(d, i) {
67695 var r = µ.util.getMousePos(backgroundCircle).radius;
67696 angularGuideCircle.attr({
67697 r: r
67698 }).style({
67699 opacity: .5
67700 });
67701 radialValue = radialScale.invert(µ.util.getMousePos(backgroundCircle).radius);
67702 var pos = µ.util.convertToCartesian(r, axisConfig.radialAxis.orientation);
67703 radialTooltip.text(µ.util.round(radialValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
67704 }).on('mouseout.radial-guide', function(d, i) {
67705 angularGuideCircle.style({
67706 opacity: 0
67707 });
67708 geometryTooltip.hide();
67709 angularTooltip.hide();
67710 radialTooltip.hide();
67711 });
67712 svg.selectAll('.geometry-group .mark').on('mouseover.tooltip', function(d, i) {
67713 var el = d3.select(this);
67714 var color = this.style.fill;
67715 var newColor = 'black';
67716 var opacity = this.style.opacity || 1;
67717 el.attr({
67718 'data-opacity': opacity
67719 });
67720 if (color && color !== 'none') {
67721 el.attr({
67722 'data-fill': color
67723 });
67724 newColor = d3.hsl(color).darker().toString();
67725 el.style({
67726 fill: newColor,
67727 opacity: 1
67728 });
67729 var textData = {
67730 t: µ.util.round(d[0]),
67731 r: µ.util.round(d[1])
67732 };
67733 if (isOrdinal) textData.t = ticks[d[0]];
67734 var text = 't: ' + textData.t + ', r: ' + textData.r;
67735 var bbox = this.getBoundingClientRect();
67736 var svgBBox = svg.node().getBoundingClientRect();
67737 var pos = [ bbox.left + bbox.width / 2 - centeringOffset[0] - svgBBox.left, bbox.top + bbox.height / 2 - centeringOffset[1] - svgBBox.top ];
67738 geometryTooltip.config({
67739 color: newColor
67740 }).text(text);
67741 geometryTooltip.move(pos);
67742 } else {
67743 color = this.style.stroke || 'black';
67744 el.attr({
67745 'data-stroke': color
67746 });
67747 newColor = d3.hsl(color).darker().toString();
67748 el.style({
67749 stroke: newColor,
67750 opacity: 1
67751 });
67752 }
67753 }).on('mousemove.tooltip', function(d, i) {
67754 if (d3.event.which != 0) return false;
67755 if (d3.select(this).attr('data-fill')) geometryTooltip.show();
67756 }).on('mouseout.tooltip', function(d, i) {
67757 geometryTooltip.hide();
67758 var el = d3.select(this);
67759 var fillColor = el.attr('data-fill');
67760 if (fillColor) el.style({
67761 fill: fillColor,
67762 opacity: el.attr('data-opacity')
67763 }); else el.style({
67764 stroke: el.attr('data-stroke'),
67765 opacity: el.attr('data-opacity')
67766 });
67767 });
67768 });
67769 return exports;
67770 }
67771 exports.render = function(_container) {
67772 render(_container);
67773 return this;
67774 };
67775 exports.config = function(_x) {
67776 if (!arguments.length) return config;
67777 var xClone = µ.util.cloneJson(_x);
67778 xClone.data.forEach(function(d, i) {
67779 if (!config.data[i]) config.data[i] = {};
67780 extendDeepAll(config.data[i], µ.Axis.defaultConfig().data[0]);
67781 extendDeepAll(config.data[i], d);
67782 });
67783 extendDeepAll(config.layout, µ.Axis.defaultConfig().layout);
67784 extendDeepAll(config.layout, xClone.layout);
67785 return this;
67786 };
67787 exports.getLiveConfig = function() {
67788 return liveConfig;
67789 };
67790 exports.getinputConfig = function() {
67791 return inputConfig;
67792 };
67793 exports.radialScale = function(_x) {
67794 return radialScale;
67795 };
67796 exports.angularScale = function(_x) {
67797 return angularScale;
67798 };
67799 exports.svg = function() {
67800 return svg;
67801 };
67802 d3.rebind(exports, dispatch, 'on');
67803 return exports;
67804};
67805
67806µ.Axis.defaultConfig = function(d, i) {
67807 var config = {
67808 data: [ {
67809 t: [ 1, 2, 3, 4 ],
67810 r: [ 10, 11, 12, 13 ],
67811 name: 'Line1',
67812 geometry: 'LinePlot',
67813 color: null,
67814 strokeDash: 'solid',
67815 strokeColor: null,
67816 strokeSize: '1',
67817 visibleInLegend: true,
67818 opacity: 1
67819 } ],
67820 layout: {
67821 defaultColorRange: d3.scale.category10().range(),
67822 title: null,
67823 height: 450,
67824 width: 500,
67825 margin: {
67826 top: 40,
67827 right: 40,
67828 bottom: 40,
67829 left: 40
67830 },
67831 font: {
67832 size: 12,
67833 color: 'gray',
67834 outlineColor: 'white',
67835 family: 'Tahoma, sans-serif'
67836 },
67837 direction: 'clockwise',
67838 orientation: 0,
67839 labelOffset: 10,
67840 radialAxis: {
67841 domain: null,
67842 orientation: -45,
67843 ticksSuffix: '',
67844 visible: true,
67845 gridLinesVisible: true,
67846 tickOrientation: 'horizontal',
67847 rewriteTicks: null
67848 },
67849 angularAxis: {
67850 domain: [ 0, 360 ],
67851 ticksSuffix: '',
67852 visible: true,
67853 gridLinesVisible: true,
67854 labelsVisible: true,
67855 tickOrientation: 'horizontal',
67856 rewriteTicks: null,
67857 ticksCount: null,
67858 ticksStep: null
67859 },
67860 minorTicks: 0,
67861 tickLength: null,
67862 tickColor: 'silver',
67863 minorTickColor: '#eee',
67864 backgroundColor: 'none',
67865 needsEndSpacing: null,
67866 showLegend: true,
67867 legend: {
67868 reverseOrder: false
67869 },
67870 opacity: 1
67871 }
67872 };
67873 return config;
67874};
67875
67876µ.util = {};
67877
67878µ.DATAEXTENT = 'dataExtent';
67879
67880µ.AREA = 'AreaChart';
67881
67882µ.LINE = 'LinePlot';
67883
67884µ.DOT = 'DotPlot';
67885
67886µ.BAR = 'BarChart';
67887
67888µ.util._override = function(_objA, _objB) {
67889 for (var x in _objA) if (x in _objB) _objB[x] = _objA[x];
67890};
67891
67892µ.util._extend = function(_objA, _objB) {
67893 for (var x in _objA) _objB[x] = _objA[x];
67894};
67895
67896µ.util._rndSnd = function() {
67897 return Math.random() * 2 - 1 + (Math.random() * 2 - 1) + (Math.random() * 2 - 1);
67898};
67899
67900µ.util.dataFromEquation2 = function(_equation, _step) {
67901 var step = _step || 6;
67902 var data = d3.range(0, 360 + step, step).map(function(deg, index) {
67903 var theta = deg * Math.PI / 180;
67904 var radius = _equation(theta);
67905 return [ deg, radius ];
67906 });
67907 return data;
67908};
67909
67910µ.util.dataFromEquation = function(_equation, _step, _name) {
67911 var step = _step || 6;
67912 var t = [], r = [];
67913 d3.range(0, 360 + step, step).forEach(function(deg, index) {
67914 var theta = deg * Math.PI / 180;
67915 var radius = _equation(theta);
67916 t.push(deg);
67917 r.push(radius);
67918 });
67919 var result = {
67920 t: t,
67921 r: r
67922 };
67923 if (_name) result.name = _name;
67924 return result;
67925};
67926
67927µ.util.ensureArray = function(_val, _count) {
67928 if (typeof _val === 'undefined') return null;
67929 var arr = [].concat(_val);
67930 return d3.range(_count).map(function(d, i) {
67931 return arr[i] || arr[0];
67932 });
67933};
67934
67935µ.util.fillArrays = function(_obj, _valueNames, _count) {
67936 _valueNames.forEach(function(d, i) {
67937 _obj[d] = µ.util.ensureArray(_obj[d], _count);
67938 });
67939 return _obj;
67940};
67941
67942µ.util.cloneJson = function(json) {
67943 return JSON.parse(JSON.stringify(json));
67944};
67945
67946µ.util.validateKeys = function(obj, keys) {
67947 if (typeof keys === 'string') keys = keys.split('.');
67948 var next = keys.shift();
67949 return obj[next] && (!keys.length || objHasKeys(obj[next], keys));
67950};
67951
67952µ.util.sumArrays = function(a, b) {
67953 return d3.zip(a, b).map(function(d, i) {
67954 return d3.sum(d);
67955 });
67956};
67957
67958µ.util.arrayLast = function(a) {
67959 return a[a.length - 1];
67960};
67961
67962µ.util.arrayEqual = function(a, b) {
67963 var i = Math.max(a.length, b.length, 1);
67964 while (i-- >= 0 && a[i] === b[i]) ;
67965 return i === -2;
67966};
67967
67968µ.util.flattenArray = function(arr) {
67969 var r = [];
67970 while (!µ.util.arrayEqual(r, arr)) {
67971 r = arr;
67972 arr = [].concat.apply([], arr);
67973 }
67974 return arr;
67975};
67976
67977µ.util.deduplicate = function(arr) {
67978 return arr.filter(function(v, i, a) {
67979 return a.indexOf(v) == i;
67980 });
67981};
67982
67983µ.util.convertToCartesian = function(radius, theta) {
67984 var thetaRadians = theta * Math.PI / 180;
67985 var x = radius * Math.cos(thetaRadians);
67986 var y = radius * Math.sin(thetaRadians);
67987 return [ x, y ];
67988};
67989
67990µ.util.round = function(_value, _digits) {
67991 var digits = _digits || 2;
67992 var mult = Math.pow(10, digits);
67993 return Math.round(_value * mult) / mult;
67994};
67995
67996µ.util.getMousePos = function(_referenceElement) {
67997 var mousePos = d3.mouse(_referenceElement.node());
67998 var mouseX = mousePos[0];
67999 var mouseY = mousePos[1];
68000 var mouse = {};
68001 mouse.x = mouseX;
68002 mouse.y = mouseY;
68003 mouse.pos = mousePos;
68004 mouse.angle = (Math.atan2(mouseY, mouseX) + Math.PI) * 180 / Math.PI;
68005 mouse.radius = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
68006 return mouse;
68007};
68008
68009µ.util.duplicatesCount = function(arr) {
68010 var uniques = {}, val;
68011 var dups = {};
68012 for (var i = 0, len = arr.length; i < len; i++) {
68013 val = arr[i];
68014 if (val in uniques) {
68015 uniques[val]++;
68016 dups[val] = uniques[val];
68017 } else {
68018 uniques[val] = 1;
68019 }
68020 }
68021 return dups;
68022};
68023
68024µ.util.duplicates = function(arr) {
68025 return Object.keys(µ.util.duplicatesCount(arr));
68026};
68027
68028µ.util.translator = function(obj, sourceBranch, targetBranch, reverse) {
68029 if (reverse) {
68030 var targetBranchCopy = targetBranch.slice();
68031 targetBranch = sourceBranch;
68032 sourceBranch = targetBranchCopy;
68033 }
68034 var value = sourceBranch.reduce(function(previousValue, currentValue) {
68035 if (typeof previousValue != 'undefined') return previousValue[currentValue];
68036 }, obj);
68037 if (typeof value === 'undefined') return;
68038 sourceBranch.reduce(function(previousValue, currentValue, index) {
68039 if (typeof previousValue == 'undefined') return;
68040 if (index === sourceBranch.length - 1) delete previousValue[currentValue];
68041 return previousValue[currentValue];
68042 }, obj);
68043 targetBranch.reduce(function(previousValue, currentValue, index) {
68044 if (typeof previousValue[currentValue] === 'undefined') previousValue[currentValue] = {};
68045 if (index === targetBranch.length - 1) previousValue[currentValue] = value;
68046 return previousValue[currentValue];
68047 }, obj);
68048};
68049
68050µ.PolyChart = function module() {
68051 var config = [ µ.PolyChart.defaultConfig() ];
68052 var dispatch = d3.dispatch('hover');
68053 var dashArray = {
68054 solid: 'none',
68055 dash: [ 5, 2 ],
68056 dot: [ 2, 5 ]
68057 };
68058 var colorScale;
68059 function exports() {
68060 var geometryConfig = config[0].geometryConfig;
68061 var container = geometryConfig.container;
68062 if (typeof container == 'string') container = d3.select(container);
68063 container.datum(config).each(function(_config, _index) {
68064 var isStack = !!_config[0].data.yStack;
68065 var data = _config.map(function(d, i) {
68066 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]);
68067 });
68068 var angularScale = geometryConfig.angularScale;
68069 var domainMin = geometryConfig.radialScale.domain()[0];
68070 var generator = {};
68071 generator.bar = function(d, i, pI) {
68072 var dataConfig = _config[pI].data;
68073 var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0);
68074 var stackTop = geometryConfig.radialScale(d[2] || 0);
68075 var w = dataConfig.barWidth;
68076 d3.select(this).attr({
68077 'class': 'mark bar',
68078 d: 'M' + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join('L') + 'Z',
68079 transform: function(d, i) {
68080 return 'rotate(' + (geometryConfig.orientation + angularScale(d[0])) + ')';
68081 }
68082 });
68083 };
68084 generator.dot = function(d, i, pI) {
68085 var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d;
68086 var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i);
68087 d3.select(this).attr({
68088 'class': 'mark dot',
68089 d: symbol,
68090 transform: function(d, i) {
68091 var coord = convertToCartesian(getPolarCoordinates(stackedData));
68092 return 'translate(' + [ coord.x, coord.y ] + ')';
68093 }
68094 });
68095 };
68096 var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) {
68097 return geometryConfig.radialScale(d[1]);
68098 }).angle(function(d) {
68099 return geometryConfig.angularScale(d[0]) * Math.PI / 180;
68100 });
68101 generator.line = function(d, i, pI) {
68102 var lineData = d[2] ? data[pI].map(function(d, i) {
68103 return [ d[0], d[1] + d[2] ];
68104 }) : data[pI];
68105 d3.select(this).each(generator['dot']).style({
68106 opacity: function(dB, iB) {
68107 return +_config[pI].data.dotVisible;
68108 },
68109 fill: markStyle.stroke(d, i, pI)
68110 }).attr({
68111 'class': 'mark dot'
68112 });
68113 if (i > 0) return;
68114 var lineSelection = d3.select(this.parentNode).selectAll('path.line').data([ 0 ]);
68115 lineSelection.enter().insert('path');
68116 lineSelection.attr({
68117 'class': 'line',
68118 d: line(lineData),
68119 transform: function(dB, iB) {
68120 return 'rotate(' + (geometryConfig.orientation + 90) + ')';
68121 },
68122 'pointer-events': 'none'
68123 }).style({
68124 fill: function(dB, iB) {
68125 return markStyle.fill(d, i, pI);
68126 },
68127 'fill-opacity': 0,
68128 stroke: function(dB, iB) {
68129 return markStyle.stroke(d, i, pI);
68130 },
68131 'stroke-width': function(dB, iB) {
68132 return markStyle['stroke-width'](d, i, pI);
68133 },
68134 'stroke-dasharray': function(dB, iB) {
68135 return markStyle['stroke-dasharray'](d, i, pI);
68136 },
68137 opacity: function(dB, iB) {
68138 return markStyle.opacity(d, i, pI);
68139 },
68140 display: function(dB, iB) {
68141 return markStyle.display(d, i, pI);
68142 }
68143 });
68144 };
68145 var angularRange = geometryConfig.angularScale.range();
68146 var triangleAngle = Math.abs(angularRange[1] - angularRange[0]) / data[0].length * Math.PI / 180;
68147 var arc = d3.svg.arc().startAngle(function(d) {
68148 return -triangleAngle / 2;
68149 }).endAngle(function(d) {
68150 return triangleAngle / 2;
68151 }).innerRadius(function(d) {
68152 return geometryConfig.radialScale(domainMin + (d[2] || 0));
68153 }).outerRadius(function(d) {
68154 return geometryConfig.radialScale(domainMin + (d[2] || 0)) + geometryConfig.radialScale(d[1]);
68155 });
68156 generator.arc = function(d, i, pI) {
68157 d3.select(this).attr({
68158 'class': 'mark arc',
68159 d: arc,
68160 transform: function(d, i) {
68161 return 'rotate(' + (geometryConfig.orientation + angularScale(d[0]) + 90) + ')';
68162 }
68163 });
68164 };
68165 var markStyle = {
68166 fill: function(d, i, pI) {
68167 return _config[pI].data.color;
68168 },
68169 stroke: function(d, i, pI) {
68170 return _config[pI].data.strokeColor;
68171 },
68172 'stroke-width': function(d, i, pI) {
68173 return _config[pI].data.strokeSize + 'px';
68174 },
68175 'stroke-dasharray': function(d, i, pI) {
68176 return dashArray[_config[pI].data.strokeDash];
68177 },
68178 opacity: function(d, i, pI) {
68179 return _config[pI].data.opacity;
68180 },
68181 display: function(d, i, pI) {
68182 return typeof _config[pI].data.visible === 'undefined' || _config[pI].data.visible ? 'block' : 'none';
68183 }
68184 };
68185 var geometryLayer = d3.select(this).selectAll('g.layer').data(data);
68186 geometryLayer.enter().append('g').attr({
68187 'class': 'layer'
68188 });
68189 var geometry = geometryLayer.selectAll('path.mark').data(function(d, i) {
68190 return d;
68191 });
68192 geometry.enter().append('path').attr({
68193 'class': 'mark'
68194 });
68195 geometry.style(markStyle).each(generator[geometryConfig.geometryType]);
68196 geometry.exit().remove();
68197 geometryLayer.exit().remove();
68198 function getPolarCoordinates(d, i) {
68199 var r = geometryConfig.radialScale(d[1]);
68200 var t = (geometryConfig.angularScale(d[0]) + geometryConfig.orientation) * Math.PI / 180;
68201 return {
68202 r: r,
68203 t: t
68204 };
68205 }
68206 function convertToCartesian(polarCoordinates) {
68207 var x = polarCoordinates.r * Math.cos(polarCoordinates.t);
68208 var y = polarCoordinates.r * Math.sin(polarCoordinates.t);
68209 return {
68210 x: x,
68211 y: y
68212 };
68213 }
68214 });
68215 }
68216 exports.config = function(_x) {
68217 if (!arguments.length) return config;
68218 _x.forEach(function(d, i) {
68219 if (!config[i]) config[i] = {};
68220 extendDeepAll(config[i], µ.PolyChart.defaultConfig());
68221 extendDeepAll(config[i], d);
68222 });
68223 return this;
68224 };
68225 exports.getColorScale = function() {
68226 return colorScale;
68227 };
68228 d3.rebind(exports, dispatch, 'on');
68229 return exports;
68230};
68231
68232µ.PolyChart.defaultConfig = function() {
68233 var config = {
68234 data: {
68235 name: 'geom1',
68236 t: [ [ 1, 2, 3, 4 ] ],
68237 r: [ [ 1, 2, 3, 4 ] ],
68238 dotType: 'circle',
68239 dotSize: 64,
68240 dotVisible: false,
68241 barWidth: 20,
68242 color: '#ffa500',
68243 strokeSize: 1,
68244 strokeColor: 'silver',
68245 strokeDash: 'solid',
68246 opacity: 1,
68247 index: 0,
68248 visible: true,
68249 visibleInLegend: true
68250 },
68251 geometryConfig: {
68252 geometry: 'LinePlot',
68253 geometryType: 'arc',
68254 direction: 'clockwise',
68255 orientation: 0,
68256 container: 'body',
68257 radialScale: null,
68258 angularScale: null,
68259 colorScale: d3.scale.category20()
68260 }
68261 };
68262 return config;
68263};
68264
68265µ.BarChart = function module() {
68266 return µ.PolyChart();
68267};
68268
68269µ.BarChart.defaultConfig = function() {
68270 var config = {
68271 geometryConfig: {
68272 geometryType: 'bar'
68273 }
68274 };
68275 return config;
68276};
68277
68278µ.AreaChart = function module() {
68279 return µ.PolyChart();
68280};
68281
68282µ.AreaChart.defaultConfig = function() {
68283 var config = {
68284 geometryConfig: {
68285 geometryType: 'arc'
68286 }
68287 };
68288 return config;
68289};
68290
68291µ.DotPlot = function module() {
68292 return µ.PolyChart();
68293};
68294
68295µ.DotPlot.defaultConfig = function() {
68296 var config = {
68297 geometryConfig: {
68298 geometryType: 'dot',
68299 dotType: 'circle'
68300 }
68301 };
68302 return config;
68303};
68304
68305µ.LinePlot = function module() {
68306 return µ.PolyChart();
68307};
68308
68309µ.LinePlot.defaultConfig = function() {
68310 var config = {
68311 geometryConfig: {
68312 geometryType: 'line'
68313 }
68314 };
68315 return config;
68316};
68317
68318µ.Legend = function module() {
68319 var config = µ.Legend.defaultConfig();
68320 var dispatch = d3.dispatch('hover');
68321 function exports() {
68322 var legendConfig = config.legendConfig;
68323 var flattenData = config.data.map(function(d, i) {
68324 return [].concat(d).map(function(dB, iB) {
68325 var element = extendDeepAll({}, legendConfig.elements[i]);
68326 element.name = dB;
68327 element.color = [].concat(legendConfig.elements[i].color)[iB];
68328 return element;
68329 });
68330 });
68331 var data = d3.merge(flattenData);
68332 data = data.filter(function(d, i) {
68333 return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend === 'undefined');
68334 });
68335 if (legendConfig.reverseOrder) data = data.reverse();
68336 var container = legendConfig.container;
68337 if (typeof container == 'string' || container.nodeName) container = d3.select(container);
68338 var colors = data.map(function(d, i) {
68339 return d.color;
68340 });
68341 var lineHeight = legendConfig.fontSize;
68342 var isContinuous = legendConfig.isContinuous == null ? typeof data[0] === 'number' : legendConfig.isContinuous;
68343 var height = isContinuous ? legendConfig.height : lineHeight * data.length;
68344 var legendContainerGroup = container.classed('legend-group', true);
68345 var svg = legendContainerGroup.selectAll('svg').data([ 0 ]);
68346 var svgEnter = svg.enter().append('svg').attr({
68347 width: 300,
68348 height: height + lineHeight,
68349 xmlns: 'http://www.w3.org/2000/svg',
68350 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
68351 version: '1.1'
68352 });
68353 svgEnter.append('g').classed('legend-axis', true);
68354 svgEnter.append('g').classed('legend-marks', true);
68355 var dataNumbered = d3.range(data.length);
68356 var colorScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered).range(colors);
68357 var dataScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered)[isContinuous ? 'range' : 'rangePoints']([ 0, height ]);
68358 var shapeGenerator = function(_type, _size) {
68359 var squareSize = _size * 3;
68360 if (_type === 'line') {
68361 return 'M' + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2, _size / 12 ] ] + 'Z';
68362 } 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)();
68363 };
68364 if (isContinuous) {
68365 var gradient = svg.select('.legend-marks').append('defs').append('linearGradient').attr({
68366 id: 'grad1',
68367 x1: '0%',
68368 y1: '0%',
68369 x2: '0%',
68370 y2: '100%'
68371 }).selectAll('stop').data(colors);
68372 gradient.enter().append('stop');
68373 gradient.attr({
68374 offset: function(d, i) {
68375 return i / (colors.length - 1) * 100 + '%';
68376 }
68377 }).style({
68378 'stop-color': function(d, i) {
68379 return d;
68380 }
68381 });
68382 svg.append('rect').classed('legend-mark', true).attr({
68383 height: legendConfig.height,
68384 width: legendConfig.colorBandWidth,
68385 fill: 'url(#grad1)'
68386 });
68387 } else {
68388 var legendElement = svg.select('.legend-marks').selectAll('path.legend-mark').data(data);
68389 legendElement.enter().append('path').classed('legend-mark', true);
68390 legendElement.attr({
68391 transform: function(d, i) {
68392 return 'translate(' + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ')';
68393 },
68394 d: function(d, i) {
68395 var symbolType = d.symbol;
68396 return shapeGenerator(symbolType, lineHeight);
68397 },
68398 fill: function(d, i) {
68399 return colorScale(i);
68400 }
68401 });
68402 legendElement.exit().remove();
68403 }
68404 var legendAxis = d3.svg.axis().scale(dataScale).orient('right');
68405 var axis = svg.select('g.legend-axis').attr({
68406 transform: 'translate(' + [ isContinuous ? legendConfig.colorBandWidth : lineHeight, lineHeight / 2 ] + ')'
68407 }).call(legendAxis);
68408 axis.selectAll('.domain').style({
68409 fill: 'none',
68410 stroke: 'none'
68411 });
68412 axis.selectAll('line').style({
68413 fill: 'none',
68414 stroke: isContinuous ? legendConfig.textColor : 'none'
68415 });
68416 axis.selectAll('text').style({
68417 fill: legendConfig.textColor,
68418 'font-size': legendConfig.fontSize
68419 }).text(function(d, i) {
68420 return data[i].name;
68421 });
68422 return exports;
68423 }
68424 exports.config = function(_x) {
68425 if (!arguments.length) return config;
68426 extendDeepAll(config, _x);
68427 return this;
68428 };
68429 d3.rebind(exports, dispatch, 'on');
68430 return exports;
68431};
68432
68433µ.Legend.defaultConfig = function(d, i) {
68434 var config = {
68435 data: [ 'a', 'b', 'c' ],
68436 legendConfig: {
68437 elements: [ {
68438 symbol: 'line',
68439 color: 'red'
68440 }, {
68441 symbol: 'square',
68442 color: 'yellow'
68443 }, {
68444 symbol: 'diamond',
68445 color: 'limegreen'
68446 } ],
68447 height: 150,
68448 colorBandWidth: 30,
68449 fontSize: 12,
68450 container: 'body',
68451 isContinuous: null,
68452 textColor: 'grey',
68453 reverseOrder: false
68454 }
68455 };
68456 return config;
68457};
68458
68459µ.tooltipPanel = function() {
68460 var tooltipEl, tooltipTextEl, backgroundEl;
68461 var config = {
68462 container: null,
68463 hasTick: false,
68464 fontSize: 12,
68465 color: 'white',
68466 padding: 5
68467 };
68468 var id = 'tooltip-' + µ.tooltipPanel.uid++;
68469 var tickSize = 10;
68470 var exports = function() {
68471 tooltipEl = config.container.selectAll('g.' + id).data([ 0 ]);
68472 var tooltipEnter = tooltipEl.enter().append('g').classed(id, true).style({
68473 'pointer-events': 'none',
68474 display: 'none'
68475 });
68476 backgroundEl = tooltipEnter.append('path').style({
68477 fill: 'white',
68478 'fill-opacity': .9
68479 }).attr({
68480 d: 'M0 0'
68481 });
68482 tooltipTextEl = tooltipEnter.append('text').attr({
68483 dx: config.padding + tickSize,
68484 dy: +config.fontSize * .3
68485 });
68486 return exports;
68487 };
68488 exports.text = function(_text) {
68489 var l = d3.hsl(config.color).l;
68490 var strokeColor = l >= .5 ? '#aaa' : 'white';
68491 var fillColor = l >= .5 ? 'black' : 'white';
68492 var text = _text || '';
68493 tooltipTextEl.style({
68494 fill: fillColor,
68495 'font-size': config.fontSize + 'px'
68496 }).text(text);
68497 var padding = config.padding;
68498 var bbox = tooltipTextEl.node().getBBox();
68499 var boxStyle = {
68500 fill: config.color,
68501 stroke: strokeColor,
68502 'stroke-width': '2px'
68503 };
68504 var backGroundW = bbox.width + padding * 2 + tickSize;
68505 var backGroundH = bbox.height + padding * 2;
68506 backgroundEl.attr({
68507 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'
68508 }).style(boxStyle);
68509 tooltipEl.attr({
68510 transform: 'translate(' + [ tickSize, -backGroundH / 2 + padding * 2 ] + ')'
68511 });
68512 tooltipEl.style({
68513 display: 'block'
68514 });
68515 return exports;
68516 };
68517 exports.move = function(_pos) {
68518 if (!tooltipEl) return;
68519 tooltipEl.attr({
68520 transform: 'translate(' + [ _pos[0], _pos[1] ] + ')'
68521 }).style({
68522 display: 'block'
68523 });
68524 return exports;
68525 };
68526 exports.hide = function() {
68527 if (!tooltipEl) return;
68528 tooltipEl.style({
68529 display: 'none'
68530 });
68531 return exports;
68532 };
68533 exports.show = function() {
68534 if (!tooltipEl) return;
68535 tooltipEl.style({
68536 display: 'block'
68537 });
68538 return exports;
68539 };
68540 exports.config = function(_x) {
68541 extendDeepAll(config, _x);
68542 return exports;
68543 };
68544 return exports;
68545};
68546
68547µ.tooltipPanel.uid = 1;
68548
68549µ.adapter = {};
68550
68551µ.adapter.plotly = function module() {
68552 var exports = {};
68553 exports.convert = function(_inputConfig, reverse) {
68554 var outputConfig = {};
68555 if (_inputConfig.data) {
68556 outputConfig.data = _inputConfig.data.map(function(d, i) {
68557 var r = extendDeepAll({}, d);
68558 var toTranslate = [
68559 [ r, [ 'marker', 'color' ], [ 'color' ] ],
68560 [ r, [ 'marker', 'opacity' ], [ 'opacity' ] ],
68561 [ r, [ 'marker', 'line', 'color' ], [ 'strokeColor' ] ],
68562 [ r, [ 'marker', 'line', 'dash' ], [ 'strokeDash' ] ],
68563 [ r, [ 'marker', 'line', 'width' ], [ 'strokeSize' ] ],
68564 [ r, [ 'marker', 'symbol' ], [ 'dotType' ] ],
68565 [ r, [ 'marker', 'size' ], [ 'dotSize' ] ],
68566 [ r, [ 'marker', 'barWidth' ], [ 'barWidth' ] ],
68567 [ r, [ 'line', 'interpolation' ], [ 'lineInterpolation' ] ],
68568 [ r, [ 'showlegend' ], [ 'visibleInLegend' ] ]
68569 ];
68570 toTranslate.forEach(function(d, i) {
68571 µ.util.translator.apply(null, d.concat(reverse));
68572 });
68573
68574 if (!reverse) delete r.marker;
68575 if (reverse) delete r.groupId;
68576 if (!reverse) {
68577 if (r.type === 'scatter') {
68578 if (r.mode === 'lines') r.geometry = 'LinePlot'; else if (r.mode === 'markers') r.geometry = 'DotPlot'; else if (r.mode === 'lines+markers') {
68579 r.geometry = 'LinePlot';
68580 r.dotVisible = true;
68581 }
68582 } else if (r.type === 'area') r.geometry = 'AreaChart'; else if (r.type === 'bar') r.geometry = 'BarChart';
68583 delete r.mode;
68584 delete r.type;
68585 } else {
68586 if (r.geometry === 'LinePlot') {
68587 r.type = 'scatter';
68588 if (r.dotVisible === true) {
68589 delete r.dotVisible;
68590 r.mode = 'lines+markers';
68591 } else r.mode = 'lines';
68592 } else if (r.geometry === 'DotPlot') {
68593 r.type = 'scatter';
68594 r.mode = 'markers';
68595 } else if (r.geometry === 'AreaChart') r.type = 'area'; else if (r.geometry === 'BarChart') r.type = 'bar';
68596 delete r.geometry;
68597 }
68598 return r;
68599 });
68600 if (!reverse && _inputConfig.layout && _inputConfig.layout.barmode === 'stack') {
68601 var duplicates = µ.util.duplicates(outputConfig.data.map(function(d, i) {
68602 return d.geometry;
68603 }));
68604 outputConfig.data.forEach(function(d, i) {
68605 var idx = duplicates.indexOf(d.geometry);
68606 if (idx != -1) outputConfig.data[i].groupId = idx;
68607 });
68608 }
68609 }
68610 if (_inputConfig.layout) {
68611 var r = extendDeepAll({}, _inputConfig.layout);
68612 var toTranslate = [
68613 [ r, [ 'plot_bgcolor' ], [ 'backgroundColor' ] ],
68614 [ r, [ 'showlegend' ], [ 'showLegend' ] ],
68615 [ r, [ 'radialaxis' ], [ 'radialAxis' ] ],
68616 [ r, [ 'angularaxis' ], [ 'angularAxis' ] ],
68617 [ r.angularaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
68618 [ r.angularaxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
68619 [ r.angularaxis, [ 'nticks' ], [ 'ticksCount' ] ],
68620 [ r.angularaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
68621 [ r.angularaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
68622 [ r.angularaxis, [ 'range' ], [ 'domain' ] ],
68623 [ r.angularaxis, [ 'endpadding' ], [ 'endPadding' ] ],
68624 [ r.radialaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
68625 [ r.radialaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
68626 [ r.radialaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
68627 [ r.radialaxis, [ 'range' ], [ 'domain' ] ],
68628 [ r.angularAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
68629 [ r.angularAxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
68630 [ r.angularAxis, [ 'nticks' ], [ 'ticksCount' ] ],
68631 [ r.angularAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
68632 [ r.angularAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
68633 [ r.angularAxis, [ 'range' ], [ 'domain' ] ],
68634 [ r.angularAxis, [ 'endpadding' ], [ 'endPadding' ] ],
68635 [ r.radialAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
68636 [ r.radialAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
68637 [ r.radialAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
68638 [ r.radialAxis, [ 'range' ], [ 'domain' ] ],
68639 [ r.font, [ 'outlinecolor' ], [ 'outlineColor' ] ],
68640 [ r.legend, [ 'traceorder' ], [ 'reverseOrder' ] ],
68641 [ r, [ 'labeloffset' ], [ 'labelOffset' ] ],
68642 [ r, [ 'defaultcolorrange' ], [ 'defaultColorRange' ] ]
68643 ];
68644 toTranslate.forEach(function(d, i) {
68645 µ.util.translator.apply(null, d.concat(reverse));
68646 });
68647
68648 if (!reverse) {
68649 if (r.angularAxis && typeof r.angularAxis.ticklen !== 'undefined') r.tickLength = r.angularAxis.ticklen;
68650 if (r.angularAxis && typeof r.angularAxis.tickcolor !== 'undefined') r.tickColor = r.angularAxis.tickcolor;
68651 } else {
68652 if (typeof r.tickLength !== 'undefined') {
68653 r.angularaxis.ticklen = r.tickLength;
68654 delete r.tickLength;
68655 }
68656 if (r.tickColor) {
68657 r.angularaxis.tickcolor = r.tickColor;
68658 delete r.tickColor;
68659 }
68660 }
68661 if (r.legend && typeof r.legend.reverseOrder != 'boolean') {
68662 r.legend.reverseOrder = r.legend.reverseOrder != 'normal';
68663 }
68664 if (r.legend && typeof r.legend.traceorder == 'boolean') {
68665 r.legend.traceorder = r.legend.traceorder ? 'reversed' : 'normal';
68666 delete r.legend.reverseOrder;
68667 }
68668 if (r.margin && typeof r.margin.t != 'undefined') {
68669 var source = [ 't', 'r', 'b', 'l', 'pad' ];
68670 var target = [ 'top', 'right', 'bottom', 'left', 'pad' ];
68671 var margin = {};
68672 d3.entries(r.margin).forEach(function(dB, iB) {
68673 margin[target[source.indexOf(dB.key)]] = dB.value;
68674 });
68675 r.margin = margin;
68676 }
68677 if (reverse) {
68678 delete r.needsEndSpacing;
68679 delete r.minorTickColor;
68680 delete r.minorTicks;
68681 delete r.angularaxis.ticksCount;
68682 delete r.angularaxis.ticksCount;
68683 delete r.angularaxis.ticksStep;
68684 delete r.angularaxis.rewriteTicks;
68685 delete r.angularaxis.nticks;
68686 delete r.radialaxis.ticksCount;
68687 delete r.radialaxis.ticksCount;
68688 delete r.radialaxis.ticksStep;
68689 delete r.radialaxis.rewriteTicks;
68690 delete r.radialaxis.nticks;
68691 }
68692 outputConfig.layout = r;
68693 }
68694 return outputConfig;
68695 };
68696 return exports;
68697};
68698
68699},{"../../../constants/alignment":154,"../../../lib":178,"d3":16}],261:[function(_dereq_,module,exports){
68700/**
68701* Copyright 2012-2020, Plotly, Inc.
68702* All rights reserved.
68703*
68704* This source code is licensed under the MIT license found in the
68705* LICENSE file in the root directory of this source tree.
68706*/
68707
68708/* eslint-disable new-cap */
68709
68710'use strict';
68711
68712var d3 = _dereq_('d3');
68713var Lib = _dereq_('../../../lib');
68714var Color = _dereq_('../../../components/color');
68715
68716var micropolar = _dereq_('./micropolar');
68717var UndoManager = _dereq_('./undo_manager');
68718var extendDeepAll = Lib.extendDeepAll;
68719
68720var manager = module.exports = {};
68721
68722manager.framework = function(_gd) {
68723 var config, previousConfigClone, plot, convertedInput, container;
68724 var undoManager = new UndoManager();
68725
68726 function exports(_inputConfig, _container) {
68727 if(_container) container = _container;
68728 d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove();
68729
68730 config = (!config) ?
68731 _inputConfig :
68732 extendDeepAll(config, _inputConfig);
68733
68734 if(!plot) plot = micropolar.Axis();
68735 convertedInput = micropolar.adapter.plotly().convert(config);
68736 plot.config(convertedInput).render(container);
68737 _gd.data = config.data;
68738 _gd.layout = config.layout;
68739 manager.fillLayout(_gd);
68740 return config;
68741 }
68742 exports.isPolar = true;
68743 exports.svg = function() { return plot.svg(); };
68744 exports.getConfig = function() { return config; };
68745 exports.getLiveConfig = function() {
68746 return micropolar.adapter.plotly().convert(plot.getLiveConfig(), true);
68747 };
68748 exports.getLiveScales = function() { return {t: plot.angularScale(), r: plot.radialScale()}; };
68749 exports.setUndoPoint = function() {
68750 var that = this;
68751 var configClone = micropolar.util.cloneJson(config);
68752 (function(_configClone, _previousConfigClone) {
68753 undoManager.add({
68754 undo: function() {
68755 if(_previousConfigClone) that(_previousConfigClone);
68756 },
68757 redo: function() {
68758 that(_configClone);
68759 }
68760 });
68761 })(configClone, previousConfigClone);
68762 previousConfigClone = micropolar.util.cloneJson(configClone);
68763 };
68764 exports.undo = function() { undoManager.undo(); };
68765 exports.redo = function() { undoManager.redo(); };
68766 return exports;
68767};
68768
68769manager.fillLayout = function(_gd) {
68770 var container = d3.select(_gd).selectAll('.plot-container');
68771 var paperDiv = container.selectAll('.svg-container');
68772 var paper = _gd.framework && _gd.framework.svg && _gd.framework.svg();
68773 var dflts = {
68774 width: 800,
68775 height: 600,
68776 paper_bgcolor: Color.background,
68777 _container: container,
68778 _paperdiv: paperDiv,
68779 _paper: paper
68780 };
68781
68782 _gd._fullLayout = extendDeepAll(dflts, _gd.layout);
68783};
68784
68785},{"../../../components/color":52,"../../../lib":178,"./micropolar":260,"./undo_manager":262,"d3":16}],262:[function(_dereq_,module,exports){
68786/**
68787* Copyright 2012-2020, Plotly, Inc.
68788* All rights reserved.
68789*
68790* This source code is licensed under the MIT license found in the
68791* LICENSE file in the root directory of this source tree.
68792*/
68793
68794'use strict';
68795
68796// Modified from https://github.com/ArthurClemens/Javascript-Undo-Manager
68797// Copyright (c) 2010-2013 Arthur Clemens, arthur@visiblearea.com
68798module.exports = function UndoManager() {
68799 var undoCommands = [];
68800 var index = -1;
68801 var isExecuting = false;
68802 var callback;
68803
68804 function execute(command, action) {
68805 if(!command) return this;
68806
68807 isExecuting = true;
68808 command[action]();
68809 isExecuting = false;
68810
68811 return this;
68812 }
68813
68814 return {
68815 add: function(command) {
68816 if(isExecuting) return this;
68817 undoCommands.splice(index + 1, undoCommands.length - index);
68818 undoCommands.push(command);
68819 index = undoCommands.length - 1;
68820 return this;
68821 },
68822 setCallback: function(callbackFunc) { callback = callbackFunc; },
68823 undo: function() {
68824 var command = undoCommands[index];
68825 if(!command) return this;
68826 execute(command, 'undo');
68827 index -= 1;
68828 if(callback) callback(command.undo);
68829 return this;
68830 },
68831 redo: function() {
68832 var command = undoCommands[index + 1];
68833 if(!command) return this;
68834 execute(command, 'redo');
68835 index += 1;
68836 if(callback) callback(command.redo);
68837 return this;
68838 },
68839 clear: function() {
68840 undoCommands = [];
68841 index = -1;
68842 },
68843 hasUndo: function() { return index !== -1; },
68844 hasRedo: function() { return index < (undoCommands.length - 1); },
68845 getCommands: function() { return undoCommands; },
68846 getPreviousCommand: function() { return undoCommands[index - 1]; },
68847 getIndex: function() { return index; }
68848 };
68849};
68850
68851},{}],263:[function(_dereq_,module,exports){
68852/**
68853* Copyright 2012-2020, Plotly, Inc.
68854* All rights reserved.
68855*
68856* This source code is licensed under the MIT license found in the
68857* LICENSE file in the root directory of this source tree.
68858*/
68859
68860
68861'use strict';
68862
68863var Lib = _dereq_('../lib');
68864var Template = _dereq_('../plot_api/plot_template');
68865var handleDomainDefaults = _dereq_('./domain').defaults;
68866
68867
68868/**
68869 * Find and supply defaults to all subplots of a given type
68870 * This handles subplots that are contained within one container - so
68871 * gl3d, geo, ternary... but not 2d axes which have separate x and y axes
68872 * finds subplots, coerces their `domain` attributes, then calls the
68873 * given handleDefaults function to fill in everything else.
68874 *
68875 * layoutIn: the complete user-supplied input layout
68876 * layoutOut: the complete finished layout
68877 * fullData: the finished data array, used only to find subplots
68878 * opts: {
68879 * type: subplot type string
68880 * attributes: subplot attributes object
68881 * partition: 'x' or 'y', which direction to divide domain space by default
68882 * (default 'x', ie side-by-side subplots)
68883 * TODO: this option is only here because 3D and geo made opposite
68884 * choices in this regard previously and I didn't want to change it.
68885 * Instead we should do:
68886 * - something consistent
68887 * - something more square (4 cuts 2x2, 5/6 cuts 2x3, etc.)
68888 * - something that includes all subplot types in one arrangement,
68889 * now that we can have them together!
68890 * handleDefaults: function of (subplotLayoutIn, subplotLayoutOut, coerce, opts)
68891 * this opts object is passed through to handleDefaults, so attach any
68892 * additional items needed by this function here as well
68893 * }
68894 */
68895module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) {
68896 var subplotType = opts.type;
68897 var subplotAttributes = opts.attributes;
68898 var handleDefaults = opts.handleDefaults;
68899 var partition = opts.partition || 'x';
68900
68901 var ids = layoutOut._subplots[subplotType];
68902 var idsLength = ids.length;
68903
68904 var baseId = idsLength && ids[0].replace(/\d+$/, '');
68905
68906 var subplotLayoutIn, subplotLayoutOut;
68907
68908 function coerce(attr, dflt) {
68909 return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt);
68910 }
68911
68912 for(var i = 0; i < idsLength; i++) {
68913 var id = ids[i];
68914
68915 // ternary traces get a layout ternary for free!
68916 if(layoutIn[id]) subplotLayoutIn = layoutIn[id];
68917 else subplotLayoutIn = layoutIn[id] = {};
68918
68919 subplotLayoutOut = Template.newContainer(layoutOut, id, baseId);
68920
68921 // All subplot containers get a `uirevision` inheriting from the base.
68922 // Currently all subplots containers have some user interaction
68923 // attributes, but if we ever add one that doesn't, we would need an
68924 // option to skip this step.
68925 coerce('uirevision', layoutOut.uirevision);
68926
68927 var dfltDomains = {};
68928 dfltDomains[partition] = [i / idsLength, (i + 1) / idsLength];
68929 handleDomainDefaults(subplotLayoutOut, layoutOut, coerce, dfltDomains);
68930
68931 opts.id = id;
68932 handleDefaults(subplotLayoutIn, subplotLayoutOut, coerce, opts);
68933 }
68934};
68935
68936},{"../lib":178,"../plot_api/plot_template":212,"./domain":249}],264:[function(_dereq_,module,exports){
68937/**
68938* Copyright 2012-2020, Plotly, Inc.
68939* All rights reserved.
68940*
68941* This source code is licensed under the MIT license found in the
68942* LICENSE file in the root directory of this source tree.
68943*/
68944
68945'use strict';
68946
68947var FORMAT_LINK = _dereq_('../constants/docs').FORMAT_LINK;
68948var DATE_FORMAT_LINK = _dereq_('../constants/docs').DATE_FORMAT_LINK;
68949
68950var templateFormatStringDescription = [
68951 'Variables are inserted using %{variable}, for example "y: %{y}".',
68952 'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".',
68953 FORMAT_LINK,
68954 'for details on the formatting syntax.',
68955 'Dates are formatted using d3-time-format\'s syntax %{variable|d3-time-format}, for example "Day: %{2019-01-01|%A}".',
68956 DATE_FORMAT_LINK,
68957 'for details on the date formatting syntax.'
68958].join(' ');
68959
68960function describeVariables(extra) {
68961 var descPart = extra.description ? ' ' + extra.description : '';
68962 var keys = extra.keys || [];
68963 if(keys.length > 0) {
68964 var quotedKeys = [];
68965 for(var i = 0; i < keys.length; i++) {
68966 quotedKeys[i] = '`' + keys[i] + '`';
68967 }
68968 descPart = descPart + 'Finally, the template string has access to ';
68969 if(keys.length === 1) {
68970 descPart = 'variable ' + quotedKeys[0];
68971 } else {
68972 descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.';
68973 }
68974 }
68975 return descPart;
68976}
68977
68978exports.hovertemplateAttrs = function(opts, extra) {
68979 opts = opts || {};
68980 extra = extra || {};
68981
68982 var descPart = describeVariables(extra);
68983
68984 var hovertemplate = {
68985 valType: 'string',
68986
68987 dflt: '',
68988 editType: opts.editType || 'none',
68989
68990 };
68991
68992 if(opts.arrayOk !== false) {
68993 hovertemplate.arrayOk = true;
68994 }
68995
68996 return hovertemplate;
68997};
68998
68999exports.texttemplateAttrs = function(opts, extra) {
69000 opts = opts || {};
69001 extra = extra || {};
69002
69003 var descPart = describeVariables(extra);
69004
69005 var texttemplate = {
69006 valType: 'string',
69007
69008 dflt: '',
69009 editType: opts.editType || 'calc',
69010
69011 };
69012
69013 if(opts.arrayOk !== false) {
69014 texttemplate.arrayOk = true;
69015 }
69016 return texttemplate;
69017};
69018
69019},{"../constants/docs":155}],265:[function(_dereq_,module,exports){
69020/**
69021* Copyright 2012-2020, Plotly, Inc.
69022* All rights reserved.
69023*
69024* This source code is licensed under the MIT license found in the
69025* LICENSE file in the root directory of this source tree.
69026*/
69027
69028
69029'use strict';
69030
69031var Ternary = _dereq_('./ternary');
69032
69033var getSubplotCalcData = _dereq_('../../plots/get_data').getSubplotCalcData;
69034var counterRegex = _dereq_('../../lib').counterRegex;
69035var TERNARY = 'ternary';
69036
69037exports.name = TERNARY;
69038
69039var attr = exports.attr = 'subplot';
69040
69041exports.idRoot = TERNARY;
69042
69043exports.idRegex = exports.attrRegex = counterRegex(TERNARY);
69044
69045var attributes = exports.attributes = {};
69046attributes[attr] = {
69047 valType: 'subplotid',
69048
69049 dflt: 'ternary',
69050 editType: 'calc',
69051
69052};
69053
69054exports.layoutAttributes = _dereq_('./layout_attributes');
69055
69056exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
69057
69058exports.plot = function plot(gd) {
69059 var fullLayout = gd._fullLayout;
69060 var calcData = gd.calcdata;
69061 var ternaryIds = fullLayout._subplots[TERNARY];
69062
69063 for(var i = 0; i < ternaryIds.length; i++) {
69064 var ternaryId = ternaryIds[i];
69065 var ternaryCalcData = getSubplotCalcData(calcData, TERNARY, ternaryId);
69066 var ternary = fullLayout[ternaryId]._subplot;
69067
69068 // If ternary is not instantiated, create one!
69069 if(!ternary) {
69070 ternary = new Ternary({
69071 id: ternaryId,
69072 graphDiv: gd,
69073 container: fullLayout._ternarylayer.node()
69074 },
69075 fullLayout
69076 );
69077
69078 fullLayout[ternaryId]._subplot = ternary;
69079 }
69080
69081 ternary.plot(ternaryCalcData, fullLayout, gd._promises);
69082 }
69083};
69084
69085exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
69086 var oldTernaryKeys = oldFullLayout._subplots[TERNARY] || [];
69087
69088 for(var i = 0; i < oldTernaryKeys.length; i++) {
69089 var oldTernaryKey = oldTernaryKeys[i];
69090 var oldTernary = oldFullLayout[oldTernaryKey]._subplot;
69091
69092 if(!newFullLayout[oldTernaryKey] && !!oldTernary) {
69093 oldTernary.plotContainer.remove();
69094 oldTernary.clipDef.remove();
69095 oldTernary.clipDefRelative.remove();
69096 oldTernary.layers['a-title'].remove();
69097 oldTernary.layers['b-title'].remove();
69098 oldTernary.layers['c-title'].remove();
69099 }
69100 }
69101};
69102
69103},{"../../lib":178,"../../plots/get_data":252,"./layout_attributes":266,"./layout_defaults":267,"./ternary":268}],266:[function(_dereq_,module,exports){
69104/**
69105* Copyright 2012-2020, Plotly, Inc.
69106* All rights reserved.
69107*
69108* This source code is licensed under the MIT license found in the
69109* LICENSE file in the root directory of this source tree.
69110*/
69111
69112'use strict';
69113
69114var colorAttrs = _dereq_('../../components/color/attributes');
69115var domainAttrs = _dereq_('../domain').attributes;
69116var axesAttrs = _dereq_('../cartesian/layout_attributes');
69117
69118var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
69119var extendFlat = _dereq_('../../lib/extend').extendFlat;
69120
69121var ternaryAxesAttrs = {
69122 title: {
69123 text: axesAttrs.title.text,
69124 font: axesAttrs.title.font
69125 // TODO does standoff here make sense?
69126 },
69127 color: axesAttrs.color,
69128 // ticks
69129 tickmode: axesAttrs.tickmode,
69130 nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}),
69131 tick0: axesAttrs.tick0,
69132 dtick: axesAttrs.dtick,
69133 tickvals: axesAttrs.tickvals,
69134 ticktext: axesAttrs.ticktext,
69135 ticks: axesAttrs.ticks,
69136 ticklen: axesAttrs.ticklen,
69137 tickwidth: axesAttrs.tickwidth,
69138 tickcolor: axesAttrs.tickcolor,
69139 showticklabels: axesAttrs.showticklabels,
69140 showtickprefix: axesAttrs.showtickprefix,
69141 tickprefix: axesAttrs.tickprefix,
69142 showticksuffix: axesAttrs.showticksuffix,
69143 ticksuffix: axesAttrs.ticksuffix,
69144 showexponent: axesAttrs.showexponent,
69145 exponentformat: axesAttrs.exponentformat,
69146 separatethousands: axesAttrs.separatethousands,
69147 tickfont: axesAttrs.tickfont,
69148 tickangle: axesAttrs.tickangle,
69149 tickformat: axesAttrs.tickformat,
69150 tickformatstops: axesAttrs.tickformatstops,
69151 hoverformat: axesAttrs.hoverformat,
69152 // lines and grids
69153 showline: extendFlat({}, axesAttrs.showline, {dflt: true}),
69154 linecolor: axesAttrs.linecolor,
69155 linewidth: axesAttrs.linewidth,
69156 showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
69157 gridcolor: axesAttrs.gridcolor,
69158 gridwidth: axesAttrs.gridwidth,
69159 layer: axesAttrs.layer,
69160 // range
69161 min: {
69162 valType: 'number',
69163 dflt: 0,
69164
69165 min: 0,
69166
69167 },
69168 _deprecated: {
69169 title: axesAttrs._deprecated.title,
69170 titlefont: axesAttrs._deprecated.titlefont
69171 }
69172};
69173
69174var attrs = module.exports = overrideAll({
69175 domain: domainAttrs({name: 'ternary'}),
69176
69177 bgcolor: {
69178 valType: 'color',
69179
69180 dflt: colorAttrs.background,
69181
69182 },
69183 sum: {
69184 valType: 'number',
69185
69186 dflt: 1,
69187 min: 0,
69188
69189 },
69190 aaxis: ternaryAxesAttrs,
69191 baxis: ternaryAxesAttrs,
69192 caxis: ternaryAxesAttrs
69193}, 'plot', 'from-root');
69194
69195// set uirevisions outside of `overrideAll` so we can get `editType: none`
69196attrs.uirevision = {
69197 valType: 'any',
69198
69199 editType: 'none',
69200
69201};
69202
69203attrs.aaxis.uirevision = attrs.baxis.uirevision = attrs.caxis.uirevision = {
69204 valType: 'any',
69205
69206 editType: 'none',
69207
69208};
69209
69210},{"../../components/color/attributes":51,"../../lib/extend":173,"../../plot_api/edit_types":205,"../cartesian/layout_attributes":236,"../domain":249}],267:[function(_dereq_,module,exports){
69211/**
69212* Copyright 2012-2020, Plotly, Inc.
69213* All rights reserved.
69214*
69215* This source code is licensed under the MIT license found in the
69216* LICENSE file in the root directory of this source tree.
69217*/
69218
69219'use strict';
69220
69221var Color = _dereq_('../../components/color');
69222var Template = _dereq_('../../plot_api/plot_template');
69223var Lib = _dereq_('../../lib');
69224
69225var handleSubplotDefaults = _dereq_('../subplot_defaults');
69226var handleTickLabelDefaults = _dereq_('../cartesian/tick_label_defaults');
69227var handleTickMarkDefaults = _dereq_('../cartesian/tick_mark_defaults');
69228var handleTickValueDefaults = _dereq_('../cartesian/tick_value_defaults');
69229var handleLineGridDefaults = _dereq_('../cartesian/line_grid_defaults');
69230var layoutAttributes = _dereq_('./layout_attributes');
69231
69232var axesNames = ['aaxis', 'baxis', 'caxis'];
69233
69234module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
69235 handleSubplotDefaults(layoutIn, layoutOut, fullData, {
69236 type: 'ternary',
69237 attributes: layoutAttributes,
69238 handleDefaults: handleTernaryDefaults,
69239 font: layoutOut.font,
69240 paper_bgcolor: layoutOut.paper_bgcolor
69241 });
69242};
69243
69244function handleTernaryDefaults(ternaryLayoutIn, ternaryLayoutOut, coerce, options) {
69245 var bgColor = coerce('bgcolor');
69246 var sum = coerce('sum');
69247 options.bgColor = Color.combine(bgColor, options.paper_bgcolor);
69248 var axName, containerIn, containerOut;
69249
69250 // TODO: allow most (if not all) axis attributes to be set
69251 // in the outer container and used as defaults in the individual axes?
69252
69253 for(var j = 0; j < axesNames.length; j++) {
69254 axName = axesNames[j];
69255 containerIn = ternaryLayoutIn[axName] || {};
69256 containerOut = Template.newContainer(ternaryLayoutOut, axName);
69257 containerOut._name = axName;
69258
69259 handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut);
69260 }
69261
69262 // if the min values contradict each other, set them all to default (0)
69263 // and delete *all* the inputs so the user doesn't get confused later by
69264 // changing one and having them all change.
69265 var aaxis = ternaryLayoutOut.aaxis;
69266 var baxis = ternaryLayoutOut.baxis;
69267 var caxis = ternaryLayoutOut.caxis;
69268 if(aaxis.min + baxis.min + caxis.min >= sum) {
69269 aaxis.min = 0;
69270 baxis.min = 0;
69271 caxis.min = 0;
69272 if(ternaryLayoutIn.aaxis) delete ternaryLayoutIn.aaxis.min;
69273 if(ternaryLayoutIn.baxis) delete ternaryLayoutIn.baxis.min;
69274 if(ternaryLayoutIn.caxis) delete ternaryLayoutIn.caxis.min;
69275 }
69276}
69277
69278function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut) {
69279 var axAttrs = layoutAttributes[containerOut._name];
69280
69281 function coerce(attr, dflt) {
69282 return Lib.coerce(containerIn, containerOut, axAttrs, attr, dflt);
69283 }
69284
69285 coerce('uirevision', ternaryLayoutOut.uirevision);
69286
69287 containerOut.type = 'linear'; // no other types allowed for ternary
69288
69289 var dfltColor = coerce('color');
69290 // if axis.color was provided, use it for fonts too; otherwise,
69291 // inherit from global font color in case that was provided.
69292 var dfltFontColor = (dfltColor !== axAttrs.color.dflt) ? dfltColor : options.font.color;
69293
69294 var axName = containerOut._name;
69295 var letterUpper = axName.charAt(0).toUpperCase();
69296 var dfltTitle = 'Component ' + letterUpper;
69297
69298 var title = coerce('title.text', dfltTitle);
69299 containerOut._hovertitle = title === dfltTitle ? title : letterUpper;
69300
69301 Lib.coerceFont(coerce, 'title.font', {
69302 family: options.font.family,
69303 size: Math.round(options.font.size * 1.2),
69304 color: dfltFontColor
69305 });
69306
69307 // range is just set by 'min' - max is determined by the other axes mins
69308 coerce('min');
69309
69310 handleTickValueDefaults(containerIn, containerOut, coerce, 'linear');
69311 handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear', {});
69312 handleTickMarkDefaults(containerIn, containerOut, coerce,
69313 { outerTicks: true });
69314
69315 var showTickLabels = coerce('showticklabels');
69316 if(showTickLabels) {
69317 Lib.coerceFont(coerce, 'tickfont', {
69318 family: options.font.family,
69319 size: options.font.size,
69320 color: dfltFontColor
69321 });
69322 coerce('tickangle');
69323 coerce('tickformat');
69324 }
69325
69326 handleLineGridDefaults(containerIn, containerOut, coerce, {
69327 dfltColor: dfltColor,
69328 bgColor: options.bgColor,
69329 // default grid color is darker here (60%, vs cartesian default ~91%)
69330 // because the grid is not square so the eye needs heavier cues to follow
69331 blend: 60,
69332 showLine: true,
69333 showGrid: true,
69334 noZeroLine: true,
69335 attributes: axAttrs
69336 });
69337
69338 coerce('hoverformat');
69339 coerce('layer');
69340}
69341
69342},{"../../components/color":52,"../../lib":178,"../../plot_api/plot_template":212,"../cartesian/line_grid_defaults":238,"../cartesian/tick_label_defaults":243,"../cartesian/tick_mark_defaults":244,"../cartesian/tick_value_defaults":245,"../subplot_defaults":263,"./layout_attributes":266}],268:[function(_dereq_,module,exports){
69343/**
69344* Copyright 2012-2020, Plotly, Inc.
69345* All rights reserved.
69346*
69347* This source code is licensed under the MIT license found in the
69348* LICENSE file in the root directory of this source tree.
69349*/
69350
69351
69352'use strict';
69353
69354var d3 = _dereq_('d3');
69355var tinycolor = _dereq_('tinycolor2');
69356
69357var Registry = _dereq_('../../registry');
69358var Lib = _dereq_('../../lib');
69359var _ = Lib._;
69360var Color = _dereq_('../../components/color');
69361var Drawing = _dereq_('../../components/drawing');
69362var setConvert = _dereq_('../cartesian/set_convert');
69363var extendFlat = _dereq_('../../lib/extend').extendFlat;
69364var Plots = _dereq_('../plots');
69365var Axes = _dereq_('../cartesian/axes');
69366var dragElement = _dereq_('../../components/dragelement');
69367var Fx = _dereq_('../../components/fx');
69368var dragHelpers = _dereq_('../../components/dragelement/helpers');
69369var freeMode = dragHelpers.freeMode;
69370var rectMode = dragHelpers.rectMode;
69371var Titles = _dereq_('../../components/titles');
69372var prepSelect = _dereq_('../cartesian/select').prepSelect;
69373var selectOnClick = _dereq_('../cartesian/select').selectOnClick;
69374var clearSelect = _dereq_('../cartesian/select').clearSelect;
69375var clearSelectionsCache = _dereq_('../cartesian/select').clearSelectionsCache;
69376var constants = _dereq_('../cartesian/constants');
69377
69378function Ternary(options, fullLayout) {
69379 this.id = options.id;
69380 this.graphDiv = options.graphDiv;
69381 this.init(fullLayout);
69382 this.makeFramework(fullLayout);
69383
69384 // unfortunately, we have to keep track of some axis tick settings
69385 // as ternary subplots do not implement the 'ticks' editType
69386 this.aTickLayout = null;
69387 this.bTickLayout = null;
69388 this.cTickLayout = null;
69389}
69390
69391module.exports = Ternary;
69392
69393var proto = Ternary.prototype;
69394
69395proto.init = function(fullLayout) {
69396 this.container = fullLayout._ternarylayer;
69397 this.defs = fullLayout._defs;
69398 this.layoutId = fullLayout._uid;
69399 this.traceHash = {};
69400 this.layers = {};
69401};
69402
69403proto.plot = function(ternaryCalcData, fullLayout) {
69404 var _this = this;
69405 var ternaryLayout = fullLayout[_this.id];
69406 var graphSize = fullLayout._size;
69407
69408 _this._hasClipOnAxisFalse = false;
69409 for(var i = 0; i < ternaryCalcData.length; i++) {
69410 var trace = ternaryCalcData[i][0].trace;
69411
69412 if(trace.cliponaxis === false) {
69413 _this._hasClipOnAxisFalse = true;
69414 break;
69415 }
69416 }
69417
69418 _this.updateLayers(ternaryLayout);
69419 _this.adjustLayout(ternaryLayout, graphSize);
69420 Plots.generalUpdatePerTraceModule(_this.graphDiv, _this, ternaryCalcData, ternaryLayout);
69421 _this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
69422};
69423
69424proto.makeFramework = function(fullLayout) {
69425 var _this = this;
69426 var gd = _this.graphDiv;
69427 var ternaryLayout = fullLayout[_this.id];
69428
69429 var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
69430 var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id;
69431
69432 // clippath for this ternary subplot
69433 _this.clipDef = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
69434 s.append('path').attr('d', 'M0,0Z');
69435 });
69436
69437 // 'relative' clippath (i.e. no translation) for this ternary subplot
69438 _this.clipDefRelative = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipIdRelative, function(s) {
69439 s.append('path').attr('d', 'M0,0Z');
69440 });
69441
69442 // container for everything in this ternary subplot
69443 _this.plotContainer = Lib.ensureSingle(_this.container, 'g', _this.id);
69444 _this.updateLayers(ternaryLayout);
69445
69446 Drawing.setClipUrl(_this.layers.backplot, clipId, gd);
69447 Drawing.setClipUrl(_this.layers.grids, clipId, gd);
69448};
69449
69450proto.updateLayers = function(ternaryLayout) {
69451 var _this = this;
69452 var layers = _this.layers;
69453
69454 // inside that container, we have one container for the data, and
69455 // one each for the three axes around it.
69456
69457 var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids'];
69458
69459 if(ternaryLayout.aaxis.layer === 'below traces') {
69460 plotLayers.push('aaxis', 'aline');
69461 }
69462 if(ternaryLayout.baxis.layer === 'below traces') {
69463 plotLayers.push('baxis', 'bline');
69464 }
69465 if(ternaryLayout.caxis.layer === 'below traces') {
69466 plotLayers.push('caxis', 'cline');
69467 }
69468
69469 plotLayers.push('frontplot');
69470
69471 if(ternaryLayout.aaxis.layer === 'above traces') {
69472 plotLayers.push('aaxis', 'aline');
69473 }
69474 if(ternaryLayout.baxis.layer === 'above traces') {
69475 plotLayers.push('baxis', 'bline');
69476 }
69477 if(ternaryLayout.caxis.layer === 'above traces') {
69478 plotLayers.push('caxis', 'cline');
69479 }
69480
69481 var toplevel = _this.plotContainer.selectAll('g.toplevel')
69482 .data(plotLayers, String);
69483
69484 var grids = ['agrid', 'bgrid', 'cgrid'];
69485
69486 toplevel.enter().append('g')
69487 .attr('class', function(d) { return 'toplevel ' + d; })
69488 .each(function(d) {
69489 var s = d3.select(this);
69490 layers[d] = s;
69491
69492 // containers for different trace types.
69493 // NOTE - this is different from cartesian, where all traces
69494 // are in front of grids. Here I'm putting maps behind the grids
69495 // so the grids will always be visible if they're requested.
69496 // Perhaps we want that for cartesian too?
69497 if(d === 'frontplot') {
69498 s.append('g').classed('scatterlayer', true);
69499 } else if(d === 'backplot') {
69500 s.append('g').classed('maplayer', true);
69501 } else if(d === 'plotbg') {
69502 s.append('path').attr('d', 'M0,0Z');
69503 } else if(d === 'aline' || d === 'bline' || d === 'cline') {
69504 s.append('path');
69505 } else if(d === 'grids') {
69506 grids.forEach(function(d) {
69507 layers[d] = s.append('g').classed('grid ' + d, true);
69508 });
69509 }
69510 });
69511
69512 toplevel.order();
69513};
69514
69515var whRatio = Math.sqrt(4 / 3);
69516
69517proto.adjustLayout = function(ternaryLayout, graphSize) {
69518 var _this = this;
69519 var domain = ternaryLayout.domain;
69520 var xDomainCenter = (domain.x[0] + domain.x[1]) / 2;
69521 var yDomainCenter = (domain.y[0] + domain.y[1]) / 2;
69522 var xDomain = domain.x[1] - domain.x[0];
69523 var yDomain = domain.y[1] - domain.y[0];
69524 var wmax = xDomain * graphSize.w;
69525 var hmax = yDomain * graphSize.h;
69526 var sum = ternaryLayout.sum;
69527 var amin = ternaryLayout.aaxis.min;
69528 var bmin = ternaryLayout.baxis.min;
69529 var cmin = ternaryLayout.caxis.min;
69530
69531 var x0, y0, w, h, xDomainFinal, yDomainFinal;
69532
69533 if(wmax > whRatio * hmax) {
69534 h = hmax;
69535 w = h * whRatio;
69536 } else {
69537 w = wmax;
69538 h = w / whRatio;
69539 }
69540
69541 xDomainFinal = xDomain * w / wmax;
69542 yDomainFinal = yDomain * h / hmax;
69543
69544 x0 = graphSize.l + graphSize.w * xDomainCenter - w / 2;
69545 y0 = graphSize.t + graphSize.h * (1 - yDomainCenter) - h / 2;
69546
69547 _this.x0 = x0;
69548 _this.y0 = y0;
69549 _this.w = w;
69550 _this.h = h;
69551 _this.sum = sum;
69552
69553 // set up the x and y axis objects we'll use to lay out the points
69554 _this.xaxis = {
69555 type: 'linear',
69556 range: [amin + 2 * cmin - sum, sum - amin - 2 * bmin],
69557 domain: [
69558 xDomainCenter - xDomainFinal / 2,
69559 xDomainCenter + xDomainFinal / 2
69560 ],
69561 _id: 'x'
69562 };
69563 setConvert(_this.xaxis, _this.graphDiv._fullLayout);
69564 _this.xaxis.setScale();
69565 _this.xaxis.isPtWithinRange = function(d) {
69566 return (
69567 d.a >= _this.aaxis.range[0] &&
69568 d.a <= _this.aaxis.range[1] &&
69569 d.b >= _this.baxis.range[1] &&
69570 d.b <= _this.baxis.range[0] &&
69571 d.c >= _this.caxis.range[1] &&
69572 d.c <= _this.caxis.range[0]
69573 );
69574 };
69575
69576 _this.yaxis = {
69577 type: 'linear',
69578 range: [amin, sum - bmin - cmin],
69579 domain: [
69580 yDomainCenter - yDomainFinal / 2,
69581 yDomainCenter + yDomainFinal / 2
69582 ],
69583 _id: 'y'
69584 };
69585 setConvert(_this.yaxis, _this.graphDiv._fullLayout);
69586 _this.yaxis.setScale();
69587 _this.yaxis.isPtWithinRange = function() { return true; };
69588
69589 // set up the modified axes for tick drawing
69590 var yDomain0 = _this.yaxis.domain[0];
69591
69592 // aaxis goes up the left side. Set it up as a y axis, but with
69593 // fictitious angles and domain, but then rotate and translate
69594 // it into place at the end
69595 var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, {
69596 range: [amin, sum - bmin - cmin],
69597 side: 'left',
69598 // tickangle = 'auto' means 0 anyway for a y axis, need to coerce to 0 here
69599 // so we can shift by 30.
69600 tickangle: (+ternaryLayout.aaxis.tickangle || 0) - 30,
69601 domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
69602 anchor: 'free',
69603 position: 0,
69604 _id: 'y',
69605 _length: w
69606 });
69607 setConvert(aaxis, _this.graphDiv._fullLayout);
69608 aaxis.setScale();
69609
69610 // baxis goes across the bottom (backward). We can set it up as an x axis
69611 // without any enclosing transformation.
69612 var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, {
69613 range: [sum - amin - cmin, bmin],
69614 side: 'bottom',
69615 domain: _this.xaxis.domain,
69616 anchor: 'free',
69617 position: 0,
69618 _id: 'x',
69619 _length: w
69620 });
69621 setConvert(baxis, _this.graphDiv._fullLayout);
69622 baxis.setScale();
69623
69624 // caxis goes down the right side. Set it up as a y axis, with
69625 // post-transformation similar to aaxis
69626 var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, {
69627 range: [sum - amin - bmin, cmin],
69628 side: 'right',
69629 tickangle: (+ternaryLayout.caxis.tickangle || 0) + 30,
69630 domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
69631 anchor: 'free',
69632 position: 0,
69633 _id: 'y',
69634 _length: w
69635 });
69636 setConvert(caxis, _this.graphDiv._fullLayout);
69637 caxis.setScale();
69638
69639 var triangleClip = 'M' + x0 + ',' + (y0 + h) + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
69640 _this.clipDef.select('path').attr('d', triangleClip);
69641 _this.layers.plotbg.select('path').attr('d', triangleClip);
69642
69643 var triangleClipRelative = 'M0,' + h + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
69644 _this.clipDefRelative.select('path').attr('d', triangleClipRelative);
69645
69646 var plotTransform = 'translate(' + x0 + ',' + y0 + ')';
69647 _this.plotContainer.selectAll('.scatterlayer,.maplayer')
69648 .attr('transform', plotTransform);
69649
69650 _this.clipDefRelative.select('path').attr('transform', null);
69651
69652 // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle
69653
69654 // TODO: there's probably an easier way to handle these translations/offsets now...
69655 var bTransform = 'translate(' + (x0 - baxis._offset) + ',' + (y0 + h) + ')';
69656
69657 _this.layers.baxis.attr('transform', bTransform);
69658 _this.layers.bgrid.attr('transform', bTransform);
69659
69660 var aTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
69661 ')rotate(30)translate(0,' + -aaxis._offset + ')';
69662 _this.layers.aaxis.attr('transform', aTransform);
69663 _this.layers.agrid.attr('transform', aTransform);
69664
69665 var cTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
69666 ')rotate(-30)translate(0,' + -caxis._offset + ')';
69667 _this.layers.caxis.attr('transform', cTransform);
69668 _this.layers.cgrid.attr('transform', cTransform);
69669
69670 _this.drawAxes(true);
69671
69672 _this.layers.aline.select('path')
69673 .attr('d', aaxis.showline ?
69674 'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0')
69675 .call(Color.stroke, aaxis.linecolor || '#000')
69676 .style('stroke-width', (aaxis.linewidth || 0) + 'px');
69677 _this.layers.bline.select('path')
69678 .attr('d', baxis.showline ?
69679 'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0')
69680 .call(Color.stroke, baxis.linecolor || '#000')
69681 .style('stroke-width', (baxis.linewidth || 0) + 'px');
69682 _this.layers.cline.select('path')
69683 .attr('d', caxis.showline ?
69684 'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
69685 .call(Color.stroke, caxis.linecolor || '#000')
69686 .style('stroke-width', (caxis.linewidth || 0) + 'px');
69687
69688 if(!_this.graphDiv._context.staticPlot) {
69689 _this.initInteractions();
69690 }
69691
69692 Drawing.setClipUrl(
69693 _this.layers.frontplot,
69694 _this._hasClipOnAxisFalse ? null : _this.clipId,
69695 _this.graphDiv
69696 );
69697};
69698
69699proto.drawAxes = function(doTitles) {
69700 var _this = this;
69701 var gd = _this.graphDiv;
69702 var titlesuffix = _this.id.substr(7) + 'title';
69703 var layers = _this.layers;
69704 var aaxis = _this.aaxis;
69705 var baxis = _this.baxis;
69706 var caxis = _this.caxis;
69707
69708 _this.drawAx(aaxis);
69709 _this.drawAx(baxis);
69710 _this.drawAx(caxis);
69711
69712 if(doTitles) {
69713 var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0,
69714 (caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) +
69715 (caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0));
69716 var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) +
69717 (baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3;
69718
69719 layers['a-title'] = Titles.draw(gd, 'a' + titlesuffix, {
69720 propContainer: aaxis,
69721 propName: _this.id + '.aaxis.title',
69722 placeholder: _(gd, 'Click to enter Component A title'),
69723 attributes: {
69724 x: _this.x0 + _this.w / 2,
69725 y: _this.y0 - aaxis.title.font.size / 3 - apad,
69726 'text-anchor': 'middle'
69727 }
69728 });
69729 layers['b-title'] = Titles.draw(gd, 'b' + titlesuffix, {
69730 propContainer: baxis,
69731 propName: _this.id + '.baxis.title',
69732 placeholder: _(gd, 'Click to enter Component B title'),
69733 attributes: {
69734 x: _this.x0 - bpad,
69735 y: _this.y0 + _this.h + baxis.title.font.size * 0.83 + bpad,
69736 'text-anchor': 'middle'
69737 }
69738 });
69739 layers['c-title'] = Titles.draw(gd, 'c' + titlesuffix, {
69740 propContainer: caxis,
69741 propName: _this.id + '.caxis.title',
69742 placeholder: _(gd, 'Click to enter Component C title'),
69743 attributes: {
69744 x: _this.x0 + _this.w + bpad,
69745 y: _this.y0 + _this.h + caxis.title.font.size * 0.83 + bpad,
69746 'text-anchor': 'middle'
69747 }
69748 });
69749 }
69750};
69751
69752proto.drawAx = function(ax) {
69753 var _this = this;
69754 var gd = _this.graphDiv;
69755 var axName = ax._name;
69756 var axLetter = axName.charAt(0);
69757 var axId = ax._id;
69758 var axLayer = _this.layers[axName];
69759 var counterAngle = 30;
69760
69761 var stashKey = axLetter + 'tickLayout';
69762 var newTickLayout = strTickLayout(ax);
69763 if(_this[stashKey] !== newTickLayout) {
69764 axLayer.selectAll('.' + axId + 'tick').remove();
69765 _this[stashKey] = newTickLayout;
69766 }
69767
69768 ax.setScale();
69769
69770 var vals = Axes.calcTicks(ax);
69771 var valsClipped = Axes.clipEnds(ax, vals);
69772 var transFn = Axes.makeTransFn(ax);
69773 var tickSign = Axes.getTickSigns(ax)[2];
69774
69775 var caRad = Lib.deg2rad(counterAngle);
69776 var pad = tickSign * (ax.linewidth || 1) / 2;
69777 var len = tickSign * ax.ticklen;
69778 var w = _this.w;
69779 var h = _this.h;
69780
69781 var tickPath = axLetter === 'b' ?
69782 'M0,' + pad + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len) :
69783 'M' + pad + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len);
69784
69785 var gridPath = {
69786 a: 'M0,0l' + h + ',-' + (w / 2),
69787 b: 'M0,0l-' + (w / 2) + ',-' + h,
69788 c: 'M0,0l-' + h + ',' + (w / 2)
69789 }[axLetter];
69790
69791 Axes.drawTicks(gd, ax, {
69792 vals: ax.ticks === 'inside' ? valsClipped : vals,
69793 layer: axLayer,
69794 path: tickPath,
69795 transFn: transFn,
69796 crisp: false
69797 });
69798
69799 Axes.drawGrid(gd, ax, {
69800 vals: valsClipped,
69801 layer: _this.layers[axLetter + 'grid'],
69802 path: gridPath,
69803 transFn: transFn,
69804 crisp: false
69805 });
69806
69807 Axes.drawLabels(gd, ax, {
69808 vals: vals,
69809 layer: axLayer,
69810 transFn: transFn,
69811 labelFns: Axes.makeLabelFns(ax, 0, counterAngle)
69812 });
69813};
69814
69815function strTickLayout(axLayout) {
69816 return axLayout.ticks + String(axLayout.ticklen) + String(axLayout.showticklabels);
69817}
69818
69819// hard coded paths for zoom corners
69820// uses the same sizing as cartesian, length is MINZOOM/2, width is 3px
69821var CLEN = constants.MINZOOM / 2 + 0.87;
69822var BLPATH = 'm-0.87,.5h' + CLEN + 'v3h-' + (CLEN + 5.2) +
69823 'l' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
69824 'l2.6,1.5l-' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
69825var BRPATH = 'm0.87,.5h-' + CLEN + 'v3h' + (CLEN + 5.2) +
69826 'l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
69827 'l-2.6,1.5l' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
69828var TOPPATH = 'm0,1l' + (CLEN / 2) + ',' + (CLEN * 0.87) +
69829 'l2.6,-1.5l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
69830 'l-' + (CLEN / 2 + 2.6) + ',' + (CLEN * 0.87 + 4.5) +
69831 'l2.6,1.5l' + (CLEN / 2) + ',-' + (CLEN * 0.87) + 'Z';
69832var STARTMARKER = 'm0.5,0.5h5v-2h-5v-5h-2v5h-5v2h5v5h2Z';
69833
69834// I guess this could be shared with cartesian... but for now it's separate.
69835var SHOWZOOMOUTTIP = true;
69836
69837proto.clearSelect = function() {
69838 clearSelectionsCache(this.dragOptions);
69839 clearSelect(this.dragOptions.gd);
69840};
69841
69842proto.initInteractions = function() {
69843 var _this = this;
69844 var dragger = _this.layers.plotbg.select('path').node();
69845 var gd = _this.graphDiv;
69846 var zoomLayer = gd._fullLayout._zoomlayer;
69847
69848 // use plotbg for the main interactions
69849 this.dragOptions = {
69850 element: dragger,
69851 gd: gd,
69852 plotinfo: {
69853 id: _this.id,
69854 domain: gd._fullLayout[_this.id].domain,
69855 xaxis: _this.xaxis,
69856 yaxis: _this.yaxis
69857 },
69858 subplot: _this.id,
69859 prepFn: function(e, startX, startY) {
69860 // these aren't available yet when initInteractions
69861 // is called
69862 _this.dragOptions.xaxes = [_this.xaxis];
69863 _this.dragOptions.yaxes = [_this.yaxis];
69864
69865 var dragModeNow = _this.dragOptions.dragmode = gd._fullLayout.dragmode;
69866
69867 if(freeMode(dragModeNow)) _this.dragOptions.minDrag = 1;
69868 else _this.dragOptions.minDrag = undefined;
69869
69870 if(dragModeNow === 'zoom') {
69871 _this.dragOptions.moveFn = zoomMove;
69872 _this.dragOptions.clickFn = clickZoomPan;
69873 _this.dragOptions.doneFn = zoomDone;
69874 zoomPrep(e, startX, startY);
69875 } else if(dragModeNow === 'pan') {
69876 _this.dragOptions.moveFn = plotDrag;
69877 _this.dragOptions.clickFn = clickZoomPan;
69878 _this.dragOptions.doneFn = dragDone;
69879 panPrep();
69880 _this.clearSelect(gd);
69881 } else if(rectMode(dragModeNow) || freeMode(dragModeNow)) {
69882 prepSelect(e, startX, startY, _this.dragOptions, dragModeNow);
69883 }
69884 }
69885 };
69886
69887 var x0, y0, mins0, span0, mins, lum, path0, dimmed, zb, corners;
69888
69889 function makeUpdate(_mins) {
69890 var attrs = {};
69891 attrs[_this.id + '.aaxis.min'] = _mins.a;
69892 attrs[_this.id + '.baxis.min'] = _mins.b;
69893 attrs[_this.id + '.caxis.min'] = _mins.c;
69894 return attrs;
69895 }
69896
69897 function clickZoomPan(numClicks, evt) {
69898 var clickMode = gd._fullLayout.clickmode;
69899
69900 removeZoombox(gd);
69901
69902 if(numClicks === 2) {
69903 gd.emit('plotly_doubleclick', null);
69904 Registry.call('_guiRelayout', gd, makeUpdate({a: 0, b: 0, c: 0}));
69905 }
69906
69907 if(clickMode.indexOf('select') > -1 && numClicks === 1) {
69908 selectOnClick(evt, gd, [_this.xaxis], [_this.yaxis], _this.id, _this.dragOptions);
69909 }
69910
69911 if(clickMode.indexOf('event') > -1) {
69912 Fx.click(gd, evt, _this.id);
69913 }
69914 }
69915
69916 function zoomPrep(e, startX, startY) {
69917 var dragBBox = dragger.getBoundingClientRect();
69918 x0 = startX - dragBBox.left;
69919 y0 = startY - dragBBox.top;
69920 mins0 = {
69921 a: _this.aaxis.range[0],
69922 b: _this.baxis.range[1],
69923 c: _this.caxis.range[1]
69924 };
69925 mins = mins0;
69926 span0 = _this.aaxis.range[1] - mins0.a;
69927 lum = tinycolor(_this.graphDiv._fullLayout[_this.id].bgcolor).getLuminance();
69928 path0 = 'M0,' + _this.h + 'L' + (_this.w / 2) + ', 0L' + _this.w + ',' + _this.h + 'Z';
69929 dimmed = false;
69930
69931 zb = zoomLayer.append('path')
69932 .attr('class', 'zoombox')
69933 .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
69934 .style({
69935 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
69936 'stroke-width': 0
69937 })
69938 .attr('d', path0);
69939
69940 corners = zoomLayer.append('path')
69941 .attr('class', 'zoombox-corners')
69942 .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
69943 .style({
69944 fill: Color.background,
69945 stroke: Color.defaultLine,
69946 'stroke-width': 1,
69947 opacity: 0
69948 })
69949 .attr('d', 'M0,0Z');
69950
69951 _this.clearSelect(gd);
69952 }
69953
69954 function getAFrac(x, y) { return 1 - (y / _this.h); }
69955 function getBFrac(x, y) { return 1 - ((x + (_this.h - y) / Math.sqrt(3)) / _this.w); }
69956 function getCFrac(x, y) { return ((x - (_this.h - y) / Math.sqrt(3)) / _this.w); }
69957
69958 function zoomMove(dx0, dy0) {
69959 var x1 = x0 + dx0;
69960 var y1 = y0 + dy0;
69961 var afrac = Math.max(0, Math.min(1, getAFrac(x0, y0), getAFrac(x1, y1)));
69962 var bfrac = Math.max(0, Math.min(1, getBFrac(x0, y0), getBFrac(x1, y1)));
69963 var cfrac = Math.max(0, Math.min(1, getCFrac(x0, y0), getCFrac(x1, y1)));
69964 var xLeft = ((afrac / 2) + cfrac) * _this.w;
69965 var xRight = (1 - (afrac / 2) - bfrac) * _this.w;
69966 var xCenter = (xLeft + xRight) / 2;
69967 var xSpan = xRight - xLeft;
69968 var yBottom = (1 - afrac) * _this.h;
69969 var yTop = yBottom - xSpan / whRatio;
69970
69971 if(xSpan < constants.MINZOOM) {
69972 mins = mins0;
69973 zb.attr('d', path0);
69974 corners.attr('d', 'M0,0Z');
69975 } else {
69976 mins = {
69977 a: mins0.a + afrac * span0,
69978 b: mins0.b + bfrac * span0,
69979 c: mins0.c + cfrac * span0
69980 };
69981 zb.attr('d', path0 + 'M' + xLeft + ',' + yBottom +
69982 'H' + xRight + 'L' + xCenter + ',' + yTop +
69983 'L' + xLeft + ',' + yBottom + 'Z');
69984 corners.attr('d', 'M' + x0 + ',' + y0 + STARTMARKER +
69985 'M' + xLeft + ',' + yBottom + BLPATH +
69986 'M' + xRight + ',' + yBottom + BRPATH +
69987 'M' + xCenter + ',' + yTop + TOPPATH);
69988 }
69989
69990 if(!dimmed) {
69991 zb.transition()
69992 .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
69993 'rgba(255,255,255,0.3)')
69994 .duration(200);
69995 corners.transition()
69996 .style('opacity', 1)
69997 .duration(200);
69998 dimmed = true;
69999 }
70000
70001 gd.emit('plotly_relayouting', makeUpdate(mins));
70002 }
70003
70004 function zoomDone() {
70005 removeZoombox(gd);
70006
70007 if(mins === mins0) return;
70008
70009 Registry.call('_guiRelayout', gd, makeUpdate(mins));
70010
70011 if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
70012 Lib.notifier(_(gd, 'Double-click to zoom back out'), 'long');
70013 SHOWZOOMOUTTIP = false;
70014 }
70015 }
70016
70017 function panPrep() {
70018 mins0 = {
70019 a: _this.aaxis.range[0],
70020 b: _this.baxis.range[1],
70021 c: _this.caxis.range[1]
70022 };
70023 mins = mins0;
70024 }
70025
70026 function plotDrag(dx, dy) {
70027 var dxScaled = dx / _this.xaxis._m;
70028 var dyScaled = dy / _this.yaxis._m;
70029 mins = {
70030 a: mins0.a - dyScaled,
70031 b: mins0.b + (dxScaled + dyScaled) / 2,
70032 c: mins0.c - (dxScaled - dyScaled) / 2
70033 };
70034 var minsorted = [mins.a, mins.b, mins.c].sort();
70035 var minindices = {
70036 a: minsorted.indexOf(mins.a),
70037 b: minsorted.indexOf(mins.b),
70038 c: minsorted.indexOf(mins.c)
70039 };
70040 if(minsorted[0] < 0) {
70041 if(minsorted[1] + minsorted[0] / 2 < 0) {
70042 minsorted[2] += minsorted[0] + minsorted[1];
70043 minsorted[0] = minsorted[1] = 0;
70044 } else {
70045 minsorted[2] += minsorted[0] / 2;
70046 minsorted[1] += minsorted[0] / 2;
70047 minsorted[0] = 0;
70048 }
70049 mins = {
70050 a: minsorted[minindices.a],
70051 b: minsorted[minindices.b],
70052 c: minsorted[minindices.c]
70053 };
70054 dy = (mins0.a - mins.a) * _this.yaxis._m;
70055 dx = (mins0.c - mins.c - mins0.b + mins.b) * _this.xaxis._m;
70056 }
70057
70058 // move the data (translate, don't redraw)
70059 var plotTransform = 'translate(' + (_this.x0 + dx) + ',' + (_this.y0 + dy) + ')';
70060 _this.plotContainer.selectAll('.scatterlayer,.maplayer')
70061 .attr('transform', plotTransform);
70062
70063 var plotTransform2 = 'translate(' + -dx + ',' + -dy + ')';
70064 _this.clipDefRelative.select('path').attr('transform', plotTransform2);
70065
70066 // move the ticks
70067 _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c];
70068 _this.baxis.range = [_this.sum - mins.a - mins.c, mins.b];
70069 _this.caxis.range = [_this.sum - mins.a - mins.b, mins.c];
70070
70071 _this.drawAxes(false);
70072
70073 if(_this._hasClipOnAxisFalse) {
70074 _this.plotContainer
70075 .select('.scatterlayer').selectAll('.trace')
70076 .call(Drawing.hideOutsideRangePoints, _this);
70077 }
70078
70079 gd.emit('plotly_relayouting', makeUpdate(mins));
70080 }
70081
70082 function dragDone() {
70083 Registry.call('_guiRelayout', gd, makeUpdate(mins));
70084 }
70085
70086 // finally, set up hover and click
70087 // these event handlers must already be set before dragElement.init
70088 // so it can stash them and override them.
70089 dragger.onmousemove = function(evt) {
70090 Fx.hover(gd, evt, _this.id);
70091 gd._fullLayout._lasthover = dragger;
70092 gd._fullLayout._hoversubplot = _this.id;
70093 };
70094
70095 dragger.onmouseout = function(evt) {
70096 if(gd._dragging) return;
70097
70098 dragElement.unhover(gd, evt);
70099 };
70100
70101 dragElement.init(this.dragOptions);
70102};
70103
70104function removeZoombox(gd) {
70105 d3.select(gd)
70106 .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
70107 .remove();
70108}
70109
70110},{"../../components/color":52,"../../components/dragelement":71,"../../components/dragelement/helpers":70,"../../components/drawing":74,"../../components/fx":92,"../../components/titles":147,"../../lib":178,"../../lib/extend":173,"../../registry":269,"../cartesian/axes":222,"../cartesian/constants":228,"../cartesian/select":241,"../cartesian/set_convert":242,"../plots":256,"d3":16,"tinycolor2":35}],269:[function(_dereq_,module,exports){
70111/**
70112* Copyright 2012-2020, Plotly, Inc.
70113* All rights reserved.
70114*
70115* This source code is licensed under the MIT license found in the
70116* LICENSE file in the root directory of this source tree.
70117*/
70118
70119'use strict';
70120
70121var Loggers = _dereq_('./lib/loggers');
70122var noop = _dereq_('./lib/noop');
70123var pushUnique = _dereq_('./lib/push_unique');
70124var isPlainObject = _dereq_('./lib/is_plain_object');
70125var addStyleRule = _dereq_('./lib/dom').addStyleRule;
70126var ExtendModule = _dereq_('./lib/extend');
70127
70128var basePlotAttributes = _dereq_('./plots/attributes');
70129var baseLayoutAttributes = _dereq_('./plots/layout_attributes');
70130
70131var extendFlat = ExtendModule.extendFlat;
70132var extendDeepAll = ExtendModule.extendDeepAll;
70133
70134exports.modules = {};
70135exports.allCategories = {};
70136exports.allTypes = [];
70137exports.subplotsRegistry = {};
70138exports.transformsRegistry = {};
70139exports.componentsRegistry = {};
70140exports.layoutArrayContainers = [];
70141exports.layoutArrayRegexes = [];
70142exports.traceLayoutAttributes = {};
70143exports.localeRegistry = {};
70144exports.apiMethodRegistry = {};
70145exports.collectableSubplotTypes = null;
70146
70147/**
70148 * Top-level register routine, exported as Plotly.register
70149 *
70150 * @param {object array or array of objects} _modules :
70151 * module object or list of module object to register.
70152 *
70153 * A valid `moduleType: 'trace'` module has fields:
70154 * - name {string} : the trace type
70155 * - categories {array} : categories associated with this trace type,
70156 * tested with Register.traceIs()
70157 * - meta {object} : meta info (mostly for plot-schema)
70158 *
70159 * A valid `moduleType: 'locale'` module has fields:
70160 * - name {string} : the locale name. Should be a 2-digit language string ('en', 'de')
70161 * optionally with a country/region code ('en-GB', 'de-CH'). If a country
70162 * code is used but the base language locale has not yet been supplied,
70163 * we will use this locale for the base as well.
70164 * - dictionary {object} : the dictionary mapping input strings to localized strings
70165 * generally the keys should be the literal input strings, but
70166 * if default translations are provided you can use any string as a key.
70167 * - format {object} : a `d3.locale` format specifier for this locale
70168 * any omitted keys we'll fall back on en-US.
70169 *
70170 * A valid `moduleType: 'transform'` module has fields:
70171 * - name {string} : transform name
70172 * - transform {function} : default-level transform function
70173 * - calcTransform {function} : calc-level transform function
70174 * - attributes {object} : transform attributes declarations
70175 * - supplyDefaults {function} : attributes default-supply function
70176 *
70177 * A valid `moduleType: 'component'` module has fields:
70178 * - name {string} : the component name, used it with Register.getComponentMethod()
70179 * to employ component method.
70180 *
70181 * A valid `moduleType: 'apiMethod'` module has fields:
70182 * - name {string} : the api method name.
70183 * - fn {function} : the api method called with Register.call();
70184 *
70185 */
70186exports.register = function register(_modules) {
70187 exports.collectableSubplotTypes = null;
70188
70189 if(!_modules) {
70190 throw new Error('No argument passed to Plotly.register.');
70191 } else if(_modules && !Array.isArray(_modules)) {
70192 _modules = [_modules];
70193 }
70194
70195 for(var i = 0; i < _modules.length; i++) {
70196 var newModule = _modules[i];
70197
70198 if(!newModule) {
70199 throw new Error('Invalid module was attempted to be registered!');
70200 }
70201
70202 switch(newModule.moduleType) {
70203 case 'trace':
70204 registerTraceModule(newModule);
70205 break;
70206 case 'transform':
70207 registerTransformModule(newModule);
70208 break;
70209 case 'component':
70210 registerComponentModule(newModule);
70211 break;
70212 case 'locale':
70213 registerLocale(newModule);
70214 break;
70215 case 'apiMethod':
70216 var name = newModule.name;
70217 exports.apiMethodRegistry[name] = newModule.fn;
70218 break;
70219 default:
70220 throw new Error('Invalid module was attempted to be registered!');
70221 }
70222 }
70223};
70224
70225/**
70226 * Get registered module using trace object or trace type
70227 *
70228 * @param {object||string} trace
70229 * trace object with prop 'type' or trace type as a string
70230 * @return {object}
70231 * module object corresponding to trace type
70232 */
70233exports.getModule = function(trace) {
70234 var _module = exports.modules[getTraceType(trace)];
70235 if(!_module) return false;
70236 return _module._module;
70237};
70238
70239/**
70240 * Determine if this trace type is in a given category
70241 *
70242 * @param {object||string} traceType
70243 * a trace (object) or trace type (string)
70244 * @param {string} category
70245 * category in question
70246 * @return {boolean}
70247 */
70248exports.traceIs = function(traceType, category) {
70249 traceType = getTraceType(traceType);
70250
70251 // old Chart Studio Cloud workspace hack, nothing to see here
70252 if(traceType === 'various') return false;
70253
70254 var _module = exports.modules[traceType];
70255
70256 if(!_module) {
70257 if(traceType && traceType !== 'area') {
70258 Loggers.log('Unrecognized trace type ' + traceType + '.');
70259 }
70260
70261 _module = exports.modules[basePlotAttributes.type.dflt];
70262 }
70263
70264 return !!_module.categories[category];
70265};
70266
70267/**
70268 * Determine if this trace has a transform of the given type and return
70269 * array of matching indices.
70270 *
70271 * @param {object} data
70272 * a trace object (member of data or fullData)
70273 * @param {string} type
70274 * type of trace to test
70275 * @return {array}
70276 * array of matching indices. If none found, returns []
70277 */
70278exports.getTransformIndices = function(data, type) {
70279 var indices = [];
70280 var transforms = data.transforms || [];
70281 for(var i = 0; i < transforms.length; i++) {
70282 if(transforms[i].type === type) {
70283 indices.push(i);
70284 }
70285 }
70286 return indices;
70287};
70288
70289/**
70290 * Determine if this trace has a transform of the given type
70291 *
70292 * @param {object} data
70293 * a trace object (member of data or fullData)
70294 * @param {string} type
70295 * type of trace to test
70296 * @return {boolean}
70297 */
70298exports.hasTransform = function(data, type) {
70299 var transforms = data.transforms || [];
70300 for(var i = 0; i < transforms.length; i++) {
70301 if(transforms[i].type === type) {
70302 return true;
70303 }
70304 }
70305 return false;
70306};
70307
70308/**
70309 * Retrieve component module method. Falls back on noop if either the
70310 * module or the method is missing, so the result can always be safely called
70311 *
70312 * @param {string} name
70313 * name of component (as declared in component module)
70314 * @param {string} method
70315 * name of component module method
70316 * @return {function}
70317 */
70318exports.getComponentMethod = function(name, method) {
70319 var _module = exports.componentsRegistry[name];
70320
70321 if(!_module) return noop;
70322 return _module[method] || noop;
70323};
70324
70325/**
70326 * Call registered api method.
70327 *
70328 * @param {string} name : api method name
70329 * @param {...array} args : arguments passed to api method
70330 * @return {any} : returns api method output
70331 */
70332exports.call = function() {
70333 var name = arguments[0];
70334 var args = [].slice.call(arguments, 1);
70335 return exports.apiMethodRegistry[name].apply(null, args);
70336};
70337
70338function registerTraceModule(_module) {
70339 var thisType = _module.name;
70340 var categoriesIn = _module.categories;
70341 var meta = _module.meta;
70342
70343 if(exports.modules[thisType]) {
70344 Loggers.log('Type ' + thisType + ' already registered');
70345 return;
70346 }
70347
70348 if(!exports.subplotsRegistry[_module.basePlotModule.name]) {
70349 registerSubplot(_module.basePlotModule);
70350 }
70351
70352 var categoryObj = {};
70353 for(var i = 0; i < categoriesIn.length; i++) {
70354 categoryObj[categoriesIn[i]] = true;
70355 exports.allCategories[categoriesIn[i]] = true;
70356 }
70357
70358 exports.modules[thisType] = {
70359 _module: _module,
70360 categories: categoryObj
70361 };
70362
70363 if(meta && Object.keys(meta).length) {
70364 exports.modules[thisType].meta = meta;
70365 }
70366
70367 exports.allTypes.push(thisType);
70368
70369 for(var componentName in exports.componentsRegistry) {
70370 mergeComponentAttrsToTrace(componentName, thisType);
70371 }
70372
70373 /*
70374 * Collect all trace layout attributes in one place for easier lookup later
70375 * but don't merge them into the base schema as it would confuse the docs
70376 * (at least after https://github.com/plotly/documentation/issues/202 gets done!)
70377 */
70378 if(_module.layoutAttributes) {
70379 extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes);
70380 }
70381
70382 var basePlotModule = _module.basePlotModule;
70383 var bpmName = basePlotModule.name;
70384
70385 // add mapbox-gl CSS here to avoid console warning on instantiation
70386 if(bpmName === 'mapbox') {
70387 var styleRules = basePlotModule.constants.styleRules;
70388 for(var k in styleRules) {
70389 addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]);
70390 }
70391 }
70392
70393 // if `plotly-geo-assets.js` is not included,
70394 // add `PlotlyGeoAssets` global to stash references to all fetched
70395 // topojson / geojson data
70396 if((bpmName === 'geo' || bpmName === 'mapbox') &&
70397 (typeof window !== undefined && window.PlotlyGeoAssets === undefined)
70398 ) {
70399 window.PlotlyGeoAssets = {topojson: {}};
70400 }
70401}
70402
70403function registerSubplot(_module) {
70404 var plotType = _module.name;
70405
70406 if(exports.subplotsRegistry[plotType]) {
70407 Loggers.log('Plot type ' + plotType + ' already registered.');
70408 return;
70409 }
70410
70411 // relayout array handling will look for component module methods with this
70412 // name and won't find them because this is a subplot module... but that
70413 // should be fine, it will just fall back on redrawing the plot.
70414 findArrayRegexps(_module);
70415
70416 // not sure what's best for the 'cartesian' type at this point
70417 exports.subplotsRegistry[plotType] = _module;
70418
70419 for(var componentName in exports.componentsRegistry) {
70420 mergeComponentAttrsToSubplot(componentName, _module.name);
70421 }
70422}
70423
70424function registerComponentModule(_module) {
70425 if(typeof _module.name !== 'string') {
70426 throw new Error('Component module *name* must be a string.');
70427 }
70428
70429 var name = _module.name;
70430 exports.componentsRegistry[name] = _module;
70431
70432 if(_module.layoutAttributes) {
70433 if(_module.layoutAttributes._isLinkedToArray) {
70434 pushUnique(exports.layoutArrayContainers, name);
70435 }
70436 findArrayRegexps(_module);
70437 }
70438
70439 for(var traceType in exports.modules) {
70440 mergeComponentAttrsToTrace(name, traceType);
70441 }
70442
70443 for(var subplotName in exports.subplotsRegistry) {
70444 mergeComponentAttrsToSubplot(name, subplotName);
70445 }
70446
70447 for(var transformType in exports.transformsRegistry) {
70448 mergeComponentAttrsToTransform(name, transformType);
70449 }
70450
70451 if(_module.schema && _module.schema.layout) {
70452 extendDeepAll(baseLayoutAttributes, _module.schema.layout);
70453 }
70454}
70455
70456function registerTransformModule(_module) {
70457 if(typeof _module.name !== 'string') {
70458 throw new Error('Transform module *name* must be a string.');
70459 }
70460
70461 var prefix = 'Transform module ' + _module.name;
70462 var hasTransform = typeof _module.transform === 'function';
70463 var hasCalcTransform = typeof _module.calcTransform === 'function';
70464
70465 if(!hasTransform && !hasCalcTransform) {
70466 throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
70467 }
70468 if(hasTransform && hasCalcTransform) {
70469 Loggers.log([
70470 prefix + ' has both a *transform* and *calcTransform* methods.',
70471 'Please note that all *transform* methods are executed',
70472 'before all *calcTransform* methods.'
70473 ].join(' '));
70474 }
70475 if(!isPlainObject(_module.attributes)) {
70476 Loggers.log(prefix + ' registered without an *attributes* object.');
70477 }
70478 if(typeof _module.supplyDefaults !== 'function') {
70479 Loggers.log(prefix + ' registered without a *supplyDefaults* method.');
70480 }
70481
70482 exports.transformsRegistry[_module.name] = _module;
70483
70484 for(var componentName in exports.componentsRegistry) {
70485 mergeComponentAttrsToTransform(componentName, _module.name);
70486 }
70487}
70488
70489function registerLocale(_module) {
70490 var locale = _module.name;
70491 var baseLocale = locale.split('-')[0];
70492
70493 var newDict = _module.dictionary;
70494 var newFormat = _module.format;
70495 var hasDict = newDict && Object.keys(newDict).length;
70496 var hasFormat = newFormat && Object.keys(newFormat).length;
70497
70498 var locales = exports.localeRegistry;
70499
70500 var localeObj = locales[locale];
70501 if(!localeObj) locales[locale] = localeObj = {};
70502
70503 // Should we use this dict for the base locale?
70504 // In case we're overwriting a previous dict for this locale, check
70505 // whether the base matches the full locale dict now. If we're not
70506 // overwriting, locales[locale] is undefined so this just checks if
70507 // baseLocale already had a dict or not.
70508 // Same logic for dateFormats
70509 if(baseLocale !== locale) {
70510 var baseLocaleObj = locales[baseLocale];
70511 if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {};
70512
70513 if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) {
70514 baseLocaleObj.dictionary = newDict;
70515 }
70516 if(hasFormat && baseLocaleObj.format === localeObj.format) {
70517 baseLocaleObj.format = newFormat;
70518 }
70519 }
70520
70521 if(hasDict) localeObj.dictionary = newDict;
70522 if(hasFormat) localeObj.format = newFormat;
70523}
70524
70525function findArrayRegexps(_module) {
70526 if(_module.layoutAttributes) {
70527 var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps;
70528 if(arrayAttrRegexps) {
70529 for(var i = 0; i < arrayAttrRegexps.length; i++) {
70530 pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]);
70531 }
70532 }
70533 }
70534}
70535
70536function mergeComponentAttrsToTrace(componentName, traceType) {
70537 var componentSchema = exports.componentsRegistry[componentName].schema;
70538 if(!componentSchema || !componentSchema.traces) return;
70539
70540 var traceAttrs = componentSchema.traces[traceType];
70541 if(traceAttrs) {
70542 extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs);
70543 }
70544}
70545
70546function mergeComponentAttrsToTransform(componentName, transformType) {
70547 var componentSchema = exports.componentsRegistry[componentName].schema;
70548 if(!componentSchema || !componentSchema.transforms) return;
70549
70550 var transformAttrs = componentSchema.transforms[transformType];
70551 if(transformAttrs) {
70552 extendDeepAll(exports.transformsRegistry[transformType].attributes, transformAttrs);
70553 }
70554}
70555
70556function mergeComponentAttrsToSubplot(componentName, subplotName) {
70557 var componentSchema = exports.componentsRegistry[componentName].schema;
70558 if(!componentSchema || !componentSchema.subplots) return;
70559
70560 var subplotModule = exports.subplotsRegistry[subplotName];
70561 var subplotAttrs = subplotModule.layoutAttributes;
70562 var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr;
70563 if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0];
70564
70565 var componentLayoutAttrs = componentSchema.subplots[subplotAttr];
70566 if(subplotAttrs && componentLayoutAttrs) {
70567 extendDeepAll(subplotAttrs, componentLayoutAttrs);
70568 }
70569}
70570
70571function getTraceType(traceType) {
70572 if(typeof traceType === 'object') traceType = traceType.type;
70573 return traceType;
70574}
70575
70576},{"./lib/dom":171,"./lib/extend":173,"./lib/is_plain_object":179,"./lib/loggers":182,"./lib/noop":187,"./lib/push_unique":191,"./plots/attributes":219,"./plots/layout_attributes":254}],270:[function(_dereq_,module,exports){
70577/**
70578* Copyright 2012-2020, Plotly, Inc.
70579* All rights reserved.
70580*
70581* This source code is licensed under the MIT license found in the
70582* LICENSE file in the root directory of this source tree.
70583*/
70584
70585'use strict';
70586
70587var Registry = _dereq_('../registry');
70588var Lib = _dereq_('../lib');
70589
70590var extendFlat = Lib.extendFlat;
70591var extendDeep = Lib.extendDeep;
70592
70593// Put default plotTile layouts here
70594function cloneLayoutOverride(tileClass) {
70595 var override;
70596
70597 switch(tileClass) {
70598 case 'themes__thumb':
70599 override = {
70600 autosize: true,
70601 width: 150,
70602 height: 150,
70603 title: {text: ''},
70604 showlegend: false,
70605 margin: {l: 5, r: 5, t: 5, b: 5, pad: 0},
70606 annotations: []
70607 };
70608 break;
70609
70610 case 'thumbnail':
70611 override = {
70612 title: {text: ''},
70613 hidesources: true,
70614 showlegend: false,
70615 borderwidth: 0,
70616 bordercolor: '',
70617 margin: {l: 1, r: 1, t: 1, b: 1, pad: 0},
70618 annotations: []
70619 };
70620 break;
70621
70622 default:
70623 override = {};
70624 }
70625
70626
70627 return override;
70628}
70629
70630function keyIsAxis(keyName) {
70631 var types = ['xaxis', 'yaxis', 'zaxis'];
70632 return (types.indexOf(keyName.slice(0, 5)) > -1);
70633}
70634
70635
70636module.exports = function clonePlot(graphObj, options) {
70637 // Polar plot compatibility
70638 if(graphObj.framework && graphObj.framework.isPolar) {
70639 graphObj = graphObj.framework.getConfig();
70640 }
70641
70642 var i;
70643 var oldData = graphObj.data;
70644 var oldLayout = graphObj.layout;
70645 var newData = extendDeep([], oldData);
70646 var newLayout = extendDeep({}, oldLayout, cloneLayoutOverride(options.tileClass));
70647 var context = graphObj._context || {};
70648
70649 if(options.width) newLayout.width = options.width;
70650 if(options.height) newLayout.height = options.height;
70651
70652 if(options.tileClass === 'thumbnail' || options.tileClass === 'themes__thumb') {
70653 // kill annotations
70654 newLayout.annotations = [];
70655 var keys = Object.keys(newLayout);
70656
70657 for(i = 0; i < keys.length; i++) {
70658 if(keyIsAxis(keys[i])) {
70659 newLayout[keys[i]].title = {text: ''};
70660 }
70661 }
70662
70663 // kill colorbar and pie labels
70664 for(i = 0; i < newData.length; i++) {
70665 var trace = newData[i];
70666 trace.showscale = false;
70667 if(trace.marker) trace.marker.showscale = false;
70668 if(Registry.traceIs(trace, 'pie-like')) trace.textposition = 'none';
70669 }
70670 }
70671
70672 if(Array.isArray(options.annotations)) {
70673 for(i = 0; i < options.annotations.length; i++) {
70674 newLayout.annotations.push(options.annotations[i]);
70675 }
70676 }
70677
70678 // TODO: does this scene modification really belong here?
70679 // If we still need it, can it move into the gl3d module?
70680 var sceneIds = Object.keys(newLayout).filter(function(key) {
70681 return key.match(/^scene\d*$/);
70682 });
70683 if(sceneIds.length) {
70684 var axesImageOverride = {};
70685 if(options.tileClass === 'thumbnail') {
70686 axesImageOverride = {
70687 title: {text: ''},
70688 showaxeslabels: false,
70689 showticklabels: false,
70690 linetickenable: false
70691 };
70692 }
70693 for(i = 0; i < sceneIds.length; i++) {
70694 var scene = newLayout[sceneIds[i]];
70695
70696 if(!scene.xaxis) {
70697 scene.xaxis = {};
70698 }
70699
70700 if(!scene.yaxis) {
70701 scene.yaxis = {};
70702 }
70703
70704 if(!scene.zaxis) {
70705 scene.zaxis = {};
70706 }
70707
70708 extendFlat(scene.xaxis, axesImageOverride);
70709 extendFlat(scene.yaxis, axesImageOverride);
70710 extendFlat(scene.zaxis, axesImageOverride);
70711
70712 // TODO what does this do?
70713 scene._scene = null;
70714 }
70715 }
70716
70717 var gd = document.createElement('div');
70718 if(options.tileClass) gd.className = options.tileClass;
70719
70720 var plotTile = {
70721 gd: gd,
70722 td: gd, // for external (image server) compatibility
70723 layout: newLayout,
70724 data: newData,
70725 config: {
70726 staticPlot: (options.staticPlot === undefined) ?
70727 true :
70728 options.staticPlot,
70729 plotGlPixelRatio: (options.plotGlPixelRatio === undefined) ?
70730 2 :
70731 options.plotGlPixelRatio,
70732 displaylogo: options.displaylogo || false,
70733 showLink: options.showLink || false,
70734 showTips: options.showTips || false,
70735 mapboxAccessToken: context.mapboxAccessToken
70736 }
70737 };
70738
70739 if(options.setBackground !== 'transparent') {
70740 plotTile.config.setBackground = options.setBackground || 'opaque';
70741 }
70742
70743 // attaching the default Layout the gd, so you can grab it later
70744 plotTile.gd.defaultLayout = cloneLayoutOverride(options.tileClass);
70745
70746 return plotTile;
70747};
70748
70749},{"../lib":178,"../registry":269}],271:[function(_dereq_,module,exports){
70750/**
70751* Copyright 2012-2020, Plotly, Inc.
70752* All rights reserved.
70753*
70754* This source code is licensed under the MIT license found in the
70755* LICENSE file in the root directory of this source tree.
70756*/
70757
70758'use strict';
70759
70760var Lib = _dereq_('../lib');
70761
70762var toImage = _dereq_('../plot_api/to_image');
70763
70764var fileSaver = _dereq_('./filesaver');
70765var helpers = _dereq_('./helpers');
70766
70767/**
70768 * Plotly.downloadImage
70769 *
70770 * @param {object | string | HTML div} gd
70771 * can either be a data/layout/config object
70772 * or an existing graph <div>
70773 * or an id to an existing graph <div>
70774 * @param {object} opts (see Plotly.toImage in ../plot_api/to_image)
70775 * @return {promise}
70776 */
70777function downloadImage(gd, opts) {
70778 var _gd;
70779 if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd);
70780
70781 opts = opts || {};
70782 opts.format = opts.format || 'png';
70783 opts.imageDataOnly = true;
70784
70785 return new Promise(function(resolve, reject) {
70786 if(_gd && _gd._snapshotInProgress) {
70787 reject(new Error('Snapshotting already in progress.'));
70788 }
70789
70790 // see comments within svgtoimg for additional
70791 // discussion of problems with IE
70792 // can now draw to canvas, but CORS tainted canvas
70793 // does not allow toDataURL
70794 // svg format will work though
70795 if(Lib.isIE() && opts.format !== 'svg') {
70796 reject(new Error(helpers.MSG_IE_BAD_FORMAT));
70797 }
70798
70799 if(_gd) _gd._snapshotInProgress = true;
70800 var promise = toImage(gd, opts);
70801
70802 var filename = opts.filename || gd.fn || 'newplot';
70803 filename += '.' + opts.format.replace('-', '.');
70804
70805 promise.then(function(result) {
70806 if(_gd) _gd._snapshotInProgress = false;
70807 return fileSaver(result, filename, opts.format);
70808 }).then(function(name) {
70809 resolve(name);
70810 }).catch(function(err) {
70811 if(_gd) _gd._snapshotInProgress = false;
70812 reject(err);
70813 });
70814 });
70815}
70816
70817module.exports = downloadImage;
70818
70819},{"../lib":178,"../plot_api/to_image":215,"./filesaver":272,"./helpers":273}],272:[function(_dereq_,module,exports){
70820/**
70821* Copyright 2012-2020, Plotly, Inc.
70822* All rights reserved.
70823*
70824* This source code is licensed under the MIT license found in the
70825* LICENSE file in the root directory of this source tree.
70826*/
70827
70828'use strict';
70829
70830var Lib = _dereq_('../lib');
70831var helpers = _dereq_('./helpers');
70832
70833/*
70834* substantial portions of this code from FileSaver.js
70835* https://github.com/eligrey/FileSaver.js
70836* License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
70837* FileSaver.js
70838* A saveAs() FileSaver implementation.
70839* 1.1.20160328
70840*
70841* By Eli Grey, http://eligrey.com
70842* License: MIT
70843* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
70844*/
70845function fileSaver(url, name, format) {
70846 var saveLink = document.createElement('a');
70847 var canUseSaveLink = 'download' in saveLink;
70848
70849 var promise = new Promise(function(resolve, reject) {
70850 var blob;
70851 var objectUrl;
70852
70853 if(Lib.isIE9orBelow()) {
70854 reject(new Error('IE < 10 unsupported'));
70855 }
70856
70857 // Safari doesn't allow downloading of blob urls
70858 if(Lib.isSafari()) {
70859 var prefix = format === 'svg' ? ',' : ';base64,';
70860 helpers.octetStream(prefix + encodeURIComponent(url));
70861 return resolve(name);
70862 }
70863
70864 // IE 10+ (native saveAs)
70865 if(Lib.isIE()) {
70866 // At this point we are only dealing with a decoded SVG as
70867 // a data URL (since IE only supports SVG)
70868 blob = helpers.createBlob(url, 'svg');
70869 window.navigator.msSaveBlob(blob, name);
70870 blob = null;
70871 return resolve(name);
70872 }
70873
70874 if(canUseSaveLink) {
70875 blob = helpers.createBlob(url, format);
70876 objectUrl = helpers.createObjectURL(blob);
70877
70878 saveLink.href = objectUrl;
70879 saveLink.download = name;
70880 document.body.appendChild(saveLink);
70881 saveLink.click();
70882
70883 document.body.removeChild(saveLink);
70884 helpers.revokeObjectURL(objectUrl);
70885 blob = null;
70886
70887 return resolve(name);
70888 }
70889
70890 reject(new Error('download error'));
70891 });
70892
70893 return promise;
70894}
70895
70896
70897module.exports = fileSaver;
70898
70899},{"../lib":178,"./helpers":273}],273:[function(_dereq_,module,exports){
70900/**
70901* Copyright 2012-2020, Plotly, Inc.
70902* All rights reserved.
70903*
70904* This source code is licensed under the MIT license found in the
70905* LICENSE file in the root directory of this source tree.
70906*/
70907
70908'use strict';
70909
70910var Registry = _dereq_('../registry');
70911
70912exports.getDelay = function(fullLayout) {
70913 if(!fullLayout._has) return 0;
70914
70915 return (
70916 fullLayout._has('gl3d') ||
70917 fullLayout._has('gl2d') ||
70918 fullLayout._has('mapbox')
70919 ) ? 500 : 0;
70920};
70921
70922exports.getRedrawFunc = function(gd) {
70923 return function() {
70924 var fullLayout = gd._fullLayout || {};
70925 var hasPolar = fullLayout._has && fullLayout._has('polar');
70926 var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r;
70927
70928 if(!hasLegacyPolar) {
70929 Registry.getComponentMethod('colorbar', 'draw')(gd);
70930 }
70931 };
70932};
70933
70934exports.encodeSVG = function(svg) {
70935 return 'data:image/svg+xml,' + encodeURIComponent(svg);
70936};
70937
70938exports.encodeJSON = function(json) {
70939 return 'data:application/json,' + encodeURIComponent(json);
70940};
70941
70942var DOM_URL = window.URL || window.webkitURL;
70943
70944exports.createObjectURL = function(blob) {
70945 return DOM_URL.createObjectURL(blob);
70946};
70947
70948exports.revokeObjectURL = function(url) {
70949 return DOM_URL.revokeObjectURL(url);
70950};
70951
70952exports.createBlob = function(url, format) {
70953 if(format === 'svg') {
70954 return new window.Blob([url], {type: 'image/svg+xml;charset=utf-8'});
70955 } else if(format === 'full-json') {
70956 return new window.Blob([url], {type: 'application/json;charset=utf-8'});
70957 } else {
70958 var binary = fixBinary(window.atob(url));
70959 return new window.Blob([binary], {type: 'image/' + format});
70960 }
70961};
70962
70963exports.octetStream = function(s) {
70964 document.location.href = 'data:application/octet-stream' + s;
70965};
70966
70967// Taken from https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752
70968function fixBinary(b) {
70969 var len = b.length;
70970 var buf = new ArrayBuffer(len);
70971 var arr = new Uint8Array(buf);
70972 for(var i = 0; i < len; i++) {
70973 arr[i] = b.charCodeAt(i);
70974 }
70975 return buf;
70976}
70977
70978exports.IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;
70979
70980exports.MSG_IE_BAD_FORMAT = 'Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.';
70981
70982},{"../registry":269}],274:[function(_dereq_,module,exports){
70983/**
70984* Copyright 2012-2020, Plotly, Inc.
70985* All rights reserved.
70986*
70987* This source code is licensed under the MIT license found in the
70988* LICENSE file in the root directory of this source tree.
70989*/
70990
70991
70992'use strict';
70993
70994var helpers = _dereq_('./helpers');
70995
70996var Snapshot = {
70997 getDelay: helpers.getDelay,
70998 getRedrawFunc: helpers.getRedrawFunc,
70999 clone: _dereq_('./cloneplot'),
71000 toSVG: _dereq_('./tosvg'),
71001 svgToImg: _dereq_('./svgtoimg'),
71002 toImage: _dereq_('./toimage'),
71003 downloadImage: _dereq_('./download')
71004};
71005
71006module.exports = Snapshot;
71007
71008},{"./cloneplot":270,"./download":271,"./helpers":273,"./svgtoimg":275,"./toimage":276,"./tosvg":277}],275:[function(_dereq_,module,exports){
71009/**
71010* Copyright 2012-2020, Plotly, Inc.
71011* All rights reserved.
71012*
71013* This source code is licensed under the MIT license found in the
71014* LICENSE file in the root directory of this source tree.
71015*/
71016
71017'use strict';
71018
71019var Lib = _dereq_('../lib');
71020var EventEmitter = _dereq_('events').EventEmitter;
71021
71022var helpers = _dereq_('./helpers');
71023
71024function svgToImg(opts) {
71025 var ev = opts.emitter || new EventEmitter();
71026
71027 var promise = new Promise(function(resolve, reject) {
71028 var Image = window.Image;
71029 var svg = opts.svg;
71030 var format = opts.format || 'png';
71031
71032 // IE only support svg
71033 if(Lib.isIE() && format !== 'svg') {
71034 var ieSvgError = new Error(helpers.MSG_IE_BAD_FORMAT);
71035 reject(ieSvgError);
71036 // eventually remove the ev
71037 // in favor of promises
71038 if(!opts.promise) {
71039 return ev.emit('error', ieSvgError);
71040 } else {
71041 return promise;
71042 }
71043 }
71044
71045 var canvas = opts.canvas;
71046 var scale = opts.scale || 1;
71047 var w0 = opts.width || 300;
71048 var h0 = opts.height || 150;
71049 var w1 = scale * w0;
71050 var h1 = scale * h0;
71051
71052 var ctx = canvas.getContext('2d');
71053 var img = new Image();
71054 var svgBlob, url;
71055
71056 if(format === 'svg' || Lib.isIE9orBelow() || Lib.isSafari()) {
71057 url = helpers.encodeSVG(svg);
71058 } else {
71059 svgBlob = helpers.createBlob(svg, 'svg');
71060 url = helpers.createObjectURL(svgBlob);
71061 }
71062
71063 canvas.width = w1;
71064 canvas.height = h1;
71065
71066 img.onload = function() {
71067 var imgData;
71068
71069 svgBlob = null;
71070 helpers.revokeObjectURL(url);
71071
71072 // don't need to draw to canvas if svg
71073 // save some time and also avoid failure on IE
71074 if(format !== 'svg') {
71075 ctx.drawImage(img, 0, 0, w1, h1);
71076 }
71077
71078 switch(format) {
71079 case 'jpeg':
71080 imgData = canvas.toDataURL('image/jpeg');
71081 break;
71082 case 'png':
71083 imgData = canvas.toDataURL('image/png');
71084 break;
71085 case 'webp':
71086 imgData = canvas.toDataURL('image/webp');
71087 break;
71088 case 'svg':
71089 imgData = url;
71090 break;
71091 default:
71092 var errorMsg = 'Image format is not jpeg, png, svg or webp.';
71093 reject(new Error(errorMsg));
71094 // eventually remove the ev
71095 // in favor of promises
71096 if(!opts.promise) {
71097 return ev.emit('error', errorMsg);
71098 }
71099 }
71100 resolve(imgData);
71101 // eventually remove the ev
71102 // in favor of promises
71103 if(!opts.promise) {
71104 ev.emit('success', imgData);
71105 }
71106 };
71107
71108 img.onerror = function(err) {
71109 svgBlob = null;
71110 helpers.revokeObjectURL(url);
71111
71112 reject(err);
71113 // eventually remove the ev
71114 // in favor of promises
71115 if(!opts.promise) {
71116 return ev.emit('error', err);
71117 }
71118 };
71119
71120 img.src = url;
71121 });
71122
71123 // temporary for backward compatibility
71124 // move to only Promise in 2.0.0
71125 // and eliminate the EventEmitter
71126 if(opts.promise) {
71127 return promise;
71128 }
71129
71130 return ev;
71131}
71132
71133module.exports = svgToImg;
71134
71135},{"../lib":178,"./helpers":273,"events":15}],276:[function(_dereq_,module,exports){
71136/**
71137* Copyright 2012-2020, Plotly, Inc.
71138* All rights reserved.
71139*
71140* This source code is licensed under the MIT license found in the
71141* LICENSE file in the root directory of this source tree.
71142*/
71143
71144'use strict';
71145
71146var EventEmitter = _dereq_('events').EventEmitter;
71147
71148var Registry = _dereq_('../registry');
71149var Lib = _dereq_('../lib');
71150
71151var helpers = _dereq_('./helpers');
71152var clonePlot = _dereq_('./cloneplot');
71153var toSVG = _dereq_('./tosvg');
71154var svgToImg = _dereq_('./svgtoimg');
71155
71156/**
71157 * @param {object} gd figure Object
71158 * @param {object} opts option object
71159 * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
71160 */
71161function toImage(gd, opts) {
71162 // first clone the GD so we can operate in a clean environment
71163 var ev = new EventEmitter();
71164
71165 var clone = clonePlot(gd, {format: 'png'});
71166 var clonedGd = clone.gd;
71167
71168 // put the cloned div somewhere off screen before attaching to DOM
71169 clonedGd.style.position = 'absolute';
71170 clonedGd.style.left = '-5000px';
71171 document.body.appendChild(clonedGd);
71172
71173 function wait() {
71174 var delay = helpers.getDelay(clonedGd._fullLayout);
71175
71176 setTimeout(function() {
71177 var svg = toSVG(clonedGd);
71178
71179 var canvas = document.createElement('canvas');
71180 canvas.id = Lib.randstr();
71181
71182 ev = svgToImg({
71183 format: opts.format,
71184 width: clonedGd._fullLayout.width,
71185 height: clonedGd._fullLayout.height,
71186 canvas: canvas,
71187 emitter: ev,
71188 svg: svg
71189 });
71190
71191 ev.clean = function() {
71192 if(clonedGd) document.body.removeChild(clonedGd);
71193 };
71194 }, delay);
71195 }
71196
71197 var redrawFunc = helpers.getRedrawFunc(clonedGd);
71198
71199 Registry.call('plot', clonedGd, clone.data, clone.layout, clone.config)
71200 .then(redrawFunc)
71201 .then(wait)
71202 .catch(function(err) {
71203 ev.emit('error', err);
71204 });
71205
71206
71207 return ev;
71208}
71209
71210module.exports = toImage;
71211
71212},{"../lib":178,"../registry":269,"./cloneplot":270,"./helpers":273,"./svgtoimg":275,"./tosvg":277,"events":15}],277:[function(_dereq_,module,exports){
71213/**
71214* Copyright 2012-2020, Plotly, Inc.
71215* All rights reserved.
71216*
71217* This source code is licensed under the MIT license found in the
71218* LICENSE file in the root directory of this source tree.
71219*/
71220
71221
71222'use strict';
71223
71224var d3 = _dereq_('d3');
71225
71226var Lib = _dereq_('../lib');
71227var Drawing = _dereq_('../components/drawing');
71228var Color = _dereq_('../components/color');
71229
71230var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
71231var DOUBLEQUOTE_REGEX = /"/g;
71232var DUMMY_SUB = 'TOBESTRIPPED';
71233var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g');
71234
71235function htmlEntityDecode(s) {
71236 var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html('');
71237 var replaced = s.replace(/(&[^;]*;)/gi, function(d) {
71238 if(d === '&lt;') { return '&#60;'; } // special handling for brackets
71239 if(d === '&rt;') { return '&#62;'; }
71240 if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; }
71241 return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode
71242 });
71243 hiddenDiv.remove();
71244 return replaced;
71245}
71246
71247function xmlEntityEncode(str) {
71248 return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&amp;');
71249}
71250
71251module.exports = function toSVG(gd, format, scale) {
71252 var fullLayout = gd._fullLayout;
71253 var svg = fullLayout._paper;
71254 var toppaper = fullLayout._toppaper;
71255 var width = fullLayout.width;
71256 var height = fullLayout.height;
71257 var i;
71258
71259 // make background color a rect in the svg, then revert after scraping
71260 // all other alterations have been dealt with by properly preparing the svg
71261 // in the first place... like setting cursors with css classes so we don't
71262 // have to remove them, and providing the right namespaces in the svg to
71263 // begin with
71264 svg.insert('rect', ':first-child')
71265 .call(Drawing.setRect, 0, 0, width, height)
71266 .call(Color.fill, fullLayout.paper_bgcolor);
71267
71268 // subplot-specific to-SVG methods
71269 // which notably add the contents of the gl-container
71270 // into the main svg node
71271 var basePlotModules = fullLayout._basePlotModules || [];
71272 for(i = 0; i < basePlotModules.length; i++) {
71273 var _module = basePlotModules[i];
71274
71275 if(_module.toSVG) _module.toSVG(gd);
71276 }
71277
71278 // add top items above them assumes everything in toppaper is either
71279 // a group or a defs, and if it's empty (like hoverlayer) we can ignore it.
71280 if(toppaper) {
71281 var nodes = toppaper.node().childNodes;
71282
71283 // make copy of nodes as childNodes prop gets mutated in loop below
71284 var topGroups = Array.prototype.slice.call(nodes);
71285
71286 for(i = 0; i < topGroups.length; i++) {
71287 var topGroup = topGroups[i];
71288
71289 if(topGroup.childNodes.length) svg.node().appendChild(topGroup);
71290 }
71291 }
71292
71293 // remove draglayer for Adobe Illustrator compatibility
71294 if(fullLayout._draggers) {
71295 fullLayout._draggers.remove();
71296 }
71297
71298 // in case the svg element had an explicit background color, remove this
71299 // we want the rect to get the color so it's the right size; svg bg will
71300 // fill whatever container it's displayed in regardless of plot size.
71301 svg.node().style.background = '';
71302
71303 svg.selectAll('text')
71304 .attr({'data-unformatted': null, 'data-math': null})
71305 .each(function() {
71306 var txt = d3.select(this);
71307
71308 // hidden text is pre-formatting mathjax, the browser ignores it
71309 // but in a static plot it's useless and it can confuse batik
71310 // we've tried to standardize on display:none but make sure we still
71311 // catch visibility:hidden if it ever arises
71312 if(this.style.visibility === 'hidden' || this.style.display === 'none') {
71313 txt.remove();
71314 return;
71315 } else {
71316 // clear other visibility/display values to default
71317 // to not potentially confuse non-browser SVG implementations
71318 txt.style({visibility: null, display: null});
71319 }
71320
71321 // Font family styles break things because of quotation marks,
71322 // so we must remove them *after* the SVG DOM has been serialized
71323 // to a string (browsers convert singles back)
71324 var ff = this.style.fontFamily;
71325 if(ff && ff.indexOf('"') !== -1) {
71326 txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
71327 }
71328 });
71329
71330
71331 if(fullLayout._gradientUrlQueryParts) {
71332 var queryParts = [];
71333 for(var k in fullLayout._gradientUrlQueryParts) queryParts.push(k);
71334
71335 if(queryParts.length) {
71336 svg.selectAll(queryParts.join(',')).each(function() {
71337 var pt = d3.select(this);
71338
71339 // similar to font family styles above,
71340 // we must remove " after the SVG DOM has been serialized
71341 var fill = this.style.fill;
71342 if(fill && fill.indexOf('url(') !== -1) {
71343 pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
71344 }
71345
71346 var stroke = this.style.stroke;
71347 if(stroke && stroke.indexOf('url(') !== -1) {
71348 pt.style('stroke', stroke.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
71349 }
71350 });
71351 }
71352 }
71353
71354 if(format === 'pdf' || format === 'eps') {
71355 // these formats make the extra line MathJax adds around symbols look super thick in some cases
71356 // it looks better if this is removed entirely.
71357 svg.selectAll('#MathJax_SVG_glyphs path')
71358 .attr('stroke-width', 0);
71359 }
71360
71361 // fix for IE namespacing quirk?
71362 // http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with-ie
71363 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
71364 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
71365
71366 if(format === 'svg' && scale) {
71367 svg.attr('width', scale * width);
71368 svg.attr('height', scale * height);
71369 svg.attr('viewBox', '0 0 ' + width + ' ' + height);
71370 }
71371
71372 var s = new window.XMLSerializer().serializeToString(svg.node());
71373 s = htmlEntityDecode(s);
71374 s = xmlEntityEncode(s);
71375
71376 // Fix quotations around font strings and gradient URLs
71377 s = s.replace(DUMMY_REGEX, '\'');
71378
71379 // IE is very strict, so we will need to clean
71380 // svg with the following regex
71381 // yes this is messy, but do not know a better way
71382 // Even with this IE will not work due to tainted canvas
71383 // see https://github.com/kangax/fabric.js/issues/1957
71384 // http://stackoverflow.com/questions/18112047/canvas-todataurl-working-in-all-browsers-except-ie10
71385 // Leave here just in case the CORS/tainted IE issue gets resolved
71386 if(Lib.isIE()) {
71387 // replace double quote with single quote
71388 s = s.replace(/"/gi, '\'');
71389 // url in svg are single quoted
71390 // since we changed double to single
71391 // we'll need to change these to double-quoted
71392 s = s.replace(/(\('#)([^']*)('\))/gi, '(\"#$2\")');
71393 // font names with spaces will be escaped single-quoted
71394 // we'll need to change these to double-quoted
71395 s = s.replace(/(\\')/gi, '\"');
71396 }
71397
71398 return s;
71399};
71400
71401},{"../components/color":52,"../components/drawing":74,"../constants/xmlns_namespaces":159,"../lib":178,"d3":16}],278:[function(_dereq_,module,exports){
71402/**
71403* Copyright 2012-2020, Plotly, Inc.
71404* All rights reserved.
71405*
71406* This source code is licensed under the MIT license found in the
71407* LICENSE file in the root directory of this source tree.
71408*/
71409
71410'use strict';
71411
71412var Lib = _dereq_('../../lib');
71413
71414// arrayOk attributes, merge them into calcdata array
71415module.exports = function arraysToCalcdata(cd, trace) {
71416 for(var i = 0; i < cd.length; i++) cd[i].i = i;
71417
71418 Lib.mergeArray(trace.text, cd, 'tx');
71419 Lib.mergeArray(trace.hovertext, cd, 'htx');
71420
71421 var marker = trace.marker;
71422 if(marker) {
71423 Lib.mergeArray(marker.opacity, cd, 'mo', true);
71424 Lib.mergeArray(marker.color, cd, 'mc');
71425
71426 var markerLine = marker.line;
71427 if(markerLine) {
71428 Lib.mergeArray(markerLine.color, cd, 'mlc');
71429 Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw');
71430 }
71431 }
71432};
71433
71434},{"../../lib":178}],279:[function(_dereq_,module,exports){
71435/**
71436* Copyright 2012-2020, Plotly, Inc.
71437* All rights reserved.
71438*
71439* This source code is licensed under the MIT license found in the
71440* LICENSE file in the root directory of this source tree.
71441*/
71442
71443'use strict';
71444
71445var scatterAttrs = _dereq_('../scatter/attributes');
71446var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
71447var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
71448var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
71449var fontAttrs = _dereq_('../../plots/font_attributes');
71450var constants = _dereq_('./constants');
71451
71452var extendFlat = _dereq_('../../lib/extend').extendFlat;
71453
71454var textFontAttrs = fontAttrs({
71455 editType: 'calc',
71456 arrayOk: true,
71457 colorEditType: 'style',
71458
71459});
71460
71461var scatterMarkerAttrs = scatterAttrs.marker;
71462var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
71463
71464var markerLineWidth = extendFlat({},
71465 scatterMarkerLineAttrs.width, { dflt: 0 });
71466
71467var markerLine = extendFlat({
71468 width: markerLineWidth,
71469 editType: 'calc'
71470}, colorScaleAttrs('marker.line'));
71471
71472var marker = extendFlat({
71473 line: markerLine,
71474 editType: 'calc'
71475}, colorScaleAttrs('marker'), {
71476 opacity: {
71477 valType: 'number',
71478 arrayOk: true,
71479 dflt: 1,
71480 min: 0,
71481 max: 1,
71482
71483 editType: 'style',
71484
71485 }
71486});
71487
71488module.exports = {
71489 x: scatterAttrs.x,
71490 x0: scatterAttrs.x0,
71491 dx: scatterAttrs.dx,
71492 y: scatterAttrs.y,
71493 y0: scatterAttrs.y0,
71494 dy: scatterAttrs.dy,
71495
71496 text: scatterAttrs.text,
71497 texttemplate: texttemplateAttrs({editType: 'plot'}, {
71498 keys: constants.eventDataKeys
71499 }),
71500 hovertext: scatterAttrs.hovertext,
71501 hovertemplate: hovertemplateAttrs({}, {
71502 keys: constants.eventDataKeys
71503 }),
71504
71505 textposition: {
71506 valType: 'enumerated',
71507
71508 values: ['inside', 'outside', 'auto', 'none'],
71509 dflt: 'none',
71510 arrayOk: true,
71511 editType: 'calc',
71512
71513 },
71514
71515 insidetextanchor: {
71516 valType: 'enumerated',
71517 values: ['end', 'middle', 'start'],
71518 dflt: 'end',
71519
71520 editType: 'plot',
71521
71522 },
71523
71524 textangle: {
71525 valType: 'angle',
71526 dflt: 'auto',
71527
71528 editType: 'plot',
71529
71530 },
71531
71532 textfont: extendFlat({}, textFontAttrs, {
71533
71534 }),
71535
71536 insidetextfont: extendFlat({}, textFontAttrs, {
71537
71538 }),
71539
71540 outsidetextfont: extendFlat({}, textFontAttrs, {
71541
71542 }),
71543
71544 constraintext: {
71545 valType: 'enumerated',
71546 values: ['inside', 'outside', 'both', 'none'],
71547
71548 dflt: 'both',
71549 editType: 'calc',
71550
71551 },
71552
71553 cliponaxis: extendFlat({}, scatterAttrs.cliponaxis, {
71554
71555 }),
71556
71557 orientation: {
71558 valType: 'enumerated',
71559
71560 values: ['v', 'h'],
71561 editType: 'calc+clearAxisTypes',
71562
71563 },
71564
71565 base: {
71566 valType: 'any',
71567 dflt: null,
71568 arrayOk: true,
71569
71570 editType: 'calc',
71571
71572 },
71573
71574 offset: {
71575 valType: 'number',
71576 dflt: null,
71577 arrayOk: true,
71578
71579 editType: 'calc',
71580
71581 },
71582
71583 width: {
71584 valType: 'number',
71585 dflt: null,
71586 min: 0,
71587 arrayOk: true,
71588
71589 editType: 'calc',
71590
71591 },
71592
71593 marker: marker,
71594
71595 offsetgroup: {
71596 valType: 'string',
71597
71598 dflt: '',
71599 editType: 'calc',
71600
71601 },
71602 alignmentgroup: {
71603 valType: 'string',
71604
71605 dflt: '',
71606 editType: 'calc',
71607
71608 },
71609
71610 selected: {
71611 marker: {
71612 opacity: scatterAttrs.selected.marker.opacity,
71613 color: scatterAttrs.selected.marker.color,
71614 editType: 'style'
71615 },
71616 textfont: scatterAttrs.selected.textfont,
71617 editType: 'style'
71618 },
71619 unselected: {
71620 marker: {
71621 opacity: scatterAttrs.unselected.marker.opacity,
71622 color: scatterAttrs.unselected.marker.color,
71623 editType: 'style'
71624 },
71625 textfont: scatterAttrs.unselected.textfont,
71626 editType: 'style'
71627 },
71628
71629 r: scatterAttrs.r,
71630 t: scatterAttrs.t,
71631
71632 _deprecated: {
71633 bardir: {
71634 valType: 'enumerated',
71635
71636 editType: 'calc',
71637 values: ['v', 'h'],
71638
71639 }
71640 }
71641};
71642
71643},{"../../components/colorscale/attributes":59,"../../lib/extend":173,"../../plots/font_attributes":250,"../../plots/template_attributes":264,"../scatter/attributes":389,"./constants":281}],280:[function(_dereq_,module,exports){
71644/**
71645* Copyright 2012-2020, Plotly, Inc.
71646* All rights reserved.
71647*
71648* This source code is licensed under the MIT license found in the
71649* LICENSE file in the root directory of this source tree.
71650*/
71651
71652'use strict';
71653
71654var Axes = _dereq_('../../plots/cartesian/axes');
71655var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
71656var colorscaleCalc = _dereq_('../../components/colorscale/calc');
71657var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
71658var calcSelection = _dereq_('../scatter/calc_selection');
71659
71660module.exports = function calc(gd, trace) {
71661 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
71662 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
71663 var size, pos;
71664
71665 if(trace.orientation === 'h') {
71666 size = xa.makeCalcdata(trace, 'x');
71667 pos = ya.makeCalcdata(trace, 'y');
71668 } else {
71669 size = ya.makeCalcdata(trace, 'y');
71670 pos = xa.makeCalcdata(trace, 'x');
71671 }
71672
71673 // create the "calculated data" to plot
71674 var serieslen = Math.min(pos.length, size.length);
71675 var cd = new Array(serieslen);
71676
71677 // set position and size
71678 for(var i = 0; i < serieslen; i++) {
71679 cd[i] = { p: pos[i], s: size[i] };
71680
71681 if(trace.ids) {
71682 cd[i].id = String(trace.ids[i]);
71683 }
71684 }
71685
71686 // auto-z and autocolorscale if applicable
71687 if(hasColorscale(trace, 'marker')) {
71688 colorscaleCalc(gd, trace, {
71689 vals: trace.marker.color,
71690 containerStr: 'marker',
71691 cLetter: 'c'
71692 });
71693 }
71694 if(hasColorscale(trace, 'marker.line')) {
71695 colorscaleCalc(gd, trace, {
71696 vals: trace.marker.line.color,
71697 containerStr: 'marker.line',
71698 cLetter: 'c'
71699 });
71700 }
71701
71702 arraysToCalcdata(cd, trace);
71703 calcSelection(cd, trace);
71704
71705 return cd;
71706};
71707
71708},{"../../components/colorscale/calc":60,"../../components/colorscale/helpers":63,"../../plots/cartesian/axes":222,"../scatter/calc_selection":391,"./arrays_to_calcdata":278}],281:[function(_dereq_,module,exports){
71709/**
71710* Copyright 2012-2020, Plotly, Inc.
71711* All rights reserved.
71712*
71713* This source code is licensed under the MIT license found in the
71714* LICENSE file in the root directory of this source tree.
71715*/
71716
71717
71718'use strict';
71719
71720module.exports = {
71721 // padding in pixels around text
71722 TEXTPAD: 3,
71723 // 'value' and 'label' are not really necessary for bar traces,
71724 // but they were made available to `texttemplate` (maybe by accident)
71725 // via tokens `%{value}` and `%{label}` starting in 1.50.0,
71726 // so let's include them in the event data also.
71727 eventDataKeys: ['value', 'label']
71728};
71729
71730},{}],282:[function(_dereq_,module,exports){
71731/**
71732* Copyright 2012-2020, Plotly, Inc.
71733* All rights reserved.
71734*
71735* This source code is licensed under the MIT license found in the
71736* LICENSE file in the root directory of this source tree.
71737*/
71738
71739'use strict';
71740
71741var isNumeric = _dereq_('fast-isnumeric');
71742var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
71743var BADNUM = _dereq_('../../constants/numerical').BADNUM;
71744
71745var Registry = _dereq_('../../registry');
71746var Axes = _dereq_('../../plots/cartesian/axes');
71747var getAxisGroup = _dereq_('../../plots/cartesian/axis_ids').getAxisGroup;
71748var Sieve = _dereq_('./sieve.js');
71749
71750/*
71751 * Bar chart stacking/grouping positioning and autoscaling calculations
71752 * for each direction separately calculate the ranges and positions
71753 * note that this handles histograms too
71754 * now doing this one subplot at a time
71755 */
71756
71757function crossTraceCalc(gd, plotinfo) {
71758 var xa = plotinfo.xaxis;
71759 var ya = plotinfo.yaxis;
71760
71761 var fullLayout = gd._fullLayout;
71762 var fullTraces = gd._fullData;
71763 var calcTraces = gd.calcdata;
71764 var calcTracesHorz = [];
71765 var calcTracesVert = [];
71766
71767 for(var i = 0; i < fullTraces.length; i++) {
71768 var fullTrace = fullTraces[i];
71769 if(
71770 fullTrace.visible === true &&
71771 Registry.traceIs(fullTrace, 'bar') &&
71772 fullTrace.xaxis === xa._id &&
71773 fullTrace.yaxis === ya._id
71774 ) {
71775 if(fullTrace.orientation === 'h') {
71776 calcTracesHorz.push(calcTraces[i]);
71777 } else {
71778 calcTracesVert.push(calcTraces[i]);
71779 }
71780
71781 if(fullTrace._computePh) {
71782 var cd = gd.calcdata[i];
71783 for(var j = 0; j < cd.length; j++) {
71784 if(typeof cd[j].ph0 === 'function') cd[j].ph0 = cd[j].ph0();
71785 if(typeof cd[j].ph1 === 'function') cd[j].ph1 = cd[j].ph1();
71786 }
71787 }
71788 }
71789 }
71790
71791 var opts = {
71792 mode: fullLayout.barmode,
71793 norm: fullLayout.barnorm,
71794 gap: fullLayout.bargap,
71795 groupgap: fullLayout.bargroupgap
71796 };
71797
71798 setGroupPositions(gd, xa, ya, calcTracesVert, opts);
71799 setGroupPositions(gd, ya, xa, calcTracesHorz, opts);
71800}
71801
71802function setGroupPositions(gd, pa, sa, calcTraces, opts) {
71803 if(!calcTraces.length) return;
71804
71805 var excluded;
71806 var included;
71807 var i, calcTrace, fullTrace;
71808
71809 initBase(sa, calcTraces);
71810
71811 switch(opts.mode) {
71812 case 'overlay':
71813 setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts);
71814 break;
71815
71816 case 'group':
71817 // exclude from the group those traces for which the user set an offset
71818 excluded = [];
71819 included = [];
71820 for(i = 0; i < calcTraces.length; i++) {
71821 calcTrace = calcTraces[i];
71822 fullTrace = calcTrace[0].trace;
71823
71824 if(fullTrace.offset === undefined) included.push(calcTrace);
71825 else excluded.push(calcTrace);
71826 }
71827
71828 if(included.length) {
71829 setGroupPositionsInGroupMode(gd, pa, sa, included, opts);
71830 }
71831 if(excluded.length) {
71832 setGroupPositionsInOverlayMode(pa, sa, excluded, opts);
71833 }
71834 break;
71835
71836 case 'stack':
71837 case 'relative':
71838 // exclude from the stack those traces for which the user set a base
71839 excluded = [];
71840 included = [];
71841 for(i = 0; i < calcTraces.length; i++) {
71842 calcTrace = calcTraces[i];
71843 fullTrace = calcTrace[0].trace;
71844
71845 if(fullTrace.base === undefined) included.push(calcTrace);
71846 else excluded.push(calcTrace);
71847 }
71848
71849 if(included.length) {
71850 setGroupPositionsInStackOrRelativeMode(gd, pa, sa, included, opts);
71851 }
71852 if(excluded.length) {
71853 setGroupPositionsInOverlayMode(pa, sa, excluded, opts);
71854 }
71855 break;
71856 }
71857
71858 collectExtents(calcTraces, pa);
71859}
71860
71861function initBase(sa, calcTraces) {
71862 var i, j;
71863
71864 for(i = 0; i < calcTraces.length; i++) {
71865 var cd = calcTraces[i];
71866 var trace = cd[0].trace;
71867 var base = (trace.type === 'funnel') ? trace._base : trace.base;
71868 var b;
71869
71870 // not sure if it really makes sense to have dates for bar size data...
71871 // ideally if we want to make gantt charts or something we'd treat
71872 // the actual size (trace.x or y) as time delta but base as absolute
71873 // time. But included here for completeness.
71874 var scalendar = trace.orientation === 'h' ? trace.xcalendar : trace.ycalendar;
71875
71876 // 'base' on categorical axes makes no sense
71877 var d2c = sa.type === 'category' || sa.type === 'multicategory' ?
71878 function() { return null; } :
71879 sa.d2c;
71880
71881 if(isArrayOrTypedArray(base)) {
71882 for(j = 0; j < Math.min(base.length, cd.length); j++) {
71883 b = d2c(base[j], 0, scalendar);
71884 if(isNumeric(b)) {
71885 cd[j].b = +b;
71886 cd[j].hasB = 1;
71887 } else cd[j].b = 0;
71888 }
71889 for(; j < cd.length; j++) {
71890 cd[j].b = 0;
71891 }
71892 } else {
71893 b = d2c(base, 0, scalendar);
71894 var hasBase = isNumeric(b);
71895 b = hasBase ? b : 0;
71896 for(j = 0; j < cd.length; j++) {
71897 cd[j].b = b;
71898 if(hasBase) cd[j].hasB = 1;
71899 }
71900 }
71901 }
71902}
71903
71904function setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts) {
71905 // update position axis and set bar offsets and widths
71906 for(var i = 0; i < calcTraces.length; i++) {
71907 var calcTrace = calcTraces[i];
71908
71909 var sieve = new Sieve([calcTrace], {
71910 sepNegVal: false,
71911 overlapNoMerge: !opts.norm
71912 });
71913
71914 // set bar offsets and widths, and update position axis
71915 setOffsetAndWidth(pa, sieve, opts);
71916
71917 // set bar bases and sizes, and update size axis
71918 //
71919 // (note that `setGroupPositionsInOverlayMode` handles the case barnorm
71920 // is defined, because this function is also invoked for traces that
71921 // can't be grouped or stacked)
71922 if(opts.norm) {
71923 sieveBars(sieve);
71924 normalizeBars(sa, sieve, opts);
71925 } else {
71926 setBaseAndTop(sa, sieve);
71927 }
71928 }
71929}
71930
71931function setGroupPositionsInGroupMode(gd, pa, sa, calcTraces, opts) {
71932 var sieve = new Sieve(calcTraces, {
71933 sepNegVal: false,
71934 overlapNoMerge: !opts.norm
71935 });
71936
71937 // set bar offsets and widths, and update position axis
71938 setOffsetAndWidthInGroupMode(gd, pa, sieve, opts);
71939
71940 // relative-stack bars within the same trace that would otherwise
71941 // be hidden
71942 unhideBarsWithinTrace(sieve);
71943
71944 // set bar bases and sizes, and update size axis
71945 if(opts.norm) {
71946 sieveBars(sieve);
71947 normalizeBars(sa, sieve, opts);
71948 } else {
71949 setBaseAndTop(sa, sieve);
71950 }
71951}
71952
71953function setGroupPositionsInStackOrRelativeMode(gd, pa, sa, calcTraces, opts) {
71954 var sieve = new Sieve(calcTraces, {
71955 sepNegVal: opts.mode === 'relative',
71956 overlapNoMerge: !(opts.norm || opts.mode === 'stack' || opts.mode === 'relative')
71957 });
71958
71959 // set bar offsets and widths, and update position axis
71960 setOffsetAndWidth(pa, sieve, opts);
71961
71962 // set bar bases and sizes, and update size axis
71963 stackBars(sa, sieve, opts);
71964
71965 // flag the outmost bar (for text display purposes)
71966 for(var i = 0; i < calcTraces.length; i++) {
71967 var calcTrace = calcTraces[i];
71968
71969 for(var j = 0; j < calcTrace.length; j++) {
71970 var bar = calcTrace[j];
71971
71972 if(bar.s !== BADNUM) {
71973 var isOutmostBar = ((bar.b + bar.s) === sieve.get(bar.p, bar.s));
71974 if(isOutmostBar) bar._outmost = true;
71975 }
71976 }
71977 }
71978
71979 // Note that marking the outmost bars has to be done
71980 // before `normalizeBars` changes `bar.b` and `bar.s`.
71981 if(opts.norm) normalizeBars(sa, sieve, opts);
71982}
71983
71984function setOffsetAndWidth(pa, sieve, opts) {
71985 var minDiff = sieve.minDiff;
71986 var calcTraces = sieve.traces;
71987
71988 // set bar offsets and widths
71989 var barGroupWidth = minDiff * (1 - opts.gap);
71990 var barWidthPlusGap = barGroupWidth;
71991 var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));
71992
71993 // computer bar group center and bar offset
71994 var offsetFromCenter = -barWidth / 2;
71995
71996 for(var i = 0; i < calcTraces.length; i++) {
71997 var calcTrace = calcTraces[i];
71998 var t = calcTrace[0].t;
71999
72000 // store bar width and offset for this trace
72001 t.barwidth = barWidth;
72002 t.poffset = offsetFromCenter;
72003 t.bargroupwidth = barGroupWidth;
72004 t.bardelta = minDiff;
72005 }
72006
72007 // stack bars that only differ by rounding
72008 sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
72009
72010 // if defined, apply trace offset and width
72011 applyAttributes(sieve);
72012
72013 // store the bar center in each calcdata item
72014 setBarCenterAndWidth(pa, sieve);
72015
72016 // update position axes
72017 updatePositionAxis(pa, sieve);
72018}
72019
72020function setOffsetAndWidthInGroupMode(gd, pa, sieve, opts) {
72021 var fullLayout = gd._fullLayout;
72022 var positions = sieve.positions;
72023 var distinctPositions = sieve.distinctPositions;
72024 var minDiff = sieve.minDiff;
72025 var calcTraces = sieve.traces;
72026 var nTraces = calcTraces.length;
72027
72028 // if there aren't any overlapping positions,
72029 // let them have full width even if mode is group
72030 var overlap = (positions.length !== distinctPositions.length);
72031 var barGroupWidth = minDiff * (1 - opts.gap);
72032
72033 var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation;
72034 var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
72035
72036 for(var i = 0; i < nTraces; i++) {
72037 var calcTrace = calcTraces[i];
72038 var trace = calcTrace[0].trace;
72039
72040 var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
72041 var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;
72042
72043 var barWidthPlusGap;
72044 if(nOffsetGroups) {
72045 barWidthPlusGap = barGroupWidth / nOffsetGroups;
72046 } else {
72047 barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth;
72048 }
72049
72050 var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));
72051
72052 var offsetFromCenter;
72053 if(nOffsetGroups) {
72054 offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2;
72055 } else {
72056 offsetFromCenter = overlap ?
72057 ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
72058 -barWidth / 2;
72059 }
72060
72061 var t = calcTrace[0].t;
72062 t.barwidth = barWidth;
72063 t.poffset = offsetFromCenter;
72064 t.bargroupwidth = barGroupWidth;
72065 t.bardelta = minDiff;
72066 }
72067
72068 // stack bars that only differ by rounding
72069 sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
72070
72071 // if defined, apply trace width
72072 applyAttributes(sieve);
72073
72074 // store the bar center in each calcdata item
72075 setBarCenterAndWidth(pa, sieve);
72076
72077 // update position axes
72078 updatePositionAxis(pa, sieve, overlap);
72079}
72080
72081function applyAttributes(sieve) {
72082 var calcTraces = sieve.traces;
72083 var i, j;
72084
72085 for(i = 0; i < calcTraces.length; i++) {
72086 var calcTrace = calcTraces[i];
72087 var calcTrace0 = calcTrace[0];
72088 var fullTrace = calcTrace0.trace;
72089 var t = calcTrace0.t;
72090 var offset = fullTrace._offset || fullTrace.offset;
72091 var initialPoffset = t.poffset;
72092 var newPoffset;
72093
72094 if(isArrayOrTypedArray(offset)) {
72095 // if offset is an array, then clone it into t.poffset.
72096 newPoffset = Array.prototype.slice.call(offset, 0, calcTrace.length);
72097
72098 // guard against non-numeric items
72099 for(j = 0; j < newPoffset.length; j++) {
72100 if(!isNumeric(newPoffset[j])) {
72101 newPoffset[j] = initialPoffset;
72102 }
72103 }
72104
72105 // if the length of the array is too short,
72106 // then extend it with the initial value of t.poffset
72107 for(j = newPoffset.length; j < calcTrace.length; j++) {
72108 newPoffset.push(initialPoffset);
72109 }
72110
72111 t.poffset = newPoffset;
72112 } else if(offset !== undefined) {
72113 t.poffset = offset;
72114 }
72115
72116 var width = fullTrace._width || fullTrace.width;
72117 var initialBarwidth = t.barwidth;
72118
72119 if(isArrayOrTypedArray(width)) {
72120 // if width is an array, then clone it into t.barwidth.
72121 var newBarwidth = Array.prototype.slice.call(width, 0, calcTrace.length);
72122
72123 // guard against non-numeric items
72124 for(j = 0; j < newBarwidth.length; j++) {
72125 if(!isNumeric(newBarwidth[j])) newBarwidth[j] = initialBarwidth;
72126 }
72127
72128 // if the length of the array is too short,
72129 // then extend it with the initial value of t.barwidth
72130 for(j = newBarwidth.length; j < calcTrace.length; j++) {
72131 newBarwidth.push(initialBarwidth);
72132 }
72133
72134 t.barwidth = newBarwidth;
72135
72136 // if user didn't set offset,
72137 // then correct t.poffset to ensure bars remain centered
72138 if(offset === undefined) {
72139 newPoffset = [];
72140 for(j = 0; j < calcTrace.length; j++) {
72141 newPoffset.push(
72142 initialPoffset + (initialBarwidth - newBarwidth[j]) / 2
72143 );
72144 }
72145 t.poffset = newPoffset;
72146 }
72147 } else if(width !== undefined) {
72148 t.barwidth = width;
72149
72150 // if user didn't set offset,
72151 // then correct t.poffset to ensure bars remain centered
72152 if(offset === undefined) {
72153 t.poffset = initialPoffset + (initialBarwidth - width) / 2;
72154 }
72155 }
72156 }
72157}
72158
72159function setBarCenterAndWidth(pa, sieve) {
72160 var calcTraces = sieve.traces;
72161 var pLetter = getAxisLetter(pa);
72162
72163 for(var i = 0; i < calcTraces.length; i++) {
72164 var calcTrace = calcTraces[i];
72165 var t = calcTrace[0].t;
72166 var poffset = t.poffset;
72167 var poffsetIsArray = Array.isArray(poffset);
72168 var barwidth = t.barwidth;
72169 var barwidthIsArray = Array.isArray(barwidth);
72170
72171 for(var j = 0; j < calcTrace.length; j++) {
72172 var calcBar = calcTrace[j];
72173
72174 // store the actual bar width and position, for use by hover
72175 var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth;
72176 calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2;
72177 }
72178 }
72179}
72180
72181function updatePositionAxis(pa, sieve, allowMinDtick) {
72182 var calcTraces = sieve.traces;
72183 var minDiff = sieve.minDiff;
72184 var vpad = minDiff / 2;
72185
72186 Axes.minDtick(pa, sieve.minDiff, sieve.distinctPositions[0], allowMinDtick);
72187
72188 for(var i = 0; i < calcTraces.length; i++) {
72189 var calcTrace = calcTraces[i];
72190 var calcTrace0 = calcTrace[0];
72191 var fullTrace = calcTrace0.trace;
72192 var pts = [];
72193 var bar, l, r, j;
72194
72195 for(j = 0; j < calcTrace.length; j++) {
72196 bar = calcTrace[j];
72197 l = bar.p - vpad;
72198 r = bar.p + vpad;
72199 pts.push(l, r);
72200 }
72201
72202 if(fullTrace.width || fullTrace.offset) {
72203 var t = calcTrace0.t;
72204 var poffset = t.poffset;
72205 var barwidth = t.barwidth;
72206 var poffsetIsArray = Array.isArray(poffset);
72207 var barwidthIsArray = Array.isArray(barwidth);
72208
72209 for(j = 0; j < calcTrace.length; j++) {
72210 bar = calcTrace[j];
72211 var calcBarOffset = poffsetIsArray ? poffset[j] : poffset;
72212 var calcBarWidth = barwidthIsArray ? barwidth[j] : barwidth;
72213 l = bar.p + calcBarOffset;
72214 r = l + calcBarWidth;
72215 pts.push(l, r);
72216 }
72217 }
72218
72219 fullTrace._extremes[pa._id] = Axes.findExtremes(pa, pts, {padded: false});
72220 }
72221}
72222
72223// store these bar bases and tops in calcdata
72224// and make sure the size axis includes zero,
72225// along with the bases and tops of each bar.
72226function setBaseAndTop(sa, sieve) {
72227 var calcTraces = sieve.traces;
72228 var sLetter = getAxisLetter(sa);
72229
72230 for(var i = 0; i < calcTraces.length; i++) {
72231 var calcTrace = calcTraces[i];
72232 var fullTrace = calcTrace[0].trace;
72233 var pts = [];
72234 var tozero = false;
72235
72236 for(var j = 0; j < calcTrace.length; j++) {
72237 var bar = calcTrace[j];
72238 var base = bar.b;
72239 var top = base + bar.s;
72240
72241 bar[sLetter] = top;
72242 pts.push(top);
72243 if(bar.hasB) pts.push(base);
72244
72245 if(!bar.hasB || !bar.b) {
72246 tozero = true;
72247 }
72248 }
72249
72250 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
72251 tozero: tozero,
72252 padded: true
72253 });
72254 }
72255}
72256
72257function stackBars(sa, sieve, opts) {
72258 var sLetter = getAxisLetter(sa);
72259 var calcTraces = sieve.traces;
72260 var calcTrace;
72261 var fullTrace;
72262 var isFunnel;
72263 var i, j;
72264 var bar;
72265
72266 for(i = 0; i < calcTraces.length; i++) {
72267 calcTrace = calcTraces[i];
72268 fullTrace = calcTrace[0].trace;
72269
72270 if(fullTrace.type === 'funnel') {
72271 for(j = 0; j < calcTrace.length; j++) {
72272 bar = calcTrace[j];
72273
72274 if(bar.s !== BADNUM) {
72275 // create base of funnels
72276 sieve.put(bar.p, -0.5 * bar.s);
72277 }
72278 }
72279 }
72280 }
72281
72282 for(i = 0; i < calcTraces.length; i++) {
72283 calcTrace = calcTraces[i];
72284 fullTrace = calcTrace[0].trace;
72285
72286 isFunnel = (fullTrace.type === 'funnel');
72287
72288 var pts = [];
72289
72290 for(j = 0; j < calcTrace.length; j++) {
72291 bar = calcTrace[j];
72292
72293 if(bar.s !== BADNUM) {
72294 // stack current bar and get previous sum
72295 var value;
72296 if(isFunnel) {
72297 value = bar.s;
72298 } else {
72299 value = bar.s + bar.b;
72300 }
72301
72302 var base = sieve.put(bar.p, value);
72303
72304 var top = base + value;
72305
72306 // store the bar base and top in each calcdata item
72307 bar.b = base;
72308 bar[sLetter] = top;
72309
72310 if(!opts.norm) {
72311 pts.push(top);
72312 if(bar.hasB) {
72313 pts.push(base);
72314 }
72315 }
72316 }
72317 }
72318
72319 // if barnorm is set, let normalizeBars update the axis range
72320 if(!opts.norm) {
72321 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
72322 // N.B. we don't stack base with 'base',
72323 // so set tozero:true always!
72324 tozero: true,
72325 padded: true
72326 });
72327 }
72328 }
72329}
72330
72331function sieveBars(sieve) {
72332 var calcTraces = sieve.traces;
72333
72334 for(var i = 0; i < calcTraces.length; i++) {
72335 var calcTrace = calcTraces[i];
72336
72337 for(var j = 0; j < calcTrace.length; j++) {
72338 var bar = calcTrace[j];
72339
72340 if(bar.s !== BADNUM) {
72341 sieve.put(bar.p, bar.b + bar.s);
72342 }
72343 }
72344 }
72345}
72346
72347function unhideBarsWithinTrace(sieve) {
72348 var calcTraces = sieve.traces;
72349
72350 for(var i = 0; i < calcTraces.length; i++) {
72351 var calcTrace = calcTraces[i];
72352 var fullTrace = calcTrace[0].trace;
72353
72354 if(fullTrace.base === undefined) {
72355 var inTraceSieve = new Sieve([calcTrace], {
72356 sepNegVal: true,
72357 overlapNoMerge: true
72358 });
72359
72360 for(var j = 0; j < calcTrace.length; j++) {
72361 var bar = calcTrace[j];
72362
72363 if(bar.p !== BADNUM) {
72364 // stack current bar and get previous sum
72365 var base = inTraceSieve.put(bar.p, bar.b + bar.s);
72366
72367 // if previous sum if non-zero, this means:
72368 // multiple bars have same starting point are potentially hidden,
72369 // shift them vertically so that all bars are visible by default
72370 if(base) bar.b = base;
72371 }
72372 }
72373 }
72374 }
72375}
72376
72377// Note:
72378//
72379// normalizeBars requires that either sieveBars or stackBars has been
72380// previously invoked.
72381function normalizeBars(sa, sieve, opts) {
72382 var calcTraces = sieve.traces;
72383 var sLetter = getAxisLetter(sa);
72384 var sTop = opts.norm === 'fraction' ? 1 : 100;
72385 var sTiny = sTop / 1e9; // in case of rounding error in sum
72386 var sMin = sa.l2c(sa.c2l(0));
72387 var sMax = opts.mode === 'stack' ? sTop : sMin;
72388
72389 function needsPadding(v) {
72390 return (
72391 isNumeric(sa.c2l(v)) &&
72392 ((v < sMin - sTiny) || (v > sMax + sTiny) || !isNumeric(sMin))
72393 );
72394 }
72395
72396 for(var i = 0; i < calcTraces.length; i++) {
72397 var calcTrace = calcTraces[i];
72398 var fullTrace = calcTrace[0].trace;
72399 var pts = [];
72400 var tozero = false;
72401 var padded = false;
72402
72403 for(var j = 0; j < calcTrace.length; j++) {
72404 var bar = calcTrace[j];
72405
72406 if(bar.s !== BADNUM) {
72407 var scale = Math.abs(sTop / sieve.get(bar.p, bar.s));
72408 bar.b *= scale;
72409 bar.s *= scale;
72410
72411 var base = bar.b;
72412 var top = base + bar.s;
72413
72414 bar[sLetter] = top;
72415 pts.push(top);
72416 padded = padded || needsPadding(top);
72417
72418 if(bar.hasB) {
72419 pts.push(base);
72420 padded = padded || needsPadding(base);
72421 }
72422
72423 if(!bar.hasB || !bar.b) {
72424 tozero = true;
72425 }
72426 }
72427 }
72428
72429 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
72430 tozero: tozero,
72431 padded: padded
72432 });
72433 }
72434}
72435
72436// find the full position span of bars at each position
72437// for use by hover, to ensure labels move in if bars are
72438// narrower than the space they're in.
72439// run once per trace group (subplot & direction) and
72440// the same mapping is attached to all calcdata traces
72441function collectExtents(calcTraces, pa) {
72442 var pLetter = getAxisLetter(pa);
72443 var extents = {};
72444 var i, j, cd;
72445
72446 var pMin = Infinity;
72447 var pMax = -Infinity;
72448
72449 for(i = 0; i < calcTraces.length; i++) {
72450 cd = calcTraces[i];
72451 for(j = 0; j < cd.length; j++) {
72452 var p = cd[j].p;
72453 if(isNumeric(p)) {
72454 pMin = Math.min(pMin, p);
72455 pMax = Math.max(pMax, p);
72456 }
72457 }
72458 }
72459
72460 // this is just for positioning of hover labels, and nobody will care if
72461 // the label is 1px too far out; so round positions to 1/10K in case
72462 // position values don't exactly match from trace to trace
72463 var roundFactor = 10000 / (pMax - pMin);
72464 var round = extents.round = function(p) {
72465 return String(Math.round(roundFactor * (p - pMin)));
72466 };
72467
72468 for(i = 0; i < calcTraces.length; i++) {
72469 cd = calcTraces[i];
72470 cd[0].t.extents = extents;
72471
72472 var poffset = cd[0].t.poffset;
72473 var poffsetIsArray = Array.isArray(poffset);
72474
72475 for(j = 0; j < cd.length; j++) {
72476 var di = cd[j];
72477 var p0 = di[pLetter] - di.w / 2;
72478
72479 if(isNumeric(p0)) {
72480 var p1 = di[pLetter] + di.w / 2;
72481 var pVal = round(di.p);
72482 if(extents[pVal]) {
72483 extents[pVal] = [Math.min(p0, extents[pVal][0]), Math.max(p1, extents[pVal][1])];
72484 } else {
72485 extents[pVal] = [p0, p1];
72486 }
72487 }
72488
72489 di.p0 = di.p + (poffsetIsArray ? poffset[j] : poffset);
72490 di.p1 = di.p0 + di.w;
72491 di.s0 = di.b;
72492 di.s1 = di.s0 + di.s;
72493 }
72494 }
72495}
72496
72497function getAxisLetter(ax) {
72498 return ax._id.charAt(0);
72499}
72500
72501module.exports = {
72502 crossTraceCalc: crossTraceCalc,
72503 setGroupPositions: setGroupPositions
72504};
72505
72506},{"../../constants/numerical":158,"../../lib":178,"../../plots/cartesian/axes":222,"../../plots/cartesian/axis_ids":225,"../../registry":269,"./sieve.js":292,"fast-isnumeric":18}],283:[function(_dereq_,module,exports){
72507/**
72508* Copyright 2012-2020, Plotly, Inc.
72509* All rights reserved.
72510*
72511* This source code is licensed under the MIT license found in the
72512* LICENSE file in the root directory of this source tree.
72513*/
72514
72515'use strict';
72516
72517var Lib = _dereq_('../../lib');
72518var Color = _dereq_('../../components/color');
72519var Registry = _dereq_('../../registry');
72520
72521var handleXYDefaults = _dereq_('../scatter/xy_defaults');
72522var handleStyleDefaults = _dereq_('./style_defaults');
72523var getAxisGroup = _dereq_('../../plots/cartesian/axis_ids').getAxisGroup;
72524var attributes = _dereq_('./attributes');
72525
72526var coerceFont = Lib.coerceFont;
72527
72528function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
72529 function coerce(attr, dflt) {
72530 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
72531 }
72532
72533 var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
72534 if(!len) {
72535 traceOut.visible = false;
72536 return;
72537 }
72538
72539 coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v');
72540 coerce('base');
72541 coerce('offset');
72542 coerce('width');
72543
72544 coerce('text');
72545 coerce('hovertext');
72546 coerce('hovertemplate');
72547
72548 var textposition = coerce('textposition');
72549 handleText(traceIn, traceOut, layout, coerce, textposition, {
72550 moduleHasSelected: true,
72551 moduleHasUnselected: true,
72552 moduleHasConstrain: true,
72553 moduleHasCliponaxis: true,
72554 moduleHasTextangle: true,
72555 moduleHasInsideanchor: true
72556 });
72557
72558 handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
72559
72560 var lineColor = (traceOut.marker.line || {}).color;
72561
72562 // override defaultColor for error bars with defaultLine
72563 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
72564 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
72565 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
72566
72567 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
72568}
72569
72570function handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce) {
72571 var orientation = traceOut.orientation;
72572 // N.B. grouping is done across all trace types that support it
72573 var posAxId = traceOut[{v: 'x', h: 'y'}[orientation] + 'axis'];
72574 var groupId = getAxisGroup(fullLayout, posAxId) + orientation;
72575
72576 var alignmentOpts = fullLayout._alignmentOpts || {};
72577 var alignmentgroup = coerce('alignmentgroup');
72578
72579 var alignmentGroups = alignmentOpts[groupId];
72580 if(!alignmentGroups) alignmentGroups = alignmentOpts[groupId] = {};
72581
72582 var alignmentGroupOpts = alignmentGroups[alignmentgroup];
72583
72584 if(alignmentGroupOpts) {
72585 alignmentGroupOpts.traces.push(traceOut);
72586 } else {
72587 alignmentGroupOpts = alignmentGroups[alignmentgroup] = {
72588 traces: [traceOut],
72589 alignmentIndex: Object.keys(alignmentGroups).length,
72590 offsetGroups: {}
72591 };
72592 }
72593
72594 var offsetgroup = coerce('offsetgroup');
72595 var offsetGroups = alignmentGroupOpts.offsetGroups;
72596 var offsetGroupOpts = offsetGroups[offsetgroup];
72597
72598 if(offsetgroup) {
72599 if(!offsetGroupOpts) {
72600 offsetGroupOpts = offsetGroups[offsetgroup] = {
72601 offsetIndex: Object.keys(offsetGroups).length
72602 };
72603 }
72604
72605 traceOut._offsetIndex = offsetGroupOpts.offsetIndex;
72606 }
72607}
72608
72609function crossTraceDefaults(fullData, fullLayout) {
72610 var traceIn, traceOut;
72611
72612 function coerce(attr) {
72613 return Lib.coerce(traceOut._input, traceOut, attributes, attr);
72614 }
72615
72616 if(fullLayout.barmode === 'group') {
72617 for(var i = 0; i < fullData.length; i++) {
72618 traceOut = fullData[i];
72619
72620 if(traceOut.type === 'bar') {
72621 traceIn = traceOut._input;
72622 handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
72623 }
72624 }
72625 }
72626}
72627
72628function handleText(traceIn, traceOut, layout, coerce, textposition, opts) {
72629 opts = opts || {};
72630 var moduleHasSelected = !(opts.moduleHasSelected === false);
72631 var moduleHasUnselected = !(opts.moduleHasUnselected === false);
72632 var moduleHasConstrain = !(opts.moduleHasConstrain === false);
72633 var moduleHasCliponaxis = !(opts.moduleHasCliponaxis === false);
72634 var moduleHasTextangle = !(opts.moduleHasTextangle === false);
72635 var moduleHasInsideanchor = !(opts.moduleHasInsideanchor === false);
72636 var hasPathbar = !!opts.hasPathbar;
72637
72638 var hasBoth = Array.isArray(textposition) || textposition === 'auto';
72639 var hasInside = hasBoth || textposition === 'inside';
72640 var hasOutside = hasBoth || textposition === 'outside';
72641
72642 if(hasInside || hasOutside) {
72643 var dfltFont = coerceFont(coerce, 'textfont', layout.font);
72644
72645 // Note that coercing `insidetextfont` is always needed –
72646 // even if `textposition` is `outside` for each trace – since
72647 // an outside label can become an inside one, for example because
72648 // of a bar being stacked on top of it.
72649 var insideTextFontDefault = Lib.extendFlat({}, dfltFont);
72650 var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
72651 var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
72652 if(isColorInheritedFromLayoutFont) {
72653 delete insideTextFontDefault.color;
72654 }
72655 coerceFont(coerce, 'insidetextfont', insideTextFontDefault);
72656
72657 if(hasPathbar) {
72658 var pathbarTextFontDefault = Lib.extendFlat({}, dfltFont);
72659 if(isColorInheritedFromLayoutFont) {
72660 delete pathbarTextFontDefault.color;
72661 }
72662 coerceFont(coerce, 'pathbar.textfont', pathbarTextFontDefault);
72663 }
72664
72665 if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);
72666
72667 if(moduleHasSelected) coerce('selected.textfont.color');
72668 if(moduleHasUnselected) coerce('unselected.textfont.color');
72669 if(moduleHasConstrain) coerce('constraintext');
72670 if(moduleHasCliponaxis) coerce('cliponaxis');
72671 if(moduleHasTextangle) coerce('textangle');
72672
72673 coerce('texttemplate');
72674 }
72675
72676 if(hasInside) {
72677 if(moduleHasInsideanchor) coerce('insidetextanchor');
72678 }
72679}
72680
72681module.exports = {
72682 supplyDefaults: supplyDefaults,
72683 crossTraceDefaults: crossTraceDefaults,
72684 handleGroupingDefaults: handleGroupingDefaults,
72685 handleText: handleText
72686};
72687
72688},{"../../components/color":52,"../../lib":178,"../../plots/cartesian/axis_ids":225,"../../registry":269,"../scatter/xy_defaults":415,"./attributes":279,"./style_defaults":294}],284:[function(_dereq_,module,exports){
72689/**
72690* Copyright 2012-2020, Plotly, Inc.
72691* All rights reserved.
72692*
72693* This source code is licensed under the MIT license found in the
72694* LICENSE file in the root directory of this source tree.
72695*/
72696
72697'use strict';
72698
72699module.exports = function eventData(out, pt, trace) {
72700 // standard cartesian event data
72701 out.x = 'xVal' in pt ? pt.xVal : pt.x;
72702 out.y = 'yVal' in pt ? pt.yVal : pt.y;
72703 if(pt.xa) out.xaxis = pt.xa;
72704 if(pt.ya) out.yaxis = pt.ya;
72705
72706 if(trace.orientation === 'h') {
72707 out.label = out.y;
72708 out.value = out.x;
72709 } else {
72710 out.label = out.x;
72711 out.value = out.y;
72712 }
72713
72714 return out;
72715};
72716
72717},{}],285:[function(_dereq_,module,exports){
72718/**
72719* Copyright 2012-2020, Plotly, Inc.
72720* All rights reserved.
72721*
72722* This source code is licensed under the MIT license found in the
72723* LICENSE file in the root directory of this source tree.
72724*/
72725
72726'use strict';
72727
72728var isNumeric = _dereq_('fast-isnumeric');
72729var tinycolor = _dereq_('tinycolor2');
72730var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
72731
72732exports.coerceString = function(attributeDefinition, value, defaultValue) {
72733 if(typeof value === 'string') {
72734 if(value || !attributeDefinition.noBlank) return value;
72735 } else if(typeof value === 'number' || value === true) {
72736 if(!attributeDefinition.strict) return String(value);
72737 }
72738
72739 return (defaultValue !== undefined) ?
72740 defaultValue :
72741 attributeDefinition.dflt;
72742};
72743
72744exports.coerceNumber = function(attributeDefinition, value, defaultValue) {
72745 if(isNumeric(value)) {
72746 value = +value;
72747
72748 var min = attributeDefinition.min;
72749 var max = attributeDefinition.max;
72750 var isOutOfBounds = (min !== undefined && value < min) ||
72751 (max !== undefined && value > max);
72752
72753 if(!isOutOfBounds) return value;
72754 }
72755
72756 return (defaultValue !== undefined) ?
72757 defaultValue :
72758 attributeDefinition.dflt;
72759};
72760
72761exports.coerceColor = function(attributeDefinition, value, defaultValue) {
72762 if(tinycolor(value).isValid()) return value;
72763
72764 return (defaultValue !== undefined) ?
72765 defaultValue :
72766 attributeDefinition.dflt;
72767};
72768
72769exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) {
72770 if(attributeDefinition.coerceNumber) value = +value;
72771
72772 if(attributeDefinition.values.indexOf(value) !== -1) return value;
72773
72774 return (defaultValue !== undefined) ?
72775 defaultValue :
72776 attributeDefinition.dflt;
72777};
72778
72779exports.getValue = function(arrayOrScalar, index) {
72780 var value;
72781 if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
72782 else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
72783 return value;
72784};
72785
72786exports.getLineWidth = function(trace, di) {
72787 var w =
72788 (0 < di.mlw) ? di.mlw :
72789 !isArrayOrTypedArray(trace.marker.line.width) ? trace.marker.line.width :
72790 0;
72791
72792 return w;
72793};
72794
72795},{"../../lib":178,"fast-isnumeric":18,"tinycolor2":35}],286:[function(_dereq_,module,exports){
72796/**
72797* Copyright 2012-2020, Plotly, Inc.
72798* All rights reserved.
72799*
72800* This source code is licensed under the MIT license found in the
72801* LICENSE file in the root directory of this source tree.
72802*/
72803
72804
72805'use strict';
72806
72807var Fx = _dereq_('../../components/fx');
72808var Registry = _dereq_('../../registry');
72809var Color = _dereq_('../../components/color');
72810
72811var fillText = _dereq_('../../lib').fillText;
72812var getLineWidth = _dereq_('./helpers').getLineWidth;
72813var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
72814var BADNUM = _dereq_('../../constants/numerical').BADNUM;
72815
72816function hoverPoints(pointData, xval, yval, hovermode) {
72817 var barPointData = hoverOnBars(pointData, xval, yval, hovermode);
72818
72819 if(barPointData) {
72820 var cd = barPointData.cd;
72821 var trace = cd[0].trace;
72822 var di = cd[barPointData.index];
72823
72824 barPointData.color = getTraceColor(trace, di);
72825 Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, barPointData);
72826
72827 return [barPointData];
72828 }
72829}
72830
72831function hoverOnBars(pointData, xval, yval, hovermode) {
72832 var cd = pointData.cd;
72833 var trace = cd[0].trace;
72834 var t = cd[0].t;
72835 var isClosest = (hovermode === 'closest');
72836 var isWaterfall = (trace.type === 'waterfall');
72837 var maxHoverDistance = pointData.maxHoverDistance;
72838
72839 var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc;
72840
72841 function thisBarMinPos(di) { return di[posLetter] - di.w / 2; }
72842 function thisBarMaxPos(di) { return di[posLetter] + di.w / 2; }
72843
72844 var minPos = isClosest ?
72845 thisBarMinPos :
72846 function(di) {
72847 /*
72848 * In compare mode, accept a bar if you're on it *or* its group.
72849 * Nearly always it's the group that matters, but in case the bar
72850 * was explicitly set wider than its group we'd better accept the
72851 * whole bar.
72852 *
72853 * use `bardelta` instead of `bargroupwidth` so we accept hover
72854 * in the gap. That way hover doesn't flash on and off as you
72855 * mouse over the plot in compare modes.
72856 * In 'closest' mode though the flashing seems inevitable,
72857 * without far more complex logic
72858 */
72859 return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2);
72860 };
72861
72862 var maxPos = isClosest ?
72863 thisBarMaxPos :
72864 function(di) {
72865 return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2);
72866 };
72867
72868 function _positionFn(_minPos, _maxPos) {
72869 // add a little to the pseudo-distance for wider bars, so that like scatter,
72870 // if you are over two overlapping bars, the narrower one wins.
72871 return Fx.inbox(_minPos - posVal, _maxPos - posVal,
72872 maxHoverDistance + Math.min(1, Math.abs(_maxPos - _minPos) / pRangeCalc) - 1);
72873 }
72874
72875 function positionFn(di) {
72876 return _positionFn(minPos(di), maxPos(di));
72877 }
72878
72879 function thisBarPositionFn(di) {
72880 return _positionFn(thisBarMinPos(di), thisBarMaxPos(di));
72881 }
72882
72883 function sizeFn(di) {
72884 var v = sizeVal;
72885 var b = di.b;
72886 var s = di[sizeLetter];
72887
72888 if(isWaterfall) {
72889 var rawS = Math.abs(di.rawS) || 0;
72890 if(v > 0) {
72891 s += rawS;
72892 } else if(v < 0) {
72893 s -= rawS;
72894 }
72895 }
72896
72897 // add a gradient so hovering near the end of a
72898 // bar makes it a little closer match
72899 return Fx.inbox(b - v, s - v, maxHoverDistance + (s - v) / (s - b) - 1);
72900 }
72901
72902 if(trace.orientation === 'h') {
72903 posVal = yval;
72904 sizeVal = xval;
72905 posLetter = 'y';
72906 sizeLetter = 'x';
72907 dx = sizeFn;
72908 dy = positionFn;
72909 } else {
72910 posVal = xval;
72911 sizeVal = yval;
72912 posLetter = 'x';
72913 sizeLetter = 'y';
72914 dy = sizeFn;
72915 dx = positionFn;
72916 }
72917
72918 var pa = pointData[posLetter + 'a'];
72919 var sa = pointData[sizeLetter + 'a'];
72920
72921 pRangeCalc = Math.abs(pa.r2c(pa.range[1]) - pa.r2c(pa.range[0]));
72922
72923 function dxy(di) { return (dx(di) + dy(di)) / 2; }
72924 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
72925 Fx.getClosest(cd, distfn, pointData);
72926
72927 // skip the rest (for this trace) if we didn't find a close point
72928 if(pointData.index === false) return;
72929
72930 // skip points inside axis rangebreaks
72931 if(cd[pointData.index].p === BADNUM) return;
72932
72933 // if we get here and we're not in 'closest' mode, push min/max pos back
72934 // onto the group - even though that means occasionally the mouse will be
72935 // over the hover label.
72936 if(!isClosest) {
72937 minPos = function(di) {
72938 return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2);
72939 };
72940 maxPos = function(di) {
72941 return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
72942 };
72943 }
72944
72945 // the closest data point
72946 var index = pointData.index;
72947 var di = cd[index];
72948
72949 var size = (trace.base) ? di.b + di.s : di.s;
72950 pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true);
72951 pointData[sizeLetter + 'LabelVal'] = size;
72952
72953 var extent = t.extents[t.extents.round(di.p)];
72954 pointData[posLetter + '0'] = pa.c2p(isClosest ? minPos(di) : extent[0], true);
72955 pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true);
72956 pointData[posLetter + 'LabelVal'] = di.p;
72957
72958 pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal']);
72959 pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']);
72960
72961 // spikelines always want "closest" distance regardless of hovermode
72962 pointData.spikeDistance = (sizeFn(di) + thisBarPositionFn(di)) / 2 - maxHoverDistance;
72963 // they also want to point to the data value, regardless of where the label goes
72964 // in case of bars shifted within groups
72965 pointData[posLetter + 'Spike'] = pa.c2p(di.p, true);
72966
72967 fillText(di, trace, pointData);
72968 pointData.hovertemplate = trace.hovertemplate;
72969
72970 return pointData;
72971}
72972
72973function getTraceColor(trace, di) {
72974 var mc = di.mcc || trace.marker.color;
72975 var mlc = di.mlcc || trace.marker.line.color;
72976 var mlw = getLineWidth(trace, di);
72977
72978 if(Color.opacity(mc)) return mc;
72979 else if(Color.opacity(mlc) && mlw) return mlc;
72980}
72981
72982module.exports = {
72983 hoverPoints: hoverPoints,
72984 hoverOnBars: hoverOnBars,
72985 getTraceColor: getTraceColor
72986};
72987
72988},{"../../components/color":52,"../../components/fx":92,"../../constants/numerical":158,"../../lib":178,"../../plots/cartesian/axes":222,"../../registry":269,"./helpers":285}],287:[function(_dereq_,module,exports){
72989/**
72990* Copyright 2012-2020, Plotly, Inc.
72991* All rights reserved.
72992*
72993* This source code is licensed under the MIT license found in the
72994* LICENSE file in the root directory of this source tree.
72995*/
72996
72997'use strict';
72998
72999module.exports = {
73000 attributes: _dereq_('./attributes'),
73001 layoutAttributes: _dereq_('./layout_attributes'),
73002 supplyDefaults: _dereq_('./defaults').supplyDefaults,
73003 crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults,
73004 supplyLayoutDefaults: _dereq_('./layout_defaults'),
73005 calc: _dereq_('./calc'),
73006 crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc,
73007 colorbar: _dereq_('../scatter/marker_colorbar'),
73008 arraysToCalcdata: _dereq_('./arrays_to_calcdata'),
73009 plot: _dereq_('./plot').plot,
73010 style: _dereq_('./style').style,
73011 styleOnSelect: _dereq_('./style').styleOnSelect,
73012 hoverPoints: _dereq_('./hover').hoverPoints,
73013 eventData: _dereq_('./event_data'),
73014 selectPoints: _dereq_('./select'),
73015
73016 moduleType: 'trace',
73017 name: 'bar',
73018 basePlotModule: _dereq_('../../plots/cartesian'),
73019 categories: ['bar-like', 'cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'],
73020 animatable: true,
73021 meta: {
73022
73023 }
73024};
73025
73026},{"../../plots/cartesian":235,"../scatter/marker_colorbar":407,"./arrays_to_calcdata":278,"./attributes":279,"./calc":280,"./cross_trace_calc":282,"./defaults":283,"./event_data":284,"./hover":286,"./layout_attributes":288,"./layout_defaults":289,"./plot":290,"./select":291,"./style":293}],288:[function(_dereq_,module,exports){
73027/**
73028* Copyright 2012-2020, Plotly, Inc.
73029* All rights reserved.
73030*
73031* This source code is licensed under the MIT license found in the
73032* LICENSE file in the root directory of this source tree.
73033*/
73034
73035'use strict';
73036
73037
73038module.exports = {
73039 barmode: {
73040 valType: 'enumerated',
73041 values: ['stack', 'group', 'overlay', 'relative'],
73042 dflt: 'group',
73043
73044 editType: 'calc',
73045
73046 },
73047 barnorm: {
73048 valType: 'enumerated',
73049 values: ['', 'fraction', 'percent'],
73050 dflt: '',
73051
73052 editType: 'calc',
73053
73054 },
73055 bargap: {
73056 valType: 'number',
73057 min: 0,
73058 max: 1,
73059
73060 editType: 'calc',
73061
73062 },
73063 bargroupgap: {
73064 valType: 'number',
73065 min: 0,
73066 max: 1,
73067 dflt: 0,
73068
73069 editType: 'calc',
73070
73071 }
73072};
73073
73074},{}],289:[function(_dereq_,module,exports){
73075/**
73076* Copyright 2012-2020, Plotly, Inc.
73077* All rights reserved.
73078*
73079* This source code is licensed under the MIT license found in the
73080* LICENSE file in the root directory of this source tree.
73081*/
73082
73083'use strict';
73084
73085var Registry = _dereq_('../../registry');
73086var Axes = _dereq_('../../plots/cartesian/axes');
73087var Lib = _dereq_('../../lib');
73088
73089var layoutAttributes = _dereq_('./layout_attributes');
73090
73091module.exports = function(layoutIn, layoutOut, fullData) {
73092 function coerce(attr, dflt) {
73093 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
73094 }
73095
73096 var hasBars = false;
73097 var shouldBeGapless = false;
73098 var gappedAnyway = false;
73099 var usedSubplots = {};
73100
73101 var mode = coerce('barmode');
73102
73103 for(var i = 0; i < fullData.length; i++) {
73104 var trace = fullData[i];
73105 if(Registry.traceIs(trace, 'bar') && trace.visible) hasBars = true;
73106 else continue;
73107
73108 // if we have at least 2 grouped bar traces on the same subplot,
73109 // we should default to a gap anyway, even if the data is histograms
73110 if(mode === 'group') {
73111 var subploti = trace.xaxis + trace.yaxis;
73112 if(usedSubplots[subploti]) gappedAnyway = true;
73113 usedSubplots[subploti] = true;
73114 }
73115
73116 if(trace.visible && trace.type === 'histogram') {
73117 var pa = Axes.getFromId({_fullLayout: layoutOut},
73118 trace[trace.orientation === 'v' ? 'xaxis' : 'yaxis']);
73119 if(pa.type !== 'category') shouldBeGapless = true;
73120 }
73121 }
73122
73123 if(!hasBars) {
73124 delete layoutOut.barmode;
73125 return;
73126 }
73127
73128 if(mode !== 'overlay') coerce('barnorm');
73129
73130 coerce('bargap', (shouldBeGapless && !gappedAnyway) ? 0 : 0.2);
73131 coerce('bargroupgap');
73132};
73133
73134},{"../../lib":178,"../../plots/cartesian/axes":222,"../../registry":269,"./layout_attributes":288}],290:[function(_dereq_,module,exports){
73135/**
73136* Copyright 2012-2020, Plotly, Inc.
73137* All rights reserved.
73138*
73139* This source code is licensed under the MIT license found in the
73140* LICENSE file in the root directory of this source tree.
73141*/
73142
73143'use strict';
73144
73145var d3 = _dereq_('d3');
73146var isNumeric = _dereq_('fast-isnumeric');
73147
73148var Lib = _dereq_('../../lib');
73149var svgTextUtils = _dereq_('../../lib/svg_text_utils');
73150
73151var Color = _dereq_('../../components/color');
73152var Drawing = _dereq_('../../components/drawing');
73153var Registry = _dereq_('../../registry');
73154var tickText = _dereq_('../../plots/cartesian/axes').tickText;
73155
73156var uniformText = _dereq_('./uniform_text');
73157var recordMinTextSize = uniformText.recordMinTextSize;
73158var clearMinTextSize = uniformText.clearMinTextSize;
73159
73160var style = _dereq_('./style');
73161var helpers = _dereq_('./helpers');
73162var constants = _dereq_('./constants');
73163var attributes = _dereq_('./attributes');
73164
73165var attributeText = attributes.text;
73166var attributeTextPosition = attributes.textposition;
73167
73168var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue;
73169
73170var TEXTPAD = constants.TEXTPAD;
73171
73172function keyFunc(d) {return d.id;}
73173function getKeyFunc(trace) {
73174 if(trace.ids) {
73175 return keyFunc;
73176 }
73177}
73178
73179function dirSign(a, b) {
73180 return (a < b) ? 1 : -1;
73181}
73182
73183function getXY(di, xa, ya, isHorizontal) {
73184 var s = [];
73185 var p = [];
73186
73187 var sAxis = isHorizontal ? xa : ya;
73188 var pAxis = isHorizontal ? ya : xa;
73189
73190 s[0] = sAxis.c2p(di.s0, true);
73191 p[0] = pAxis.c2p(di.p0, true);
73192
73193 s[1] = sAxis.c2p(di.s1, true);
73194 p[1] = pAxis.c2p(di.p1, true);
73195
73196 return isHorizontal ? [s, p] : [p, s];
73197}
73198
73199function transition(selection, fullLayout, opts, makeOnCompleteCallback) {
73200 if(!fullLayout.uniformtext.mode && hasTransition(opts)) {
73201 var onComplete;
73202 if(makeOnCompleteCallback) {
73203 onComplete = makeOnCompleteCallback();
73204 }
73205 return selection
73206 .transition()
73207 .duration(opts.duration)
73208 .ease(opts.easing)
73209 .each('end', function() { onComplete && onComplete(); })
73210 .each('interrupt', function() { onComplete && onComplete(); });
73211 } else {
73212 return selection;
73213 }
73214}
73215
73216function hasTransition(transitionOpts) {
73217 return transitionOpts && transitionOpts.duration > 0;
73218}
73219
73220function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) {
73221 var xa = plotinfo.xaxis;
73222 var ya = plotinfo.yaxis;
73223 var fullLayout = gd._fullLayout;
73224
73225 if(!opts) {
73226 opts = {
73227 mode: fullLayout.barmode,
73228 norm: fullLayout.barmode,
73229 gap: fullLayout.bargap,
73230 groupgap: fullLayout.bargroupgap
73231 };
73232
73233 // don't clear bar when this is called from waterfall or funnel
73234 clearMinTextSize('bar', fullLayout);
73235 }
73236
73237 var bartraces = Lib.makeTraceGroups(traceLayer, cdModule, 'trace bars').each(function(cd) {
73238 var plotGroup = d3.select(this);
73239 var trace = cd[0].trace;
73240 var isWaterfall = (trace.type === 'waterfall');
73241 var isFunnel = (trace.type === 'funnel');
73242 var isBar = (trace.type === 'bar');
73243 var shouldDisplayZeros = (isBar || isFunnel);
73244
73245 var adjustPixel = 0;
73246 if(isWaterfall && trace.connector.visible && trace.connector.mode === 'between') {
73247 adjustPixel = trace.connector.line.width / 2;
73248 }
73249
73250 var isHorizontal = (trace.orientation === 'h');
73251 var withTransition = hasTransition(opts);
73252
73253 var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
73254
73255 var keyFunc = getKeyFunc(trace);
73256 var bars = pointGroup.selectAll('g.point').data(Lib.identity, keyFunc);
73257
73258 bars.enter().append('g')
73259 .classed('point', true);
73260
73261 bars.exit().remove();
73262
73263 bars.each(function(di, i) {
73264 var bar = d3.select(this);
73265
73266 // now display the bar
73267 // clipped xf/yf (2nd arg true): non-positive
73268 // log values go off-screen by plotwidth
73269 // so you see them continue if you drag the plot
73270 var xy = getXY(di, xa, ya, isHorizontal);
73271
73272 var x0 = xy[0][0];
73273 var x1 = xy[0][1];
73274 var y0 = xy[1][0];
73275 var y1 = xy[1][1];
73276
73277 // empty bars
73278 var isBlank = (isHorizontal ? x1 - x0 : y1 - y0) === 0;
73279
73280 // display zeros if line.width > 0
73281 if(isBlank && shouldDisplayZeros && helpers.getLineWidth(trace, di)) {
73282 isBlank = false;
73283 }
73284
73285 // skip nulls
73286 if(!isBlank) {
73287 isBlank = (
73288 !isNumeric(x0) ||
73289 !isNumeric(x1) ||
73290 !isNumeric(y0) ||
73291 !isNumeric(y1)
73292 );
73293 }
73294
73295 // record isBlank
73296 di.isBlank = isBlank;
73297
73298 // for blank bars, ensure start and end positions are equal - important for smooth transitions
73299 if(isBlank) {
73300 if(isHorizontal) {
73301 x1 = x0;
73302 } else {
73303 y1 = y0;
73304 }
73305 }
73306
73307 // in waterfall mode `between` we need to adjust bar end points to match the connector width
73308 if(adjustPixel && !isBlank) {
73309 if(isHorizontal) {
73310 x0 -= dirSign(x0, x1) * adjustPixel;
73311 x1 += dirSign(x0, x1) * adjustPixel;
73312 } else {
73313 y0 -= dirSign(y0, y1) * adjustPixel;
73314 y1 += dirSign(y0, y1) * adjustPixel;
73315 }
73316 }
73317
73318 var lw;
73319 var mc;
73320
73321 if(trace.type === 'waterfall') {
73322 if(!isBlank) {
73323 var cont = trace[di.dir].marker;
73324 lw = cont.line.width;
73325 mc = cont.color;
73326 }
73327 } else {
73328 lw = helpers.getLineWidth(trace, di);
73329 mc = di.mc || trace.marker.color;
73330 }
73331
73332 function roundWithLine(v) {
73333 var offset = d3.round((lw / 2) % 1, 2);
73334
73335 // if there are explicit gaps, don't round,
73336 // it can make the gaps look crappy
73337 return (opts.gap === 0 && opts.groupgap === 0) ?
73338 d3.round(Math.round(v) - offset, 2) : v;
73339 }
73340
73341 function expandToVisible(v, vc, hideZeroSpan) {
73342 if(hideZeroSpan && v === vc) {
73343 // should not expand zero span bars
73344 // when start and end positions are identical
73345 // i.e. for vertical when y0 === y1
73346 // and for horizontal when x0 === x1
73347 return v;
73348 }
73349
73350 // if it's not in danger of disappearing entirely,
73351 // round more precisely
73352 return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
73353 // but if it's very thin, expand it so it's
73354 // necessarily visible, even if it might overlap
73355 // its neighbor
73356 (v > vc ? Math.ceil(v) : Math.floor(v));
73357 }
73358
73359 if(!gd._context.staticPlot) {
73360 // if bars are not fully opaque or they have a line
73361 // around them, round to integer pixels, mainly for
73362 // safari so we prevent overlaps from its expansive
73363 // pixelation. if the bars ARE fully opaque and have
73364 // no line, expand to a full pixel to make sure we
73365 // can see them
73366
73367 var op = Color.opacity(mc);
73368 var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible;
73369
73370 x0 = fixpx(x0, x1, isHorizontal);
73371 x1 = fixpx(x1, x0, isHorizontal);
73372 y0 = fixpx(y0, y1, !isHorizontal);
73373 y1 = fixpx(y1, y0, !isHorizontal);
73374 }
73375
73376 var sel = transition(Lib.ensureSingle(bar, 'path'), fullLayout, opts, makeOnCompleteCallback);
73377 sel
73378 .style('vector-effect', 'non-scaling-stroke')
73379 .attr('d', isNaN((x1 - x0) * (y1 - y0)) ? 'M0,0Z' : 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
73380 .call(Drawing.setClipUrl, plotinfo.layerClipId, gd);
73381
73382 if(!fullLayout.uniformtext.mode && withTransition) {
73383 var styleFns = Drawing.makePointStyleFns(trace);
73384 Drawing.singlePointStyle(di, sel, trace, styleFns, gd);
73385 }
73386
73387 appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback);
73388
73389 if(plotinfo.layerClipId) {
73390 Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar);
73391 }
73392 });
73393
73394 // lastly, clip points groups of `cliponaxis !== false` traces
73395 // on `plotinfo._hasClipOnAxisFalse === true` subplots
73396 var hasClipOnAxisFalse = trace.cliponaxis === false;
73397 Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd);
73398 });
73399
73400 // error bars are on the top
73401 Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts);
73402}
73403
73404function appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) {
73405 var xa = plotinfo.xaxis;
73406 var ya = plotinfo.yaxis;
73407
73408 var fullLayout = gd._fullLayout;
73409 var textPosition;
73410
73411 function appendTextNode(bar, text, font) {
73412 var textSelection = Lib.ensureSingle(bar, 'text')
73413 .text(text)
73414 .attr({
73415 'class': 'bartext bartext-' + textPosition,
73416 'text-anchor': 'middle',
73417 // prohibit tex interpretation until we can handle
73418 // tex and regular text together
73419 'data-notex': 1
73420 })
73421 .call(Drawing.font, font)
73422 .call(svgTextUtils.convertToTspans, gd);
73423
73424 return textSelection;
73425 }
73426
73427 // get trace attributes
73428 var trace = cd[0].trace;
73429 var isHorizontal = (trace.orientation === 'h');
73430
73431 var text = getText(fullLayout, cd, i, xa, ya);
73432 textPosition = getTextPosition(trace, i);
73433
73434 // compute text position
73435 var inStackOrRelativeMode =
73436 opts.mode === 'stack' ||
73437 opts.mode === 'relative';
73438
73439 var calcBar = cd[i];
73440 var isOutmostBar = !inStackOrRelativeMode || calcBar._outmost;
73441
73442 if(!text ||
73443 textPosition === 'none' ||
73444 ((calcBar.isBlank || x0 === x1 || y0 === y1) && (
73445 textPosition === 'auto' ||
73446 textPosition === 'inside'))) {
73447 bar.select('text').remove();
73448 return;
73449 }
73450
73451 var layoutFont = fullLayout.font;
73452 var barColor = style.getBarColor(cd[i], trace);
73453 var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
73454 var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);
73455
73456 // Special case: don't use the c2p(v, true) value on log size axes,
73457 // so that we can get correctly inside text scaling
73458 var di = bar.datum();
73459 if(isHorizontal) {
73460 if(xa.type === 'log' && di.s0 <= 0) {
73461 if(xa.range[0] < xa.range[1]) {
73462 x0 = 0;
73463 } else {
73464 x0 = xa._length;
73465 }
73466 }
73467 } else {
73468 if(ya.type === 'log' && di.s0 <= 0) {
73469 if(ya.range[0] < ya.range[1]) {
73470 y0 = ya._length;
73471 } else {
73472 y0 = 0;
73473 }
73474 }
73475 }
73476
73477 // padding excluded
73478 var barWidth = Math.abs(x1 - x0) - 2 * TEXTPAD;
73479 var barHeight = Math.abs(y1 - y0) - 2 * TEXTPAD;
73480
73481 var textSelection;
73482 var textBB;
73483 var textWidth;
73484 var textHeight;
73485 var font;
73486
73487 if(textPosition === 'outside') {
73488 if(!isOutmostBar && !calcBar.hasB) textPosition = 'inside';
73489 }
73490
73491 if(textPosition === 'auto') {
73492 if(isOutmostBar) {
73493 // draw text using insideTextFont and check if it fits inside bar
73494 textPosition = 'inside';
73495
73496 font = Lib.ensureUniformFontSize(gd, insideTextFont);
73497
73498 textSelection = appendTextNode(bar, text, font);
73499
73500 textBB = Drawing.bBox(textSelection.node()),
73501 textWidth = textBB.width,
73502 textHeight = textBB.height;
73503
73504 var textHasSize = (textWidth > 0 && textHeight > 0);
73505 var fitsInside = (textWidth <= barWidth && textHeight <= barHeight);
73506 var fitsInsideIfRotated = (textWidth <= barHeight && textHeight <= barWidth);
73507 var fitsInsideIfShrunk = (isHorizontal) ?
73508 (barWidth >= textWidth * (barHeight / textHeight)) :
73509 (barHeight >= textHeight * (barWidth / textWidth));
73510
73511 if(textHasSize && (
73512 fitsInside ||
73513 fitsInsideIfRotated ||
73514 fitsInsideIfShrunk)
73515 ) {
73516 textPosition = 'inside';
73517 } else {
73518 textPosition = 'outside';
73519 textSelection.remove();
73520 textSelection = null;
73521 }
73522 } else {
73523 textPosition = 'inside';
73524 }
73525 }
73526
73527 if(!textSelection) {
73528 font = Lib.ensureUniformFontSize(gd, (textPosition === 'outside') ? outsideTextFont : insideTextFont);
73529
73530 textSelection = appendTextNode(bar, text, font);
73531
73532 var currentTransform = textSelection.attr('transform');
73533 textSelection.attr('transform', '');
73534 textBB = Drawing.bBox(textSelection.node()),
73535 textWidth = textBB.width,
73536 textHeight = textBB.height;
73537 textSelection.attr('transform', currentTransform);
73538
73539 if(textWidth <= 0 || textHeight <= 0) {
73540 textSelection.remove();
73541 return;
73542 }
73543 }
73544
73545 var angle = trace.textangle;
73546
73547 // compute text transform
73548 var transform, constrained;
73549 if(textPosition === 'outside') {
73550 constrained =
73551 trace.constraintext === 'both' ||
73552 trace.constraintext === 'outside';
73553
73554 transform = toMoveOutsideBar(x0, x1, y0, y1, textBB, {
73555 isHorizontal: isHorizontal,
73556 constrained: constrained,
73557 angle: angle
73558 });
73559 } else {
73560 constrained =
73561 trace.constraintext === 'both' ||
73562 trace.constraintext === 'inside';
73563
73564 transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
73565 isHorizontal: isHorizontal,
73566 constrained: constrained,
73567 angle: angle,
73568 anchor: trace.insidetextanchor
73569 });
73570 }
73571
73572 transform.fontSize = font.size;
73573 recordMinTextSize(trace.type, transform, fullLayout);
73574 calcBar.transform = transform;
73575
73576 transition(textSelection, fullLayout, opts, makeOnCompleteCallback)
73577 .attr('transform', Lib.getTextTransform(transform));
73578}
73579
73580function getRotateFromAngle(angle) {
73581 return (angle === 'auto') ? 0 : angle;
73582}
73583
73584function getRotatedTextSize(textBB, rotate) {
73585 var a = Math.PI / 180 * rotate;
73586 var absSin = Math.abs(Math.sin(a));
73587 var absCos = Math.abs(Math.cos(a));
73588
73589 return {
73590 x: textBB.width * absCos + textBB.height * absSin,
73591 y: textBB.width * absSin + textBB.height * absCos
73592 };
73593}
73594
73595function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) {
73596 var isHorizontal = !!opts.isHorizontal;
73597 var constrained = !!opts.constrained;
73598 var angle = opts.angle || 0;
73599 var anchor = opts.anchor || 'end';
73600 var isEnd = anchor === 'end';
73601 var isStart = anchor === 'start';
73602 var leftToRight = opts.leftToRight || 0; // left: -1, center: 0, right: 1
73603 var toRight = (leftToRight + 1) / 2;
73604 var toLeft = 1 - toRight;
73605
73606 var textWidth = textBB.width;
73607 var textHeight = textBB.height;
73608 var lx = Math.abs(x1 - x0);
73609 var ly = Math.abs(y1 - y0);
73610
73611 // compute remaining space
73612 var textpad = (
73613 lx > (2 * TEXTPAD) &&
73614 ly > (2 * TEXTPAD)
73615 ) ? TEXTPAD : 0;
73616
73617 lx -= 2 * textpad;
73618 ly -= 2 * textpad;
73619
73620 var rotate = getRotateFromAngle(angle);
73621 if((angle === 'auto') &&
73622 !(textWidth <= lx && textHeight <= ly) &&
73623 (textWidth > lx || textHeight > ly) && (
73624 !(textWidth > ly || textHeight > lx) ||
73625 ((textWidth < textHeight) !== (lx < ly))
73626 )) {
73627 rotate += 90;
73628 }
73629
73630 var t = getRotatedTextSize(textBB, rotate);
73631
73632 var scale = 1;
73633 if(constrained) {
73634 scale = Math.min(
73635 1,
73636 lx / t.x,
73637 ly / t.y
73638 );
73639 }
73640
73641 // compute text and target positions
73642 var textX = (
73643 textBB.left * toLeft +
73644 textBB.right * toRight
73645 );
73646 var textY = (textBB.top + textBB.bottom) / 2;
73647 var targetX = (
73648 (x0 + TEXTPAD) * toLeft +
73649 (x1 - TEXTPAD) * toRight
73650 );
73651 var targetY = (y0 + y1) / 2;
73652 var anchorX = 0;
73653 var anchorY = 0;
73654 if(isStart || isEnd) {
73655 var extrapad = (isHorizontal ? t.x : t.y) / 2;
73656 var dir = isHorizontal ? dirSign(x0, x1) : dirSign(y0, y1);
73657
73658 if(isHorizontal) {
73659 if(isStart) {
73660 targetX = x0 + dir * textpad;
73661 anchorX = -dir * extrapad;
73662 } else {
73663 targetX = x1 - dir * textpad;
73664 anchorX = dir * extrapad;
73665 }
73666 } else {
73667 if(isStart) {
73668 targetY = y0 + dir * textpad;
73669 anchorY = -dir * extrapad;
73670 } else {
73671 targetY = y1 - dir * textpad;
73672 anchorY = dir * extrapad;
73673 }
73674 }
73675 }
73676
73677 return {
73678 textX: textX,
73679 textY: textY,
73680 targetX: targetX,
73681 targetY: targetY,
73682 anchorX: anchorX,
73683 anchorY: anchorY,
73684 scale: scale,
73685 rotate: rotate
73686 };
73687}
73688
73689function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) {
73690 var isHorizontal = !!opts.isHorizontal;
73691 var constrained = !!opts.constrained;
73692 var angle = opts.angle || 0;
73693
73694 var textWidth = textBB.width;
73695 var textHeight = textBB.height;
73696 var lx = Math.abs(x1 - x0);
73697 var ly = Math.abs(y1 - y0);
73698
73699 var textpad;
73700 // Keep the padding so the text doesn't sit right against
73701 // the bars, but don't factor it into barWidth
73702 if(isHorizontal) {
73703 textpad = (ly > 2 * TEXTPAD) ? TEXTPAD : 0;
73704 } else {
73705 textpad = (lx > 2 * TEXTPAD) ? TEXTPAD : 0;
73706 }
73707
73708 // compute rotate and scale
73709 var scale = 1;
73710 if(constrained) {
73711 scale = (isHorizontal) ?
73712 Math.min(1, ly / textHeight) :
73713 Math.min(1, lx / textWidth);
73714 }
73715
73716 var rotate = getRotateFromAngle(angle);
73717 var t = getRotatedTextSize(textBB, rotate);
73718
73719 // compute text and target positions
73720 var extrapad = (isHorizontal ? t.x : t.y) / 2;
73721 var textX = (textBB.left + textBB.right) / 2;
73722 var textY = (textBB.top + textBB.bottom) / 2;
73723 var targetX = (x0 + x1) / 2;
73724 var targetY = (y0 + y1) / 2;
73725 var anchorX = 0;
73726 var anchorY = 0;
73727
73728 var dir = isHorizontal ? dirSign(x1, x0) : dirSign(y0, y1);
73729 if(isHorizontal) {
73730 targetX = x1 - dir * textpad;
73731 anchorX = dir * extrapad;
73732 } else {
73733 targetY = y1 + dir * textpad;
73734 anchorY = -dir * extrapad;
73735 }
73736
73737 return {
73738 textX: textX,
73739 textY: textY,
73740 targetX: targetX,
73741 targetY: targetY,
73742 anchorX: anchorX,
73743 anchorY: anchorY,
73744 scale: scale,
73745 rotate: rotate
73746 };
73747}
73748
73749function getText(fullLayout, cd, index, xa, ya) {
73750 var trace = cd[0].trace;
73751 var texttemplate = trace.texttemplate;
73752
73753 var value;
73754 if(texttemplate) {
73755 value = calcTexttemplate(fullLayout, cd, index, xa, ya);
73756 } else if(trace.textinfo) {
73757 value = calcTextinfo(cd, index, xa, ya);
73758 } else {
73759 value = helpers.getValue(trace.text, index);
73760 }
73761
73762 return helpers.coerceString(attributeText, value);
73763}
73764
73765function getTextPosition(trace, index) {
73766 var value = helpers.getValue(trace.textposition, index);
73767 return helpers.coerceEnumerated(attributeTextPosition, value);
73768}
73769
73770function calcTexttemplate(fullLayout, cd, index, xa, ya) {
73771 var trace = cd[0].trace;
73772 var texttemplate = Lib.castOption(trace, index, 'texttemplate');
73773 if(!texttemplate) return '';
73774 var isWaterfall = (trace.type === 'waterfall');
73775 var isFunnel = (trace.type === 'funnel');
73776
73777 var pLetter, pAxis;
73778 var vLetter, vAxis;
73779 if(trace.orientation === 'h') {
73780 pLetter = 'y';
73781 pAxis = ya;
73782 vLetter = 'x';
73783 vAxis = xa;
73784 } else {
73785 pLetter = 'x';
73786 pAxis = xa;
73787 vLetter = 'y';
73788 vAxis = ya;
73789 }
73790
73791 function formatLabel(u) {
73792 return tickText(pAxis, u, true).text;
73793 }
73794
73795 function formatNumber(v) {
73796 return tickText(vAxis, +v, true).text;
73797 }
73798
73799 var cdi = cd[index];
73800 var obj = {};
73801
73802 obj.label = cdi.p;
73803 obj.labelLabel = obj[pLetter + 'Label'] = formatLabel(cdi.p);
73804
73805 var tx = Lib.castOption(trace, cdi.i, 'text');
73806 if(tx === 0 || tx) obj.text = tx;
73807
73808 obj.value = cdi.s;
73809 obj.valueLabel = obj[vLetter + 'Label'] = formatNumber(cdi.s);
73810
73811 var pt = {};
73812 appendArrayPointValue(pt, trace, cdi.i);
73813
73814 if(isWaterfall) {
73815 obj.delta = +cdi.rawS || cdi.s;
73816 obj.deltaLabel = formatNumber(obj.delta);
73817 obj.final = cdi.v;
73818 obj.finalLabel = formatNumber(obj.final);
73819 obj.initial = obj.final - obj.delta;
73820 obj.initialLabel = formatNumber(obj.initial);
73821 }
73822
73823 if(isFunnel) {
73824 obj.value = cdi.s;
73825 obj.valueLabel = formatNumber(obj.value);
73826
73827 obj.percentInitial = cdi.begR;
73828 obj.percentInitialLabel = Lib.formatPercent(cdi.begR);
73829 obj.percentPrevious = cdi.difR;
73830 obj.percentPreviousLabel = Lib.formatPercent(cdi.difR);
73831 obj.percentTotal = cdi.sumR;
73832 obj.percenTotalLabel = Lib.formatPercent(cdi.sumR);
73833 }
73834
73835 var customdata = Lib.castOption(trace, cdi.i, 'customdata');
73836 if(customdata) obj.customdata = customdata;
73837 return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, pt, obj, trace._meta || {});
73838}
73839
73840function calcTextinfo(cd, index, xa, ya) {
73841 var trace = cd[0].trace;
73842 var isHorizontal = (trace.orientation === 'h');
73843 var isWaterfall = (trace.type === 'waterfall');
73844 var isFunnel = (trace.type === 'funnel');
73845
73846 function formatLabel(u) {
73847 var pAxis = isHorizontal ? ya : xa;
73848 return tickText(pAxis, u, true).text;
73849 }
73850
73851 function formatNumber(v) {
73852 var sAxis = isHorizontal ? xa : ya;
73853 return tickText(sAxis, +v, true).text;
73854 }
73855
73856 var textinfo = trace.textinfo;
73857 var cdi = cd[index];
73858
73859 var parts = textinfo.split('+');
73860 var text = [];
73861 var tx;
73862
73863 var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
73864
73865 if(hasFlag('label')) {
73866 text.push(formatLabel(cd[index].p));
73867 }
73868
73869 if(hasFlag('text')) {
73870 tx = Lib.castOption(trace, cdi.i, 'text');
73871 if(tx === 0 || tx) text.push(tx);
73872 }
73873
73874 if(isWaterfall) {
73875 var delta = +cdi.rawS || cdi.s;
73876 var final = cdi.v;
73877 var initial = final - delta;
73878
73879 if(hasFlag('initial')) text.push(formatNumber(initial));
73880 if(hasFlag('delta')) text.push(formatNumber(delta));
73881 if(hasFlag('final')) text.push(formatNumber(final));
73882 }
73883
73884 if(isFunnel) {
73885 if(hasFlag('value')) text.push(formatNumber(cdi.s));
73886
73887 var nPercent = 0;
73888 if(hasFlag('percent initial')) nPercent++;
73889 if(hasFlag('percent previous')) nPercent++;
73890 if(hasFlag('percent total')) nPercent++;
73891
73892 var hasMultiplePercents = nPercent > 1;
73893
73894 if(hasFlag('percent initial')) {
73895 tx = Lib.formatPercent(cdi.begR);
73896 if(hasMultiplePercents) tx += ' of initial';
73897 text.push(tx);
73898 }
73899 if(hasFlag('percent previous')) {
73900 tx = Lib.formatPercent(cdi.difR);
73901 if(hasMultiplePercents) tx += ' of previous';
73902 text.push(tx);
73903 }
73904 if(hasFlag('percent total')) {
73905 tx = Lib.formatPercent(cdi.sumR);
73906 if(hasMultiplePercents) tx += ' of total';
73907 text.push(tx);
73908 }
73909 }
73910
73911 return text.join('<br>');
73912}
73913
73914module.exports = {
73915 plot: plot,
73916 toMoveInsideBar: toMoveInsideBar
73917};
73918
73919},{"../../components/color":52,"../../components/drawing":74,"../../components/fx/helpers":88,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/cartesian/axes":222,"../../registry":269,"./attributes":279,"./constants":281,"./helpers":285,"./style":293,"./uniform_text":295,"d3":16,"fast-isnumeric":18}],291:[function(_dereq_,module,exports){
73920/**
73921* Copyright 2012-2020, Plotly, Inc.
73922* All rights reserved.
73923*
73924* This source code is licensed under the MIT license found in the
73925* LICENSE file in the root directory of this source tree.
73926*/
73927
73928'use strict';
73929
73930module.exports = function selectPoints(searchInfo, selectionTester) {
73931 var cd = searchInfo.cd;
73932 var xa = searchInfo.xaxis;
73933 var ya = searchInfo.yaxis;
73934 var trace = cd[0].trace;
73935 var isFunnel = (trace.type === 'funnel');
73936 var isHorizontal = (trace.orientation === 'h');
73937 var selection = [];
73938 var i;
73939
73940 if(selectionTester === false) {
73941 // clear selection
73942 for(i = 0; i < cd.length; i++) {
73943 cd[i].selected = 0;
73944 }
73945 } else {
73946 for(i = 0; i < cd.length; i++) {
73947 var di = cd[i];
73948 var ct = 'ct' in di ? di.ct : getCentroid(di, xa, ya, isHorizontal, isFunnel);
73949
73950 if(selectionTester.contains(ct, false, i, searchInfo)) {
73951 selection.push({
73952 pointNumber: i,
73953 x: xa.c2d(di.x),
73954 y: ya.c2d(di.y)
73955 });
73956 di.selected = 1;
73957 } else {
73958 di.selected = 0;
73959 }
73960 }
73961 }
73962
73963 return selection;
73964};
73965
73966function getCentroid(d, xa, ya, isHorizontal, isFunnel) {
73967 var x0 = xa.c2p(isHorizontal ? d.s0 : d.p0, true);
73968 var x1 = xa.c2p(isHorizontal ? d.s1 : d.p1, true);
73969 var y0 = ya.c2p(isHorizontal ? d.p0 : d.s0, true);
73970 var y1 = ya.c2p(isHorizontal ? d.p1 : d.s1, true);
73971
73972 if(isFunnel) {
73973 return [(x0 + x1) / 2, (y0 + y1) / 2];
73974 } else {
73975 if(isHorizontal) {
73976 return [x1, (y0 + y1) / 2];
73977 } else {
73978 return [(x0 + x1) / 2, y1];
73979 }
73980 }
73981}
73982
73983},{}],292:[function(_dereq_,module,exports){
73984/**
73985* Copyright 2012-2020, Plotly, Inc.
73986* All rights reserved.
73987*
73988* This source code is licensed under the MIT license found in the
73989* LICENSE file in the root directory of this source tree.
73990*/
73991
73992'use strict';
73993
73994module.exports = Sieve;
73995
73996var distinctVals = _dereq_('../../lib').distinctVals;
73997var BADNUM = _dereq_('../../constants/numerical').BADNUM;
73998
73999/**
74000 * Helper class to sieve data from traces into bins
74001 *
74002 * @class
74003 *
74004 * @param {Array} traces
74005* Array of calculated traces
74006 * @param {object} opts
74007 * - @param {boolean} [sepNegVal]
74008 * If true, then split data at the same position into a bar
74009 * for positive values and another for negative values
74010 * - @param {boolean} [overlapNoMerge]
74011 * If true, then don't merge overlapping bars into a single bar
74012 */
74013function Sieve(traces, opts) {
74014 this.traces = traces;
74015 this.sepNegVal = opts.sepNegVal;
74016 this.overlapNoMerge = opts.overlapNoMerge;
74017
74018 // for single-bin histograms - see histogram/calc
74019 var width1 = Infinity;
74020
74021 var positions = [];
74022 for(var i = 0; i < traces.length; i++) {
74023 var trace = traces[i];
74024 for(var j = 0; j < trace.length; j++) {
74025 var bar = trace[j];
74026 if(bar.p !== BADNUM) positions.push(bar.p);
74027 }
74028 if(trace[0] && trace[0].width1) {
74029 width1 = Math.min(trace[0].width1, width1);
74030 }
74031 }
74032 this.positions = positions;
74033
74034 var dv = distinctVals(positions);
74035 this.distinctPositions = dv.vals;
74036 if(dv.vals.length === 1 && width1 !== Infinity) this.minDiff = width1;
74037 else this.minDiff = Math.min(dv.minDiff, width1);
74038
74039 this.binWidth = this.minDiff;
74040
74041 this.bins = {};
74042}
74043
74044/**
74045 * Sieve datum
74046 *
74047 * @method
74048 * @param {number} position
74049 * @param {number} value
74050 * @returns {number} Previous bin value
74051 */
74052Sieve.prototype.put = function put(position, value) {
74053 var label = this.getLabel(position, value);
74054 var oldValue = this.bins[label] || 0;
74055
74056 this.bins[label] = oldValue + value;
74057
74058 return oldValue;
74059};
74060
74061/**
74062 * Get current bin value for a given datum
74063 *
74064 * @method
74065 * @param {number} position Position of datum
74066 * @param {number} [value] Value of datum
74067 * (required if this.sepNegVal is true)
74068 * @returns {number} Current bin value
74069 */
74070Sieve.prototype.get = function get(position, value) {
74071 var label = this.getLabel(position, value);
74072 return this.bins[label] || 0;
74073};
74074
74075/**
74076 * Get bin label for a given datum
74077 *
74078 * @method
74079 * @param {number} position Position of datum
74080 * @param {number} [value] Value of datum
74081 * (required if this.sepNegVal is true)
74082 * @returns {string} Bin label
74083 * (prefixed with a 'v' if value is negative and this.sepNegVal is
74084 * true; otherwise prefixed with '^')
74085 */
74086Sieve.prototype.getLabel = function getLabel(position, value) {
74087 var prefix = (value < 0 && this.sepNegVal) ? 'v' : '^';
74088 var label = (this.overlapNoMerge) ?
74089 position :
74090 Math.round(position / this.binWidth);
74091 return prefix + label;
74092};
74093
74094},{"../../constants/numerical":158,"../../lib":178}],293:[function(_dereq_,module,exports){
74095/**
74096* Copyright 2012-2020, Plotly, Inc.
74097* All rights reserved.
74098*
74099* This source code is licensed under the MIT license found in the
74100* LICENSE file in the root directory of this source tree.
74101*/
74102
74103'use strict';
74104
74105var d3 = _dereq_('d3');
74106var Color = _dereq_('../../components/color');
74107var Drawing = _dereq_('../../components/drawing');
74108var Lib = _dereq_('../../lib');
74109var Registry = _dereq_('../../registry');
74110
74111var resizeText = _dereq_('./uniform_text').resizeText;
74112var attributes = _dereq_('./attributes');
74113var attributeTextFont = attributes.textfont;
74114var attributeInsideTextFont = attributes.insidetextfont;
74115var attributeOutsideTextFont = attributes.outsidetextfont;
74116var helpers = _dereq_('./helpers');
74117
74118function style(gd) {
74119 var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace');
74120 resizeText(gd, s, 'bar');
74121
74122 var barcount = s.size();
74123 var fullLayout = gd._fullLayout;
74124
74125 // trace styling
74126 s.style('opacity', function(d) { return d[0].trace.opacity; })
74127
74128 // for gapless (either stacked or neighboring grouped) bars use
74129 // crispEdges to turn off antialiasing so an artificial gap
74130 // isn't introduced.
74131 .each(function(d) {
74132 if((fullLayout.barmode === 'stack' && barcount > 1) ||
74133 (fullLayout.bargap === 0 &&
74134 fullLayout.bargroupgap === 0 &&
74135 !d[0].trace.marker.line.width)) {
74136 d3.select(this).attr('shape-rendering', 'crispEdges');
74137 }
74138 });
74139
74140 s.selectAll('g.points').each(function(d) {
74141 var sel = d3.select(this);
74142 var trace = d[0].trace;
74143 stylePoints(sel, trace, gd);
74144 });
74145
74146 Registry.getComponentMethod('errorbars', 'style')(s);
74147}
74148
74149function stylePoints(sel, trace, gd) {
74150 Drawing.pointStyle(sel.selectAll('path'), trace, gd);
74151 styleTextPoints(sel, trace, gd);
74152}
74153
74154function styleTextPoints(sel, trace, gd) {
74155 sel.selectAll('text').each(function(d) {
74156 var tx = d3.select(this);
74157 var font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
74158
74159 Drawing.font(tx, font);
74160 });
74161}
74162
74163function styleOnSelect(gd, cd, sel) {
74164 var trace = cd[0].trace;
74165
74166 if(trace.selectedpoints) {
74167 stylePointsInSelectionMode(sel, trace, gd);
74168 } else {
74169 stylePoints(sel, trace, gd);
74170 Registry.getComponentMethod('errorbars', 'style')(sel);
74171 }
74172}
74173
74174function stylePointsInSelectionMode(s, trace, gd) {
74175 Drawing.selectedPointStyle(s.selectAll('path'), trace);
74176 styleTextInSelectionMode(s.selectAll('text'), trace, gd);
74177}
74178
74179function styleTextInSelectionMode(txs, trace, gd) {
74180 txs.each(function(d) {
74181 var tx = d3.select(this);
74182 var font;
74183
74184 if(d.selected) {
74185 font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
74186
74187 var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
74188 if(selectedFontColor) {
74189 font.color = selectedFontColor;
74190 }
74191
74192 Drawing.font(tx, font);
74193 } else {
74194 Drawing.selectedTextStyle(tx, trace);
74195 }
74196 });
74197}
74198
74199function determineFont(tx, d, trace, gd) {
74200 var layoutFont = gd._fullLayout.font;
74201 var textFont = trace.textfont;
74202
74203 if(tx.classed('bartext-inside')) {
74204 var barColor = getBarColor(d, trace);
74205 textFont = getInsideTextFont(trace, d.i, layoutFont, barColor);
74206 } else if(tx.classed('bartext-outside')) {
74207 textFont = getOutsideTextFont(trace, d.i, layoutFont);
74208 }
74209
74210 return textFont;
74211}
74212
74213function getTextFont(trace, index, defaultValue) {
74214 return getFontValue(
74215 attributeTextFont, trace.textfont, index, defaultValue);
74216}
74217
74218function getInsideTextFont(trace, index, layoutFont, barColor) {
74219 var defaultFont = getTextFont(trace, index, layoutFont);
74220
74221 var wouldFallBackToLayoutFont =
74222 (trace._input.textfont === undefined || trace._input.textfont.color === undefined) ||
74223 (Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined);
74224 if(wouldFallBackToLayoutFont) {
74225 defaultFont = {
74226 color: Color.contrast(barColor),
74227 family: defaultFont.family,
74228 size: defaultFont.size
74229 };
74230 }
74231
74232 return getFontValue(
74233 attributeInsideTextFont, trace.insidetextfont, index, defaultFont);
74234}
74235
74236function getOutsideTextFont(trace, index, layoutFont) {
74237 var defaultFont = getTextFont(trace, index, layoutFont);
74238 return getFontValue(
74239 attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont);
74240}
74241
74242function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
74243 attributeValue = attributeValue || {};
74244
74245 var familyValue = helpers.getValue(attributeValue.family, index);
74246 var sizeValue = helpers.getValue(attributeValue.size, index);
74247 var colorValue = helpers.getValue(attributeValue.color, index);
74248
74249 return {
74250 family: helpers.coerceString(
74251 attributeDefinition.family, familyValue, defaultValue.family),
74252 size: helpers.coerceNumber(
74253 attributeDefinition.size, sizeValue, defaultValue.size),
74254 color: helpers.coerceColor(
74255 attributeDefinition.color, colorValue, defaultValue.color)
74256 };
74257}
74258
74259function getBarColor(cd, trace) {
74260 if(trace.type === 'waterfall') {
74261 return trace[cd.dir].marker.color;
74262 }
74263 return cd.mc || trace.marker.color;
74264}
74265
74266module.exports = {
74267 style: style,
74268 styleTextPoints: styleTextPoints,
74269 styleOnSelect: styleOnSelect,
74270 getInsideTextFont: getInsideTextFont,
74271 getOutsideTextFont: getOutsideTextFont,
74272 getBarColor: getBarColor,
74273 resizeText: resizeText
74274};
74275
74276},{"../../components/color":52,"../../components/drawing":74,"../../lib":178,"../../registry":269,"./attributes":279,"./helpers":285,"./uniform_text":295,"d3":16}],294:[function(_dereq_,module,exports){
74277/**
74278* Copyright 2012-2020, Plotly, Inc.
74279* All rights reserved.
74280*
74281* This source code is licensed under the MIT license found in the
74282* LICENSE file in the root directory of this source tree.
74283*/
74284
74285'use strict';
74286
74287var Color = _dereq_('../../components/color');
74288var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
74289var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
74290
74291module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) {
74292 coerce('marker.color', defaultColor);
74293
74294 if(hasColorscale(traceIn, 'marker')) {
74295 colorscaleDefaults(
74296 traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}
74297 );
74298 }
74299
74300 coerce('marker.line.color', Color.defaultLine);
74301
74302 if(hasColorscale(traceIn, 'marker.line')) {
74303 colorscaleDefaults(
74304 traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'}
74305 );
74306 }
74307
74308 coerce('marker.line.width');
74309 coerce('marker.opacity');
74310 coerce('selected.marker.color');
74311 coerce('unselected.marker.color');
74312};
74313
74314},{"../../components/color":52,"../../components/colorscale/defaults":62,"../../components/colorscale/helpers":63}],295:[function(_dereq_,module,exports){
74315/**
74316* Copyright 2012-2020, Plotly, Inc.
74317* All rights reserved.
74318*
74319* This source code is licensed under the MIT license found in the
74320* LICENSE file in the root directory of this source tree.
74321*/
74322
74323'use strict';
74324
74325var d3 = _dereq_('d3');
74326var Lib = _dereq_('../../lib');
74327
74328function resizeText(gd, gTrace, traceType) {
74329 var fullLayout = gd._fullLayout;
74330 var minSize = fullLayout['_' + traceType + 'Text_minsize'];
74331 if(minSize) {
74332 var shouldHide = fullLayout.uniformtext.mode === 'hide';
74333
74334 var selector;
74335 switch(traceType) {
74336 case 'funnelarea' :
74337 case 'pie' :
74338 case 'sunburst' :
74339 selector = 'g.slice';
74340 break;
74341 case 'treemap' :
74342 selector = 'g.slice, g.pathbar';
74343 break;
74344 default :
74345 selector = 'g.points > g.point';
74346 }
74347
74348 gTrace.selectAll(selector).each(function(d) {
74349 var transform = d.transform;
74350 if(transform) {
74351 transform.scale = (shouldHide && transform.hide) ? 0 : minSize / transform.fontSize;
74352
74353 var el = d3.select(this).select('text');
74354 el.attr('transform', Lib.getTextTransform(transform));
74355 }
74356 });
74357 }
74358}
74359
74360function recordMinTextSize(
74361 traceType, // in
74362 transform, // inout
74363 fullLayout // inout
74364) {
74365 if(fullLayout.uniformtext.mode) {
74366 var minKey = getMinKey(traceType);
74367 var minSize = fullLayout.uniformtext.minsize;
74368 var size = transform.scale * transform.fontSize;
74369
74370 transform.hide = size < minSize;
74371
74372 fullLayout[minKey] = fullLayout[minKey] || Infinity;
74373 if(!transform.hide) {
74374 fullLayout[minKey] = Math.min(
74375 fullLayout[minKey],
74376 Math.max(size, minSize)
74377 );
74378 }
74379 }
74380}
74381
74382function clearMinTextSize(
74383 traceType, // in
74384 fullLayout // inout
74385) {
74386 var minKey = getMinKey(traceType);
74387 fullLayout[minKey] = undefined;
74388}
74389
74390function getMinKey(traceType) {
74391 return '_' + traceType + 'Text_minsize';
74392}
74393
74394module.exports = {
74395 recordMinTextSize: recordMinTextSize,
74396 clearMinTextSize: clearMinTextSize,
74397 resizeText: resizeText
74398};
74399
74400},{"../../lib":178,"d3":16}],296:[function(_dereq_,module,exports){
74401/**
74402* Copyright 2012-2020, Plotly, Inc.
74403* All rights reserved.
74404*
74405* This source code is licensed under the MIT license found in the
74406* LICENSE file in the root directory of this source tree.
74407*/
74408
74409'use strict';
74410
74411var scatterAttrs = _dereq_('../scatter/attributes');
74412var barAttrs = _dereq_('../bar/attributes');
74413var colorAttrs = _dereq_('../../components/color/attributes');
74414var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
74415var extendFlat = _dereq_('../../lib/extend').extendFlat;
74416
74417var scatterMarkerAttrs = scatterAttrs.marker;
74418var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
74419
74420module.exports = {
74421 y: {
74422 valType: 'data_array',
74423 editType: 'calc+clearAxisTypes',
74424
74425 },
74426 x: {
74427 valType: 'data_array',
74428 editType: 'calc+clearAxisTypes',
74429
74430 },
74431 x0: {
74432 valType: 'any',
74433
74434 editType: 'calc+clearAxisTypes',
74435
74436 },
74437 y0: {
74438 valType: 'any',
74439
74440 editType: 'calc+clearAxisTypes',
74441
74442 },
74443
74444 dx: {
74445 valType: 'number',
74446
74447 editType: 'calc',
74448
74449 },
74450 dy: {
74451 valType: 'number',
74452
74453 editType: 'calc',
74454
74455 },
74456
74457 name: {
74458 valType: 'string',
74459
74460 editType: 'calc+clearAxisTypes',
74461
74462 },
74463
74464 q1: {
74465 valType: 'data_array',
74466
74467 editType: 'calc+clearAxisTypes',
74468
74469 },
74470 median: {
74471 valType: 'data_array',
74472
74473 editType: 'calc+clearAxisTypes',
74474
74475 },
74476 q3: {
74477 valType: 'data_array',
74478
74479 editType: 'calc+clearAxisTypes',
74480
74481 },
74482 lowerfence: {
74483 valType: 'data_array',
74484
74485 editType: 'calc',
74486
74487 },
74488 upperfence: {
74489 valType: 'data_array',
74490
74491 editType: 'calc',
74492
74493 },
74494
74495 notched: {
74496 valType: 'boolean',
74497
74498 editType: 'calc',
74499
74500 },
74501 notchwidth: {
74502 valType: 'number',
74503 min: 0,
74504 max: 0.5,
74505 dflt: 0.25,
74506
74507 editType: 'calc',
74508
74509 },
74510 notchspan: {
74511 valType: 'data_array',
74512
74513 editType: 'calc',
74514
74515 },
74516
74517 // TODO
74518 // maybe add
74519 // - loweroutlierbound / upperoutlierbound
74520 // - lowersuspectedoutlierbound / uppersuspectedoutlierbound
74521
74522 boxpoints: {
74523 valType: 'enumerated',
74524 values: ['all', 'outliers', 'suspectedoutliers', false],
74525
74526 editType: 'calc',
74527
74528 },
74529 jitter: {
74530 valType: 'number',
74531 min: 0,
74532 max: 1,
74533
74534 editType: 'calc',
74535
74536 },
74537 pointpos: {
74538 valType: 'number',
74539 min: -2,
74540 max: 2,
74541
74542 editType: 'calc',
74543
74544 },
74545
74546 boxmean: {
74547 valType: 'enumerated',
74548 values: [true, 'sd', false],
74549
74550 editType: 'calc',
74551
74552 },
74553 mean: {
74554 valType: 'data_array',
74555
74556 editType: 'calc',
74557
74558 },
74559 sd: {
74560 valType: 'data_array',
74561
74562 editType: 'calc',
74563
74564 },
74565
74566 orientation: {
74567 valType: 'enumerated',
74568 values: ['v', 'h'],
74569
74570 editType: 'calc+clearAxisTypes',
74571
74572 },
74573
74574 quartilemethod: {
74575 valType: 'enumerated',
74576 values: ['linear', 'exclusive', 'inclusive'],
74577 dflt: 'linear',
74578
74579 editType: 'calc',
74580
74581 },
74582
74583 width: {
74584 valType: 'number',
74585 min: 0,
74586
74587 dflt: 0,
74588 editType: 'calc',
74589
74590 },
74591
74592 marker: {
74593 outliercolor: {
74594 valType: 'color',
74595 dflt: 'rgba(0, 0, 0, 0)',
74596
74597 editType: 'style',
74598
74599 },
74600 symbol: extendFlat({}, scatterMarkerAttrs.symbol,
74601 {arrayOk: false, editType: 'plot'}),
74602 opacity: extendFlat({}, scatterMarkerAttrs.opacity,
74603 {arrayOk: false, dflt: 1, editType: 'style'}),
74604 size: extendFlat({}, scatterMarkerAttrs.size,
74605 {arrayOk: false, editType: 'calc'}),
74606 color: extendFlat({}, scatterMarkerAttrs.color,
74607 {arrayOk: false, editType: 'style'}),
74608 line: {
74609 color: extendFlat({}, scatterMarkerLineAttrs.color,
74610 {arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'}
74611 ),
74612 width: extendFlat({}, scatterMarkerLineAttrs.width,
74613 {arrayOk: false, dflt: 0, editType: 'style'}
74614 ),
74615 outliercolor: {
74616 valType: 'color',
74617
74618 editType: 'style',
74619
74620 },
74621 outlierwidth: {
74622 valType: 'number',
74623 min: 0,
74624 dflt: 1,
74625
74626 editType: 'style',
74627
74628 },
74629 editType: 'style'
74630 },
74631 editType: 'plot'
74632 },
74633
74634 line: {
74635 color: {
74636 valType: 'color',
74637
74638 editType: 'style',
74639
74640 },
74641 width: {
74642 valType: 'number',
74643
74644 min: 0,
74645 dflt: 2,
74646 editType: 'style',
74647
74648 },
74649 editType: 'plot'
74650 },
74651
74652 fillcolor: scatterAttrs.fillcolor,
74653
74654 whiskerwidth: {
74655 valType: 'number',
74656 min: 0,
74657 max: 1,
74658 dflt: 0.5,
74659
74660 editType: 'calc',
74661
74662 },
74663
74664 offsetgroup: barAttrs.offsetgroup,
74665 alignmentgroup: barAttrs.alignmentgroup,
74666
74667 selected: {
74668 marker: scatterAttrs.selected.marker,
74669 editType: 'style'
74670 },
74671 unselected: {
74672 marker: scatterAttrs.unselected.marker,
74673 editType: 'style'
74674 },
74675
74676 text: extendFlat({}, scatterAttrs.text, {
74677
74678 }),
74679 hovertext: extendFlat({}, scatterAttrs.hovertext, {
74680
74681 }),
74682 hovertemplate: hovertemplateAttrs({
74683
74684 }),
74685
74686 hoveron: {
74687 valType: 'flaglist',
74688 flags: ['boxes', 'points'],
74689 dflt: 'boxes+points',
74690
74691 editType: 'style',
74692
74693 }
74694};
74695
74696},{"../../components/color/attributes":51,"../../lib/extend":173,"../../plots/template_attributes":264,"../bar/attributes":279,"../scatter/attributes":389}],297:[function(_dereq_,module,exports){
74697/**
74698* Copyright 2012-2020, Plotly, Inc.
74699* All rights reserved.
74700*
74701* This source code is licensed under the MIT license found in the
74702* LICENSE file in the root directory of this source tree.
74703*/
74704
74705'use strict';
74706
74707var isNumeric = _dereq_('fast-isnumeric');
74708
74709var Axes = _dereq_('../../plots/cartesian/axes');
74710var Lib = _dereq_('../../lib');
74711
74712var BADNUM = _dereq_('../../constants/numerical').BADNUM;
74713var _ = Lib._;
74714
74715module.exports = function calc(gd, trace) {
74716 var fullLayout = gd._fullLayout;
74717 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
74718 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
74719 var cd = [];
74720
74721 // N.B. violin reuses same Box.calc
74722 var numKey = trace.type === 'violin' ? '_numViolins' : '_numBoxes';
74723
74724 var i, j;
74725 var valAxis, valLetter;
74726 var posAxis, posLetter;
74727
74728 if(trace.orientation === 'h') {
74729 valAxis = xa;
74730 valLetter = 'x';
74731 posAxis = ya;
74732 posLetter = 'y';
74733 } else {
74734 valAxis = ya;
74735 valLetter = 'y';
74736 posAxis = xa;
74737 posLetter = 'x';
74738 }
74739
74740 var posArray = getPos(trace, posLetter, posAxis, fullLayout[numKey]);
74741 var dv = Lib.distinctVals(posArray);
74742 var posDistinct = dv.vals;
74743 var dPos = dv.minDiff / 2;
74744
74745 // item in trace calcdata
74746 var cdi;
74747 // array of {v: v, i, i} sample pts
74748 var pts;
74749 // values of the `pts` array of objects
74750 var boxVals;
74751 // length of sample
74752 var N;
74753 // single sample point
74754 var pt;
74755 // single sample value
74756 var v;
74757
74758 // filter function for outlier pts
74759 // outlier definition based on http://www.physics.csbsju.edu/stats/box2.html
74760 var ptFilterFn = (trace.boxpoints || trace.points) === 'all' ?
74761 Lib.identity :
74762 function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); };
74763
74764 if(trace._hasPreCompStats) {
74765 var valArrayRaw = trace[valLetter];
74766 var d2c = function(k) { return valAxis.d2c((trace[k] || [])[i]); };
74767 var minVal = Infinity;
74768 var maxVal = -Infinity;
74769
74770 for(i = 0; i < trace._length; i++) {
74771 var posi = posArray[i];
74772 if(!isNumeric(posi)) continue;
74773
74774 cdi = {};
74775 cdi.pos = cdi[posLetter] = posi;
74776
74777 cdi.q1 = d2c('q1');
74778 cdi.med = d2c('median');
74779 cdi.q3 = d2c('q3');
74780
74781 pts = [];
74782 if(valArrayRaw && Lib.isArrayOrTypedArray(valArrayRaw[i])) {
74783 for(j = 0; j < valArrayRaw[i].length; j++) {
74784 v = valAxis.d2c(valArrayRaw[i][j]);
74785 if(v !== BADNUM) {
74786 pt = {v: v, i: [i, j]};
74787 arraysToCalcdata(pt, trace, [i, j]);
74788 pts.push(pt);
74789 }
74790 }
74791 }
74792 cdi.pts = pts.sort(sortByVal);
74793 boxVals = cdi[valLetter] = pts.map(extractVal);
74794 N = boxVals.length;
74795
74796 if(cdi.med !== BADNUM && cdi.q1 !== BADNUM && cdi.q3 !== BADNUM &&
74797 cdi.med >= cdi.q1 && cdi.q3 >= cdi.med
74798 ) {
74799 var lf = d2c('lowerfence');
74800 cdi.lf = (lf !== BADNUM && lf <= cdi.q1) ?
74801 lf :
74802 computeLowerFence(cdi, boxVals, N);
74803
74804 var uf = d2c('upperfence');
74805 cdi.uf = (uf !== BADNUM && uf >= cdi.q3) ?
74806 uf :
74807 computeUpperFence(cdi, boxVals, N);
74808
74809 var mean = d2c('mean');
74810 cdi.mean = (mean !== BADNUM) ?
74811 mean :
74812 (N ? Lib.mean(boxVals, N) : (cdi.q1 + cdi.q3) / 2);
74813
74814 var sd = d2c('sd');
74815 cdi.sd = (mean !== BADNUM && sd >= 0) ?
74816 sd :
74817 (N ? Lib.stdev(boxVals, N, cdi.mean) : (cdi.q3 - cdi.q1));
74818
74819 cdi.lo = computeLowerOutlierBound(cdi);
74820 cdi.uo = computeUpperOutlierBound(cdi);
74821
74822 var ns = d2c('notchspan');
74823 ns = (ns !== BADNUM && ns > 0) ? ns : computeNotchSpan(cdi, N);
74824 cdi.ln = cdi.med - ns;
74825 cdi.un = cdi.med + ns;
74826
74827 var imin = cdi.lf;
74828 var imax = cdi.uf;
74829 if(trace.boxpoints && boxVals.length) {
74830 imin = Math.min(imin, boxVals[0]);
74831 imax = Math.max(imax, boxVals[N - 1]);
74832 }
74833 if(trace.notched) {
74834 imin = Math.min(imin, cdi.ln);
74835 imax = Math.max(imax, cdi.un);
74836 }
74837 cdi.min = imin;
74838 cdi.max = imax;
74839 } else {
74840 Lib.warn([
74841 'Invalid input - make sure that q1 <= median <= q3',
74842 'q1 = ' + cdi.q1,
74843 'median = ' + cdi.med,
74844 'q3 = ' + cdi.q3
74845 ].join('\n'));
74846
74847 var v0;
74848 if(cdi.med !== BADNUM) {
74849 v0 = cdi.med;
74850 } else if(cdi.q1 !== BADNUM) {
74851 if(cdi.q3 !== BADNUM) v0 = (cdi.q1 + cdi.q3) / 2;
74852 else v0 = cdi.q1;
74853 } else if(cdi.q3 !== BADNUM) {
74854 v0 = cdi.q3;
74855 } else {
74856 v0 = 0;
74857 }
74858
74859 // draw box as line segment
74860 cdi.med = v0;
74861 cdi.q1 = cdi.q3 = v0;
74862 cdi.lf = cdi.uf = v0;
74863 cdi.mean = cdi.sd = v0;
74864 cdi.ln = cdi.un = v0;
74865 cdi.min = cdi.max = v0;
74866 }
74867
74868 minVal = Math.min(minVal, cdi.min);
74869 maxVal = Math.max(maxVal, cdi.max);
74870
74871 cdi.pts2 = pts.filter(ptFilterFn);
74872
74873 cd.push(cdi);
74874 }
74875
74876 trace._extremes[valAxis._id] = Axes.findExtremes(valAxis,
74877 [minVal, maxVal],
74878 {padded: true}
74879 );
74880 } else {
74881 var valArray = valAxis.makeCalcdata(trace, valLetter);
74882 var posBins = makeBins(posDistinct, dPos);
74883 var pLen = posDistinct.length;
74884 var ptsPerBin = initNestedArray(pLen);
74885
74886 // bin pts info per position bins
74887 for(i = 0; i < trace._length; i++) {
74888 v = valArray[i];
74889 if(!isNumeric(v)) continue;
74890
74891 var n = Lib.findBin(posArray[i], posBins);
74892 if(n >= 0 && n < pLen) {
74893 pt = {v: v, i: i};
74894 arraysToCalcdata(pt, trace, i);
74895 ptsPerBin[n].push(pt);
74896 }
74897 }
74898
74899 var minLowerNotch = Infinity;
74900 var maxUpperNotch = -Infinity;
74901
74902 var quartilemethod = trace.quartilemethod;
74903 var usesExclusive = quartilemethod === 'exclusive';
74904 var usesInclusive = quartilemethod === 'inclusive';
74905
74906 // build calcdata trace items, one item per distinct position
74907 for(i = 0; i < pLen; i++) {
74908 if(ptsPerBin[i].length > 0) {
74909 cdi = {};
74910 cdi.pos = cdi[posLetter] = posDistinct[i];
74911
74912 pts = cdi.pts = ptsPerBin[i].sort(sortByVal);
74913 boxVals = cdi[valLetter] = pts.map(extractVal);
74914 N = boxVals.length;
74915
74916 cdi.min = boxVals[0];
74917 cdi.max = boxVals[N - 1];
74918 cdi.mean = Lib.mean(boxVals, N);
74919 cdi.sd = Lib.stdev(boxVals, N, cdi.mean);
74920 cdi.med = Lib.interp(boxVals, 0.5);
74921
74922 if((N % 2) && (usesExclusive || usesInclusive)) {
74923 var lower;
74924 var upper;
74925
74926 if(usesExclusive) {
74927 // do NOT include the median in either half
74928 lower = boxVals.slice(0, N / 2);
74929 upper = boxVals.slice(N / 2 + 1);
74930 } else if(usesInclusive) {
74931 // include the median in either half
74932 lower = boxVals.slice(0, N / 2 + 1);
74933 upper = boxVals.slice(N / 2);
74934 }
74935
74936 cdi.q1 = Lib.interp(lower, 0.5);
74937 cdi.q3 = Lib.interp(upper, 0.5);
74938 } else {
74939 cdi.q1 = Lib.interp(boxVals, 0.25);
74940 cdi.q3 = Lib.interp(boxVals, 0.75);
74941 }
74942
74943 // lower and upper fences
74944 cdi.lf = computeLowerFence(cdi, boxVals, N);
74945 cdi.uf = computeUpperFence(cdi, boxVals, N);
74946
74947 // lower and upper outliers bounds
74948 cdi.lo = computeLowerOutlierBound(cdi);
74949 cdi.uo = computeUpperOutlierBound(cdi);
74950
74951 // lower and upper notches
74952 var mci = computeNotchSpan(cdi, N);
74953 cdi.ln = cdi.med - mci;
74954 cdi.un = cdi.med + mci;
74955 minLowerNotch = Math.min(minLowerNotch, cdi.ln);
74956 maxUpperNotch = Math.max(maxUpperNotch, cdi.un);
74957
74958 cdi.pts2 = pts.filter(ptFilterFn);
74959
74960 cd.push(cdi);
74961 }
74962 }
74963
74964 trace._extremes[valAxis._id] = Axes.findExtremes(valAxis,
74965 trace.notched ? valArray.concat([minLowerNotch, maxUpperNotch]) : valArray,
74966 {padded: true}
74967 );
74968 }
74969
74970 calcSelection(cd, trace);
74971
74972 if(cd.length > 0) {
74973 cd[0].t = {
74974 num: fullLayout[numKey],
74975 dPos: dPos,
74976 posLetter: posLetter,
74977 valLetter: valLetter,
74978 labels: {
74979 med: _(gd, 'median:'),
74980 min: _(gd, 'min:'),
74981 q1: _(gd, 'q1:'),
74982 q3: _(gd, 'q3:'),
74983 max: _(gd, 'max:'),
74984 mean: trace.boxmean === 'sd' ? _(gd, 'mean ± σ:') : _(gd, 'mean:'),
74985 lf: _(gd, 'lower fence:'),
74986 uf: _(gd, 'upper fence:')
74987 }
74988 };
74989
74990 fullLayout[numKey]++;
74991 return cd;
74992 } else {
74993 return [{t: {empty: true}}];
74994 }
74995};
74996
74997// In vertical (horizontal) box plots:
74998// if no x (y) data, use x0 (y0), or name
74999// so if you want one box
75000// per trace, set x0 (y0) to the x (y) value or category for this trace
75001// (or set x (y) to a constant array matching y (x))
75002function getPos(trace, posLetter, posAxis, num) {
75003 var hasPosArray = posLetter in trace;
75004 var hasPos0 = posLetter + '0' in trace;
75005 var hasPosStep = 'd' + posLetter in trace;
75006
75007 if(hasPosArray || (hasPos0 && hasPosStep)) {
75008 return posAxis.makeCalcdata(trace, posLetter);
75009 }
75010
75011 var pos0;
75012 if(hasPos0) {
75013 pos0 = trace[posLetter + '0'];
75014 } else if('name' in trace && (
75015 posAxis.type === 'category' || (
75016 isNumeric(trace.name) &&
75017 ['linear', 'log'].indexOf(posAxis.type) !== -1
75018 ) || (
75019 Lib.isDateTime(trace.name) &&
75020 posAxis.type === 'date'
75021 )
75022 )) {
75023 pos0 = trace.name;
75024 } else {
75025 pos0 = num;
75026 }
75027
75028 var pos0c = posAxis.type === 'multicategory' ?
75029 posAxis.r2c_just_indices(pos0) :
75030 posAxis.d2c(pos0, 0, trace[posLetter + 'calendar']);
75031
75032 var len = trace._length;
75033 var out = new Array(len);
75034 for(var i = 0; i < len; i++) out[i] = pos0c;
75035
75036 return out;
75037}
75038
75039function makeBins(x, dx) {
75040 var len = x.length;
75041 var bins = new Array(len + 1);
75042
75043 for(var i = 0; i < len; i++) {
75044 bins[i] = x[i] - dx;
75045 }
75046 bins[len] = x[len - 1] + dx;
75047
75048 return bins;
75049}
75050
75051function initNestedArray(len) {
75052 var arr = new Array(len);
75053 for(var i = 0; i < len; i++) {
75054 arr[i] = [];
75055 }
75056 return arr;
75057}
75058
75059var TRACE_TO_CALC = {
75060 text: 'tx',
75061 hovertext: 'htx'
75062};
75063
75064function arraysToCalcdata(pt, trace, ptNumber) {
75065 for(var k in TRACE_TO_CALC) {
75066 if(Lib.isArrayOrTypedArray(trace[k])) {
75067 if(Array.isArray(ptNumber)) {
75068 if(Lib.isArrayOrTypedArray(trace[k][ptNumber[0]])) {
75069 pt[TRACE_TO_CALC[k]] = trace[k][ptNumber[0]][ptNumber[1]];
75070 }
75071 } else {
75072 pt[TRACE_TO_CALC[k]] = trace[k][ptNumber];
75073 }
75074 }
75075 }
75076}
75077
75078function calcSelection(cd, trace) {
75079 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
75080 for(var i = 0; i < cd.length; i++) {
75081 var pts = cd[i].pts || [];
75082 var ptNumber2cdIndex = {};
75083
75084 for(var j = 0; j < pts.length; j++) {
75085 ptNumber2cdIndex[pts[j].i] = j;
75086 }
75087
75088 Lib.tagSelected(pts, trace, ptNumber2cdIndex);
75089 }
75090 }
75091}
75092
75093function sortByVal(a, b) { return a.v - b.v; }
75094
75095function extractVal(o) { return o.v; }
75096
75097// last point below 1.5 * IQR
75098function computeLowerFence(cdi, boxVals, N) {
75099 if(N === 0) return cdi.q1;
75100 return Math.min(
75101 cdi.q1,
75102 boxVals[Math.min(
75103 Lib.findBin(2.5 * cdi.q1 - 1.5 * cdi.q3, boxVals, true) + 1,
75104 N - 1
75105 )]
75106 );
75107}
75108
75109// last point above 1.5 * IQR
75110function computeUpperFence(cdi, boxVals, N) {
75111 if(N === 0) return cdi.q3;
75112 return Math.max(
75113 cdi.q3,
75114 boxVals[Math.max(
75115 Lib.findBin(2.5 * cdi.q3 - 1.5 * cdi.q1, boxVals),
75116 0
75117 )]
75118 );
75119}
75120
75121// 3 IQR below (don't clip to max/min,
75122// this is only for discriminating suspected & far outliers)
75123function computeLowerOutlierBound(cdi) {
75124 return 4 * cdi.q1 - 3 * cdi.q3;
75125}
75126
75127// 3 IQR above (don't clip to max/min,
75128// this is only for discriminating suspected & far outliers)
75129function computeUpperOutlierBound(cdi) {
75130 return 4 * cdi.q3 - 3 * cdi.q1;
75131}
75132
75133// 95% confidence intervals for median
75134function computeNotchSpan(cdi, N) {
75135 if(N === 0) return 0;
75136 return 1.57 * (cdi.q3 - cdi.q1) / Math.sqrt(N);
75137}
75138
75139},{"../../constants/numerical":158,"../../lib":178,"../../plots/cartesian/axes":222,"fast-isnumeric":18}],298:[function(_dereq_,module,exports){
75140/**
75141* Copyright 2012-2020, Plotly, Inc.
75142* All rights reserved.
75143*
75144* This source code is licensed under the MIT license found in the
75145* LICENSE file in the root directory of this source tree.
75146*/
75147
75148'use strict';
75149
75150var Axes = _dereq_('../../plots/cartesian/axes');
75151var Lib = _dereq_('../../lib');
75152var getAxisGroup = _dereq_('../../plots/cartesian/axis_ids').getAxisGroup;
75153
75154var orientations = ['v', 'h'];
75155
75156function crossTraceCalc(gd, plotinfo) {
75157 var calcdata = gd.calcdata;
75158 var xa = plotinfo.xaxis;
75159 var ya = plotinfo.yaxis;
75160
75161 for(var i = 0; i < orientations.length; i++) {
75162 var orientation = orientations[i];
75163 var posAxis = orientation === 'h' ? ya : xa;
75164 var boxList = [];
75165
75166 // make list of boxes / candlesticks
75167 // For backward compatibility, candlesticks are treated as if they *are* box traces here
75168 for(var j = 0; j < calcdata.length; j++) {
75169 var cd = calcdata[j];
75170 var t = cd[0].t;
75171 var trace = cd[0].trace;
75172
75173 if(trace.visible === true &&
75174 (trace.type === 'box' || trace.type === 'candlestick') &&
75175 !t.empty &&
75176 (trace.orientation || 'v') === orientation &&
75177 trace.xaxis === xa._id &&
75178 trace.yaxis === ya._id
75179 ) {
75180 boxList.push(j);
75181 }
75182 }
75183
75184 setPositionOffset('box', gd, boxList, posAxis);
75185 }
75186}
75187
75188function setPositionOffset(traceType, gd, boxList, posAxis) {
75189 var calcdata = gd.calcdata;
75190 var fullLayout = gd._fullLayout;
75191 var axId = posAxis._id;
75192 var axLetter = axId.charAt(0);
75193
75194 var i, j, calcTrace;
75195 var pointList = [];
75196 var shownPts = 0;
75197
75198 // make list of box points
75199 for(i = 0; i < boxList.length; i++) {
75200 calcTrace = calcdata[boxList[i]];
75201 for(j = 0; j < calcTrace.length; j++) {
75202 pointList.push(posAxis.c2l(calcTrace[j].pos, true));
75203 shownPts += (calcTrace[j].pts2 || []).length;
75204 }
75205 }
75206
75207 if(!pointList.length) return;
75208
75209 // box plots - update dPos based on multiple traces
75210 var boxdv = Lib.distinctVals(pointList);
75211 var dPos0 = boxdv.minDiff / 2;
75212
75213 // check for forced minimum dtick
75214 Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true);
75215
75216 var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';
75217 var numTotal = fullLayout[numKey];
75218 var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1;
75219 var groupFraction = 1 - fullLayout[traceType + 'gap'];
75220 var groupGapFraction = 1 - fullLayout[traceType + 'groupgap'];
75221
75222 for(i = 0; i < boxList.length; i++) {
75223 calcTrace = calcdata[boxList[i]];
75224
75225 var trace = calcTrace[0].trace;
75226 var t = calcTrace[0].t;
75227 var width = trace.width;
75228 var side = trace.side;
75229
75230 // position coordinate delta
75231 var dPos;
75232 // box half width;
75233 var bdPos;
75234 // box center offset
75235 var bPos;
75236 // half-width within which to accept hover for this box/violin
75237 // always split the distance to the closest box/violin
75238 var wHover;
75239
75240 if(width) {
75241 dPos = bdPos = wHover = width / 2;
75242 bPos = 0;
75243 } else {
75244 dPos = dPos0;
75245
75246 if(group) {
75247 var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation;
75248 var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
75249 var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
75250 var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;
75251 var num = nOffsetGroups || numTotal;
75252 var shift = nOffsetGroups ? trace._offsetIndex : t.num;
75253
75254 bdPos = dPos * groupFraction * groupGapFraction / num;
75255 bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction;
75256 wHover = dPos * groupFraction / num;
75257 } else {
75258 bdPos = dPos * groupFraction * groupGapFraction;
75259 bPos = 0;
75260 wHover = dPos;
75261 }
75262 }
75263 t.dPos = dPos;
75264 t.bPos = bPos;
75265 t.bdPos = bdPos;
75266 t.wHover = wHover;
75267
75268 // box/violin-only value-space push value
75269 var pushplus;
75270 var pushminus;
75271 // edge of box/violin
75272 var edge = bPos + bdPos;
75273 var edgeplus;
75274 var edgeminus;
75275 // value-space padding
75276 var vpadplus;
75277 var vpadminus;
75278 // pixel-space padding
75279 var ppadplus;
75280 var ppadminus;
75281 // do we add 5% of both sides (more logic for points beyond box/violin below)
75282 var padded = Boolean(width);
75283 // does this trace show points?
75284 var hasPts = (trace.boxpoints || trace.points) && (shownPts > 0);
75285
75286 if(side === 'positive') {
75287 pushplus = dPos * (width ? 1 : 0.5);
75288 edgeplus = edge;
75289 pushminus = edgeplus = bPos;
75290 } else if(side === 'negative') {
75291 pushplus = edgeplus = bPos;
75292 pushminus = dPos * (width ? 1 : 0.5);
75293 edgeminus = edge;
75294 } else {
75295 pushplus = pushminus = dPos;
75296 edgeplus = edgeminus = edge;
75297 }
75298
75299 if(hasPts) {
75300 var pointpos = trace.pointpos;
75301 var jitter = trace.jitter;
75302 var ms = trace.marker.size / 2;
75303
75304 var pp = 0;
75305 if((pointpos + jitter) >= 0) {
75306 pp = edge * (pointpos + jitter);
75307 if(pp > pushplus) {
75308 // (++) beyond plus-value, use pp
75309 padded = true;
75310 ppadplus = ms;
75311 vpadplus = pp;
75312 } else if(pp > edgeplus) {
75313 // (+), use push-value (it's bigger), but add px-pad
75314 ppadplus = ms;
75315 vpadplus = pushplus;
75316 }
75317 }
75318 if(pp <= pushplus) {
75319 // (->) fallback to push value
75320 vpadplus = pushplus;
75321 }
75322
75323 var pm = 0;
75324 if((pointpos - jitter) <= 0) {
75325 pm = -edge * (pointpos - jitter);
75326 if(pm > pushminus) {
75327 // (--) beyond plus-value, use pp
75328 padded = true;
75329 ppadminus = ms;
75330 vpadminus = pm;
75331 } else if(pm > edgeminus) {
75332 // (-), use push-value (it's bigger), but add px-pad
75333 ppadminus = ms;
75334 vpadminus = pushminus;
75335 }
75336 }
75337 if(pm <= pushminus) {
75338 // (<-) fallback to push value
75339 vpadminus = pushminus;
75340 }
75341 } else {
75342 vpadplus = pushplus;
75343 vpadminus = pushminus;
75344 }
75345
75346 var pos = new Array(calcTrace.length);
75347 for(j = 0; j < calcTrace.length; j++) {
75348 pos[j] = calcTrace[j].pos;
75349 }
75350
75351 trace._extremes[axId] = Axes.findExtremes(posAxis, pos, {
75352 padded: padded,
75353 vpadminus: vpadminus,
75354 vpadplus: vpadplus,
75355 vpadLinearized: true,
75356 // N.B. SVG px-space positive/negative
75357 ppadminus: {x: ppadminus, y: ppadplus}[axLetter],
75358 ppadplus: {x: ppadplus, y: ppadminus}[axLetter],
75359 });
75360 }
75361}
75362
75363module.exports = {
75364 crossTraceCalc: crossTraceCalc,
75365 setPositionOffset: setPositionOffset
75366};
75367
75368},{"../../lib":178,"../../plots/cartesian/axes":222,"../../plots/cartesian/axis_ids":225}],299:[function(_dereq_,module,exports){
75369/**
75370* Copyright 2012-2020, Plotly, Inc.
75371* All rights reserved.
75372*
75373* This source code is licensed under the MIT license found in the
75374* LICENSE file in the root directory of this source tree.
75375*/
75376
75377'use strict';
75378
75379var Lib = _dereq_('../../lib');
75380var Registry = _dereq_('../../registry');
75381var Color = _dereq_('../../components/color');
75382var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults;
75383var autoType = _dereq_('../../plots/cartesian/axis_autotype');
75384var attributes = _dereq_('./attributes');
75385
75386function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
75387 function coerce(attr, dflt) {
75388 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
75389 }
75390
75391 handleSampleDefaults(traceIn, traceOut, coerce, layout);
75392 if(traceOut.visible === false) return;
75393
75394 var hasPreCompStats = traceOut._hasPreCompStats;
75395
75396 if(hasPreCompStats) {
75397 coerce('lowerfence');
75398 coerce('upperfence');
75399 }
75400
75401 coerce('line.color', (traceIn.marker || {}).color || defaultColor);
75402 coerce('line.width');
75403 coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
75404
75405 var boxmeanDflt = false;
75406 if(hasPreCompStats) {
75407 var mean = coerce('mean');
75408 var sd = coerce('sd');
75409 if(mean && mean.length) {
75410 boxmeanDflt = true;
75411 if(sd && sd.length) boxmeanDflt = 'sd';
75412 }
75413 }
75414 coerce('boxmean', boxmeanDflt);
75415
75416 coerce('whiskerwidth');
75417 coerce('width');
75418 coerce('quartilemethod');
75419
75420 var notchedDflt = false;
75421 if(hasPreCompStats) {
75422 var notchspan = coerce('notchspan');
75423 if(notchspan && notchspan.length) {
75424 notchedDflt = true;
75425 }
75426 } else if(Lib.validate(traceIn.notchwidth, attributes.notchwidth)) {
75427 notchedDflt = true;
75428 }
75429 var notched = coerce('notched', notchedDflt);
75430 if(notched) coerce('notchwidth');
75431
75432 handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
75433}
75434
75435function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
75436 function getDims(arr) {
75437 var dims = 0;
75438 if(arr && arr.length) {
75439 dims += 1;
75440 if(Lib.isArrayOrTypedArray(arr[0]) && arr[0].length) {
75441 dims += 1;
75442 }
75443 }
75444 return dims;
75445 }
75446
75447 function valid(astr) {
75448 return Lib.validate(traceIn[astr], attributes[astr]);
75449 }
75450
75451 var y = coerce('y');
75452 var x = coerce('x');
75453
75454 var sLen;
75455 if(traceOut.type === 'box') {
75456 var q1 = coerce('q1');
75457 var median = coerce('median');
75458 var q3 = coerce('q3');
75459
75460 traceOut._hasPreCompStats = (
75461 q1 && q1.length &&
75462 median && median.length &&
75463 q3 && q3.length
75464 );
75465 sLen = Math.min(
75466 Lib.minRowLength(q1),
75467 Lib.minRowLength(median),
75468 Lib.minRowLength(q3)
75469 );
75470 }
75471
75472 var yDims = getDims(y);
75473 var xDims = getDims(x);
75474 var yLen = yDims && Lib.minRowLength(y);
75475 var xLen = xDims && Lib.minRowLength(x);
75476
75477 var defaultOrientation, len;
75478 if(traceOut._hasPreCompStats) {
75479 switch(String(xDims) + String(yDims)) {
75480 // no x / no y
75481 case '00':
75482 var setInX = valid('x0') || valid('dx');
75483 var setInY = valid('y0') || valid('dy');
75484
75485 if(setInY && !setInX) {
75486 defaultOrientation = 'h';
75487 } else {
75488 defaultOrientation = 'v';
75489 }
75490
75491 len = sLen;
75492 break;
75493 // just x
75494 case '10':
75495 defaultOrientation = 'v';
75496 len = Math.min(sLen, xLen);
75497 break;
75498 case '20':
75499 defaultOrientation = 'h';
75500 len = Math.min(sLen, x.length);
75501 break;
75502 // just y
75503 case '01':
75504 defaultOrientation = 'h';
75505 len = Math.min(sLen, yLen);
75506 break;
75507 case '02':
75508 defaultOrientation = 'v';
75509 len = Math.min(sLen, y.length);
75510 break;
75511 // both
75512 case '12':
75513 defaultOrientation = 'v';
75514 len = Math.min(sLen, xLen, y.length);
75515 break;
75516 case '21':
75517 defaultOrientation = 'h';
75518 len = Math.min(sLen, x.length, yLen);
75519 break;
75520 case '11':
75521 // this one is ill-defined
75522 len = 0;
75523 break;
75524 case '22':
75525 var hasCategories = false;
75526 var i;
75527 for(i = 0; i < x.length; i++) {
75528 if(autoType(x[i]) === 'category') {
75529 hasCategories = true;
75530 break;
75531 }
75532 }
75533
75534 if(hasCategories) {
75535 defaultOrientation = 'v';
75536 len = Math.min(sLen, xLen, y.length);
75537 } else {
75538 for(i = 0; i < y.length; i++) {
75539 if(autoType(y[i]) === 'category') {
75540 hasCategories = true;
75541 break;
75542 }
75543 }
75544
75545 if(hasCategories) {
75546 defaultOrientation = 'h';
75547 len = Math.min(sLen, x.length, yLen);
75548 } else {
75549 defaultOrientation = 'v';
75550 len = Math.min(sLen, xLen, y.length);
75551 }
75552 }
75553 break;
75554 }
75555 } else if(yDims > 0) {
75556 defaultOrientation = 'v';
75557 if(xDims > 0) {
75558 len = Math.min(xLen, yLen);
75559 } else {
75560 len = Math.min(yLen);
75561 }
75562 } else if(xDims > 0) {
75563 defaultOrientation = 'h';
75564 len = Math.min(xLen);
75565 } else {
75566 len = 0;
75567 }
75568
75569 if(!len) {
75570 traceOut.visible = false;
75571 return;
75572 }
75573 traceOut._length = len;
75574
75575 var orientation = coerce('orientation', defaultOrientation);
75576
75577 // these are just used for positioning, they never define the sample
75578 if(traceOut._hasPreCompStats) {
75579 if(orientation === 'v' && xDims === 0) {
75580 coerce('x0', 0);
75581 coerce('dx', 1);
75582 } else if(orientation === 'h' && yDims === 0) {
75583 coerce('y0', 0);
75584 coerce('dy', 1);
75585 }
75586 } else {
75587 if(orientation === 'v' && xDims === 0) {
75588 coerce('x0');
75589 } else if(orientation === 'h' && yDims === 0) {
75590 coerce('y0');
75591 }
75592 }
75593
75594 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
75595 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
75596}
75597
75598function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
75599 var prefix = opts.prefix;
75600
75601 var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor');
75602 var lineoutliercolor = coerce('marker.line.outliercolor');
75603
75604 var modeDflt = 'outliers';
75605 if(traceOut._hasPreCompStats) {
75606 modeDflt = 'all';
75607 } else if(outlierColorDflt || lineoutliercolor) {
75608 modeDflt = 'suspectedoutliers';
75609 }
75610
75611 var mode = coerce(prefix + 'points', modeDflt);
75612
75613 if(mode) {
75614 coerce('jitter', mode === 'all' ? 0.3 : 0);
75615 coerce('pointpos', mode === 'all' ? -1.5 : 0);
75616
75617 coerce('marker.symbol');
75618 coerce('marker.opacity');
75619 coerce('marker.size');
75620 coerce('marker.color', traceOut.line.color);
75621 coerce('marker.line.color');
75622 coerce('marker.line.width');
75623
75624 if(mode === 'suspectedoutliers') {
75625 coerce('marker.line.outliercolor', traceOut.marker.color);
75626 coerce('marker.line.outlierwidth');
75627 }
75628
75629 coerce('selected.marker.color');
75630 coerce('unselected.marker.color');
75631 coerce('selected.marker.size');
75632 coerce('unselected.marker.size');
75633
75634 coerce('text');
75635 coerce('hovertext');
75636 } else {
75637 delete traceOut.marker;
75638 }
75639
75640 var hoveron = coerce('hoveron');
75641 if(hoveron === 'all' || hoveron.indexOf('points') !== -1) {
75642 coerce('hovertemplate');
75643 }
75644
75645 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
75646}
75647
75648function crossTraceDefaults(fullData, fullLayout) {
75649 var traceIn, traceOut;
75650
75651 function coerce(attr) {
75652 return Lib.coerce(traceOut._input, traceOut, attributes, attr);
75653 }
75654
75655 for(var i = 0; i < fullData.length; i++) {
75656 traceOut = fullData[i];
75657 var traceType = traceOut.type;
75658
75659 if(traceType === 'box' || traceType === 'violin') {
75660 traceIn = traceOut._input;
75661 if(fullLayout[traceType + 'mode'] === 'group') {
75662 handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
75663 }
75664 }
75665 }
75666}
75667
75668module.exports = {
75669 supplyDefaults: supplyDefaults,
75670 crossTraceDefaults: crossTraceDefaults,
75671
75672 handleSampleDefaults: handleSampleDefaults,
75673 handlePointsDefaults: handlePointsDefaults
75674};
75675
75676},{"../../components/color":52,"../../lib":178,"../../plots/cartesian/axis_autotype":223,"../../registry":269,"../bar/defaults":283,"./attributes":296}],300:[function(_dereq_,module,exports){
75677/**
75678* Copyright 2012-2020, Plotly, Inc.
75679* All rights reserved.
75680*
75681* This source code is licensed under the MIT license found in the
75682* LICENSE file in the root directory of this source tree.
75683*/
75684
75685'use strict';
75686
75687module.exports = function eventData(out, pt) {
75688 // Note: hoverOnBox property is needed for click-to-select
75689 // to ignore when a box was clicked. This is the reason box
75690 // implements this custom eventData function.
75691 if(pt.hoverOnBox) out.hoverOnBox = pt.hoverOnBox;
75692
75693 if('xVal' in pt) out.x = pt.xVal;
75694 if('yVal' in pt) out.y = pt.yVal;
75695 if(pt.xa) out.xaxis = pt.xa;
75696 if(pt.ya) out.yaxis = pt.ya;
75697
75698 return out;
75699};
75700
75701},{}],301:[function(_dereq_,module,exports){
75702/**
75703* Copyright 2012-2020, Plotly, Inc.
75704* All rights reserved.
75705*
75706* This source code is licensed under the MIT license found in the
75707* LICENSE file in the root directory of this source tree.
75708*/
75709
75710'use strict';
75711
75712var Axes = _dereq_('../../plots/cartesian/axes');
75713var Lib = _dereq_('../../lib');
75714var Fx = _dereq_('../../components/fx');
75715var Color = _dereq_('../../components/color');
75716var fillText = Lib.fillText;
75717
75718function hoverPoints(pointData, xval, yval, hovermode) {
75719 var cd = pointData.cd;
75720 var trace = cd[0].trace;
75721 var hoveron = trace.hoveron;
75722 var closeBoxData = [];
75723 var closePtData;
75724
75725 if(hoveron.indexOf('boxes') !== -1) {
75726 closeBoxData = closeBoxData.concat(hoverOnBoxes(pointData, xval, yval, hovermode));
75727 }
75728
75729 if(hoveron.indexOf('points') !== -1) {
75730 closePtData = hoverOnPoints(pointData, xval, yval);
75731 }
75732
75733 // If there's a point in range and hoveron has points, show the best single point only.
75734 // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats.
75735 if(hovermode === 'closest') {
75736 if(closePtData) return [closePtData];
75737 return closeBoxData;
75738 }
75739
75740 // Otherwise in compare mode, allow a point AND the box stats to be labeled
75741 // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them.
75742 if(closePtData) {
75743 closeBoxData.push(closePtData);
75744 return closeBoxData;
75745 }
75746 return closeBoxData;
75747}
75748
75749function hoverOnBoxes(pointData, xval, yval, hovermode) {
75750 var cd = pointData.cd;
75751 var xa = pointData.xa;
75752 var ya = pointData.ya;
75753 var trace = cd[0].trace;
75754 var t = cd[0].t;
75755 var isViolin = trace.type === 'violin';
75756 var closeBoxData = [];
75757
75758 var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos,
75759 hoverPseudoDistance, spikePseudoDistance;
75760
75761 var boxDelta = t.bdPos;
75762 var boxDeltaPos, boxDeltaNeg;
75763 var posAcceptance = t.wHover;
75764 var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); };
75765
75766 if(isViolin && trace.side !== 'both') {
75767 if(trace.side === 'positive') {
75768 dPos = function(di) {
75769 var pos = shiftPos(di);
75770 return Fx.inbox(pos, pos + posAcceptance, hoverPseudoDistance);
75771 };
75772 boxDeltaPos = boxDelta;
75773 boxDeltaNeg = 0;
75774 }
75775 if(trace.side === 'negative') {
75776 dPos = function(di) {
75777 var pos = shiftPos(di);
75778 return Fx.inbox(pos - posAcceptance, pos, hoverPseudoDistance);
75779 };
75780 boxDeltaPos = 0;
75781 boxDeltaNeg = boxDelta;
75782 }
75783 } else {
75784 dPos = function(di) {
75785 var pos = shiftPos(di);
75786 return Fx.inbox(pos - posAcceptance, pos + posAcceptance, hoverPseudoDistance);
75787 };
75788 boxDeltaPos = boxDeltaNeg = boxDelta;
75789 }
75790
75791 var dVal;
75792
75793 if(isViolin) {
75794 dVal = function(di) {
75795 return Fx.inbox(di.span[0] - vVal, di.span[1] - vVal, hoverPseudoDistance);
75796 };
75797 } else {
75798 dVal = function(di) {
75799 return Fx.inbox(di.min - vVal, di.max - vVal, hoverPseudoDistance);
75800 };
75801 }
75802
75803 if(trace.orientation === 'h') {
75804 vVal = xval;
75805 pVal = yval;
75806 dx = dVal;
75807 dy = dPos;
75808 pLetter = 'y';
75809 pAxis = ya;
75810 vLetter = 'x';
75811 vAxis = xa;
75812 } else {
75813 vVal = yval;
75814 pVal = xval;
75815 dx = dPos;
75816 dy = dVal;
75817 pLetter = 'x';
75818 pAxis = xa;
75819 vLetter = 'y';
75820 vAxis = ya;
75821 }
75822
75823 // if two boxes are overlaying, let the narrowest one win
75824 var pseudoDistance = Math.min(1, boxDelta / Math.abs(pAxis.r2c(pAxis.range[1]) - pAxis.r2c(pAxis.range[0])));
75825 hoverPseudoDistance = pointData.maxHoverDistance - pseudoDistance;
75826 spikePseudoDistance = pointData.maxSpikeDistance - pseudoDistance;
75827
75828 function dxy(di) { return (dx(di) + dy(di)) / 2; }
75829 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
75830 Fx.getClosest(cd, distfn, pointData);
75831
75832 // skip the rest (for this trace) if we didn't find a close point
75833 // and create the item(s) in closedata for this point
75834 if(pointData.index === false) return [];
75835
75836 var di = cd[pointData.index];
75837 var lc = trace.line.color;
75838 var mc = (trace.marker || {}).color;
75839
75840 if(Color.opacity(lc) && trace.line.width) pointData.color = lc;
75841 else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc;
75842 else pointData.color = trace.fillcolor;
75843
75844 pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDeltaNeg, true);
75845 pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDeltaPos, true);
75846
75847 pointData[pLetter + 'LabelVal'] = di.pos;
75848
75849 var spikePosAttr = pLetter + 'Spike';
75850 pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance;
75851 pointData[spikePosAttr] = pAxis.c2p(di.pos, true);
75852
75853 // box plots: each "point" gets many labels
75854 var usedVals = {};
75855 var attrs = ['med', 'q1', 'q3', 'min', 'max'];
75856
75857 if(trace.boxmean || (trace.meanline || {}).visible) {
75858 attrs.push('mean');
75859 }
75860 if(trace.boxpoints || trace.points) {
75861 attrs.push('lf', 'uf');
75862 }
75863
75864 for(var i = 0; i < attrs.length; i++) {
75865 var attr = attrs[i];
75866
75867 if(!(attr in di) || (di[attr] in usedVals)) continue;
75868 usedVals[di[attr]] = true;
75869
75870 // copy out to a new object for each value to label
75871 var val = di[attr];
75872 var valPx = vAxis.c2p(val, true);
75873 var pointData2 = Lib.extendFlat({}, pointData);
75874
75875 pointData2.attr = attr;
75876 pointData2[vLetter + '0'] = pointData2[vLetter + '1'] = valPx;
75877 pointData2[vLetter + 'LabelVal'] = val;
75878 pointData2[vLetter + 'Label'] = (t.labels ? t.labels[attr] + ' ' : '') + Axes.hoverLabelText(vAxis, val);
75879
75880 // Note: introduced to be able to distinguish a
75881 // clicked point from a box during click-to-select
75882 pointData2.hoverOnBox = true;
75883
75884 if(attr === 'mean' && ('sd' in di) && trace.boxmean === 'sd') {
75885 pointData2[vLetter + 'err'] = di.sd;
75886 }
75887
75888 // only keep name and spikes on the first item (median)
75889 pointData.name = '';
75890 pointData.spikeDistance = undefined;
75891 pointData[spikePosAttr] = undefined;
75892
75893 // no hovertemplate support yet
75894 pointData2.hovertemplate = false;
75895
75896 closeBoxData.push(pointData2);
75897 }
75898
75899 return closeBoxData;
75900}
75901
75902function hoverOnPoints(pointData, xval, yval) {
75903 var cd = pointData.cd;
75904 var xa = pointData.xa;
75905 var ya = pointData.ya;
75906 var trace = cd[0].trace;
75907 var xPx = xa.c2p(xval);
75908 var yPx = ya.c2p(yval);
75909 var closePtData;
75910
75911 var dx = function(di) {
75912 var rad = Math.max(3, di.mrc || 0);
75913 return Math.max(Math.abs(xa.c2p(di.x) - xPx) - rad, 1 - 3 / rad);
75914 };
75915 var dy = function(di) {
75916 var rad = Math.max(3, di.mrc || 0);
75917 return Math.max(Math.abs(ya.c2p(di.y) - yPx) - rad, 1 - 3 / rad);
75918 };
75919 var distfn = Fx.quadrature(dx, dy);
75920
75921 // show one point per trace
75922 var ijClosest = false;
75923 var di, pt;
75924
75925 for(var i = 0; i < cd.length; i++) {
75926 di = cd[i];
75927
75928 for(var j = 0; j < (di.pts || []).length; j++) {
75929 pt = di.pts[j];
75930
75931 var newDistance = distfn(pt);
75932 if(newDistance <= pointData.distance) {
75933 pointData.distance = newDistance;
75934 ijClosest = [i, j];
75935 }
75936 }
75937 }
75938
75939 if(!ijClosest) return false;
75940
75941 di = cd[ijClosest[0]];
75942 pt = di.pts[ijClosest[1]];
75943
75944 var xc = xa.c2p(pt.x, true);
75945 var yc = ya.c2p(pt.y, true);
75946 var rad = pt.mrc || 1;
75947
75948 closePtData = Lib.extendFlat({}, pointData, {
75949 // corresponds to index in x/y input data array
75950 index: pt.i,
75951 color: (trace.marker || {}).color,
75952 name: trace.name,
75953 x0: xc - rad,
75954 x1: xc + rad,
75955 y0: yc - rad,
75956 y1: yc + rad,
75957 spikeDistance: pointData.distance,
75958 hovertemplate: trace.hovertemplate
75959 });
75960
75961 var pa;
75962 if(trace.orientation === 'h') {
75963 pa = ya;
75964 closePtData.xLabelVal = pt.x;
75965 closePtData.yLabelVal = di.pos;
75966 } else {
75967 pa = xa;
75968 closePtData.xLabelVal = di.pos;
75969 closePtData.yLabelVal = pt.y;
75970 }
75971
75972 var pLetter = pa._id.charAt(0);
75973 closePtData[pLetter + 'Spike'] = pa.c2p(di.pos, true);
75974
75975 fillText(pt, trace, closePtData);
75976
75977 return closePtData;
75978}
75979
75980module.exports = {
75981 hoverPoints: hoverPoints,
75982 hoverOnBoxes: hoverOnBoxes,
75983 hoverOnPoints: hoverOnPoints
75984};
75985
75986},{"../../components/color":52,"../../components/fx":92,"../../lib":178,"../../plots/cartesian/axes":222}],302:[function(_dereq_,module,exports){
75987/**
75988* Copyright 2012-2020, Plotly, Inc.
75989* All rights reserved.
75990*
75991* This source code is licensed under the MIT license found in the
75992* LICENSE file in the root directory of this source tree.
75993*/
75994
75995'use strict';
75996
75997module.exports = {
75998 attributes: _dereq_('./attributes'),
75999 layoutAttributes: _dereq_('./layout_attributes'),
76000 supplyDefaults: _dereq_('./defaults').supplyDefaults,
76001 crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults,
76002 supplyLayoutDefaults: _dereq_('./layout_defaults').supplyLayoutDefaults,
76003 calc: _dereq_('./calc'),
76004 crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc,
76005 plot: _dereq_('./plot').plot,
76006 style: _dereq_('./style').style,
76007 styleOnSelect: _dereq_('./style').styleOnSelect,
76008 hoverPoints: _dereq_('./hover').hoverPoints,
76009 eventData: _dereq_('./event_data'),
76010 selectPoints: _dereq_('./select'),
76011
76012 moduleType: 'trace',
76013 name: 'box',
76014 basePlotModule: _dereq_('../../plots/cartesian'),
76015 categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'boxLayout', 'zoomScale'],
76016 meta: {
76017
76018 }
76019};
76020
76021},{"../../plots/cartesian":235,"./attributes":296,"./calc":297,"./cross_trace_calc":298,"./defaults":299,"./event_data":300,"./hover":301,"./layout_attributes":303,"./layout_defaults":304,"./plot":305,"./select":306,"./style":307}],303:[function(_dereq_,module,exports){
76022/**
76023* Copyright 2012-2020, Plotly, Inc.
76024* All rights reserved.
76025*
76026* This source code is licensed under the MIT license found in the
76027* LICENSE file in the root directory of this source tree.
76028*/
76029
76030'use strict';
76031
76032
76033module.exports = {
76034 boxmode: {
76035 valType: 'enumerated',
76036 values: ['group', 'overlay'],
76037 dflt: 'overlay',
76038
76039 editType: 'calc',
76040
76041 },
76042 boxgap: {
76043 valType: 'number',
76044 min: 0,
76045 max: 1,
76046 dflt: 0.3,
76047
76048 editType: 'calc',
76049
76050 },
76051 boxgroupgap: {
76052 valType: 'number',
76053 min: 0,
76054 max: 1,
76055 dflt: 0.3,
76056
76057 editType: 'calc',
76058
76059 }
76060};
76061
76062},{}],304:[function(_dereq_,module,exports){
76063/**
76064* Copyright 2012-2020, Plotly, Inc.
76065* All rights reserved.
76066*
76067* This source code is licensed under the MIT license found in the
76068* LICENSE file in the root directory of this source tree.
76069*/
76070
76071'use strict';
76072
76073var Registry = _dereq_('../../registry');
76074var Lib = _dereq_('../../lib');
76075var layoutAttributes = _dereq_('./layout_attributes');
76076
76077function _supply(layoutIn, layoutOut, fullData, coerce, traceType) {
76078 var category = traceType + 'Layout';
76079 var hasTraceType = false;
76080
76081 for(var i = 0; i < fullData.length; i++) {
76082 var trace = fullData[i];
76083
76084 if(Registry.traceIs(trace, category)) {
76085 hasTraceType = true;
76086 break;
76087 }
76088 }
76089 if(!hasTraceType) return;
76090
76091 coerce(traceType + 'mode');
76092 coerce(traceType + 'gap');
76093 coerce(traceType + 'groupgap');
76094}
76095
76096function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
76097 function coerce(attr, dflt) {
76098 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
76099 }
76100 _supply(layoutIn, layoutOut, fullData, coerce, 'box');
76101}
76102
76103module.exports = {
76104 supplyLayoutDefaults: supplyLayoutDefaults,
76105 _supply: _supply
76106};
76107
76108},{"../../lib":178,"../../registry":269,"./layout_attributes":303}],305:[function(_dereq_,module,exports){
76109/**
76110* Copyright 2012-2020, Plotly, Inc.
76111* All rights reserved.
76112*
76113* This source code is licensed under the MIT license found in the
76114* LICENSE file in the root directory of this source tree.
76115*/
76116
76117'use strict';
76118
76119var d3 = _dereq_('d3');
76120
76121var Lib = _dereq_('../../lib');
76122var Drawing = _dereq_('../../components/drawing');
76123
76124// constants for dynamic jitter (ie less jitter for sparser points)
76125var JITTERCOUNT = 5; // points either side of this to include
76126var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
76127
76128function plot(gd, plotinfo, cdbox, boxLayer) {
76129 var xa = plotinfo.xaxis;
76130 var ya = plotinfo.yaxis;
76131
76132 Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) {
76133 var plotGroup = d3.select(this);
76134 var cd0 = cd[0];
76135 var t = cd0.t;
76136 var trace = cd0.trace;
76137
76138 // whisker width
76139 t.wdPos = t.bdPos * trace.whiskerwidth;
76140
76141 if(trace.visible !== true || t.empty) {
76142 plotGroup.remove();
76143 return;
76144 }
76145
76146 var posAxis, valAxis;
76147
76148 if(trace.orientation === 'h') {
76149 posAxis = ya;
76150 valAxis = xa;
76151 } else {
76152 posAxis = xa;
76153 valAxis = ya;
76154 }
76155
76156 plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
76157 plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
76158 plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
76159 });
76160}
76161
76162function plotBoxAndWhiskers(sel, axes, trace, t) {
76163 var posAxis = axes.pos;
76164 var valAxis = axes.val;
76165 var bPos = t.bPos;
76166 var wdPos = t.wdPos || 0;
76167 var bPosPxOffset = t.bPosPxOffset || 0;
76168 var whiskerWidth = trace.whiskerwidth || 0;
76169 var notched = trace.notched || false;
76170 var nw = notched ? 1 - 2 * trace.notchwidth : 1;
76171
76172 // to support for one-sided box
76173 var bdPos0;
76174 var bdPos1;
76175 if(Array.isArray(t.bdPos)) {
76176 bdPos0 = t.bdPos[0];
76177 bdPos1 = t.bdPos[1];
76178 } else {
76179 bdPos0 = t.bdPos;
76180 bdPos1 = t.bdPos;
76181 }
76182
76183 var paths = sel.selectAll('path.box').data((
76184 trace.type !== 'violin' ||
76185 trace.box.visible
76186 ) ? Lib.identity : []);
76187
76188 paths.enter().append('path')
76189 .style('vector-effect', 'non-scaling-stroke')
76190 .attr('class', 'box');
76191
76192 paths.exit().remove();
76193
76194 paths.each(function(d) {
76195 if(d.empty) return 'M0,0Z';
76196
76197 var lcenter = posAxis.c2l(d.pos + bPos, true);
76198 var posc = posAxis.l2p(lcenter) + bPosPxOffset;
76199 var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset;
76200 var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset;
76201 var posw0 = posAxis.l2p(lcenter - wdPos) + bPosPxOffset;
76202 var posw1 = posAxis.l2p(lcenter + wdPos) + bPosPxOffset;
76203 var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset;
76204 var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset;
76205 var q1 = valAxis.c2p(d.q1, true);
76206 var q3 = valAxis.c2p(d.q3, true);
76207 // make sure median isn't identical to either of the
76208 // quartiles, so we can see it
76209 var m = Lib.constrain(
76210 valAxis.c2p(d.med, true),
76211 Math.min(q1, q3) + 1, Math.max(q1, q3) - 1
76212 );
76213
76214 // for compatibility with box, violin, and candlestick
76215 // perhaps we should put this into cd0.t instead so it's more explicit,
76216 // but what we have now is:
76217 // - box always has d.lf, but boxpoints can be anything
76218 // - violin has d.lf and should always use it (boxpoints is undefined)
76219 // - candlestick has only min/max
76220 var useExtremes = (d.lf === undefined) || (trace.boxpoints === false);
76221 var lf = valAxis.c2p(useExtremes ? d.min : d.lf, true);
76222 var uf = valAxis.c2p(useExtremes ? d.max : d.uf, true);
76223 var ln = valAxis.c2p(d.ln, true);
76224 var un = valAxis.c2p(d.un, true);
76225
76226 if(trace.orientation === 'h') {
76227 d3.select(this).attr('d',
76228 'M' + m + ',' + posm0 + 'V' + posm1 + // median line
76229 'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge
76230 (notched ? 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : '') + // top notched edge
76231 'H' + q3 + // end of the top edge
76232 'V' + pos0 + // right edge
76233 (notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge
76234 'Z' + // end of the box
76235 'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers
76236 ((whiskerWidth === 0) ? '' : // whisker caps
76237 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1));
76238 } else {
76239 d3.select(this).attr('d',
76240 'M' + posm0 + ',' + m + 'H' + posm1 + // median line
76241 'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box
76242 (notched ? 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : '') + // notched right edge
76243 'V' + q3 + // end of the right edge
76244 'H' + pos0 + // bottom of the box
76245 (notched ? 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : '') + // notched left edge
76246 'Z' + // end of the box
76247 'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers
76248 ((whiskerWidth === 0) ? '' : // whisker caps
76249 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1));
76250 }
76251 });
76252}
76253
76254function plotPoints(sel, axes, trace, t) {
76255 var xa = axes.x;
76256 var ya = axes.y;
76257 var bdPos = t.bdPos;
76258 var bPos = t.bPos;
76259
76260 // to support violin points
76261 var mode = trace.boxpoints || trace.points;
76262
76263 // repeatable pseudo-random number generator
76264 Lib.seedPseudoRandom();
76265
76266 // since box plot points get an extra level of nesting, each
76267 // box needs the trace styling info
76268 var fn = function(d) {
76269 d.forEach(function(v) {
76270 v.t = t;
76271 v.trace = trace;
76272 });
76273 return d;
76274 };
76275
76276 var gPoints = sel.selectAll('g.points')
76277 .data(mode ? fn : []);
76278
76279 gPoints.enter().append('g')
76280 .attr('class', 'points');
76281
76282 gPoints.exit().remove();
76283
76284 var paths = gPoints.selectAll('path')
76285 .data(function(d) {
76286 var i;
76287 var pts = d.pts2;
76288
76289 // normally use IQR, but if this is 0 or too small, use max-min
76290 var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1);
76291 var minSpread = typicalSpread * 1e-9;
76292 var spreadLimit = typicalSpread * JITTERSPREAD;
76293 var jitterFactors = [];
76294 var maxJitterFactor = 0;
76295 var newJitter;
76296
76297 // dynamic jitter
76298 if(trace.jitter) {
76299 if(typicalSpread === 0) {
76300 // edge case of no spread at all: fall back to max jitter
76301 maxJitterFactor = 1;
76302 jitterFactors = new Array(pts.length);
76303 for(i = 0; i < pts.length; i++) {
76304 jitterFactors[i] = 1;
76305 }
76306 } else {
76307 for(i = 0; i < pts.length; i++) {
76308 var i0 = Math.max(0, i - JITTERCOUNT);
76309 var pmin = pts[i0].v;
76310 var i1 = Math.min(pts.length - 1, i + JITTERCOUNT);
76311 var pmax = pts[i1].v;
76312
76313 if(mode !== 'all') {
76314 if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf);
76315 else pmin = Math.max(pmin, d.uf);
76316 }
76317
76318 var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0;
76319 jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1);
76320
76321 jitterFactors.push(jitterFactor);
76322 maxJitterFactor = Math.max(jitterFactor, maxJitterFactor);
76323 }
76324 }
76325 newJitter = trace.jitter * 2 / (maxJitterFactor || 1);
76326 }
76327
76328 // fills in 'x' and 'y' in calcdata 'pts' item
76329 for(i = 0; i < pts.length; i++) {
76330 var pt = pts[i];
76331 var v = pt.v;
76332
76333 var jitterOffset = trace.jitter ?
76334 (newJitter * jitterFactors[i] * (Lib.pseudoRandom() - 0.5)) :
76335 0;
76336
76337 var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset);
76338
76339 if(trace.orientation === 'h') {
76340 pt.y = posPx;
76341 pt.x = v;
76342 } else {
76343 pt.x = posPx;
76344 pt.y = v;
76345 }
76346
76347 // tag suspected outliers
76348 if(mode === 'suspectedoutliers' && v < d.uo && v > d.lo) {
76349 pt.so = true;
76350 }
76351 }
76352
76353 return pts;
76354 });
76355
76356 paths.enter().append('path')
76357 .classed('point', true);
76358
76359 paths.exit().remove();
76360
76361 paths.call(Drawing.translatePoints, xa, ya);
76362}
76363
76364function plotBoxMean(sel, axes, trace, t) {
76365 var posAxis = axes.pos;
76366 var valAxis = axes.val;
76367 var bPos = t.bPos;
76368 var bPosPxOffset = t.bPosPxOffset || 0;
76369
76370 // to support violin mean lines
76371 var mode = trace.boxmean || (trace.meanline || {}).visible;
76372
76373 // to support for one-sided box
76374 var bdPos0;
76375 var bdPos1;
76376 if(Array.isArray(t.bdPos)) {
76377 bdPos0 = t.bdPos[0];
76378 bdPos1 = t.bdPos[1];
76379 } else {
76380 bdPos0 = t.bdPos;
76381 bdPos1 = t.bdPos;
76382 }
76383
76384 var paths = sel.selectAll('path.mean').data((
76385 (trace.type === 'box' && trace.boxmean) ||
76386 (trace.type === 'violin' && trace.box.visible && trace.meanline.visible)
76387 ) ? Lib.identity : []);
76388
76389 paths.enter().append('path')
76390 .attr('class', 'mean')
76391 .style({
76392 fill: 'none',
76393 'vector-effect': 'non-scaling-stroke'
76394 });
76395
76396 paths.exit().remove();
76397
76398 paths.each(function(d) {
76399 var lcenter = posAxis.c2l(d.pos + bPos, true);
76400 var posc = posAxis.l2p(lcenter) + bPosPxOffset;
76401 var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset;
76402 var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset;
76403 var m = valAxis.c2p(d.mean, true);
76404 var sl = valAxis.c2p(d.mean - d.sd, true);
76405 var sh = valAxis.c2p(d.mean + d.sd, true);
76406
76407 if(trace.orientation === 'h') {
76408 d3.select(this).attr('d',
76409 'M' + m + ',' + pos0 + 'V' + pos1 +
76410 (mode === 'sd' ?
76411 'm0,0L' + sl + ',' + posc + 'L' + m + ',' + pos0 + 'L' + sh + ',' + posc + 'Z' :
76412 '')
76413 );
76414 } else {
76415 d3.select(this).attr('d',
76416 'M' + pos0 + ',' + m + 'H' + pos1 +
76417 (mode === 'sd' ?
76418 'm0,0L' + posc + ',' + sl + 'L' + pos0 + ',' + m + 'L' + posc + ',' + sh + 'Z' :
76419 '')
76420 );
76421 }
76422 });
76423}
76424
76425module.exports = {
76426 plot: plot,
76427 plotBoxAndWhiskers: plotBoxAndWhiskers,
76428 plotPoints: plotPoints,
76429 plotBoxMean: plotBoxMean
76430};
76431
76432},{"../../components/drawing":74,"../../lib":178,"d3":16}],306:[function(_dereq_,module,exports){
76433/**
76434* Copyright 2012-2020, Plotly, Inc.
76435* All rights reserved.
76436*
76437* This source code is licensed under the MIT license found in the
76438* LICENSE file in the root directory of this source tree.
76439*/
76440
76441'use strict';
76442
76443module.exports = function selectPoints(searchInfo, selectionTester) {
76444 var cd = searchInfo.cd;
76445 var xa = searchInfo.xaxis;
76446 var ya = searchInfo.yaxis;
76447 var selection = [];
76448 var i, j;
76449
76450 if(selectionTester === false) {
76451 for(i = 0; i < cd.length; i++) {
76452 for(j = 0; j < (cd[i].pts || []).length; j++) {
76453 // clear selection
76454 cd[i].pts[j].selected = 0;
76455 }
76456 }
76457 } else {
76458 for(i = 0; i < cd.length; i++) {
76459 for(j = 0; j < (cd[i].pts || []).length; j++) {
76460 var pt = cd[i].pts[j];
76461 var x = xa.c2p(pt.x);
76462 var y = ya.c2p(pt.y);
76463
76464 if(selectionTester.contains([x, y], null, pt.i, searchInfo)) {
76465 selection.push({
76466 pointNumber: pt.i,
76467 x: xa.c2d(pt.x),
76468 y: ya.c2d(pt.y)
76469 });
76470 pt.selected = 1;
76471 } else {
76472 pt.selected = 0;
76473 }
76474 }
76475 }
76476 }
76477
76478 return selection;
76479};
76480
76481},{}],307:[function(_dereq_,module,exports){
76482/**
76483* Copyright 2012-2020, Plotly, Inc.
76484* All rights reserved.
76485*
76486* This source code is licensed under the MIT license found in the
76487* LICENSE file in the root directory of this source tree.
76488*/
76489
76490'use strict';
76491
76492var d3 = _dereq_('d3');
76493var Color = _dereq_('../../components/color');
76494var Drawing = _dereq_('../../components/drawing');
76495
76496function style(gd, cd, sel) {
76497 var s = sel ? sel : d3.select(gd).selectAll('g.trace.boxes');
76498
76499 s.style('opacity', function(d) { return d[0].trace.opacity; });
76500
76501 s.each(function(d) {
76502 var el = d3.select(this);
76503 var trace = d[0].trace;
76504 var lineWidth = trace.line.width;
76505
76506 function styleBox(boxSel, lineWidth, lineColor, fillColor) {
76507 boxSel.style('stroke-width', lineWidth + 'px')
76508 .call(Color.stroke, lineColor)
76509 .call(Color.fill, fillColor);
76510 }
76511
76512 var allBoxes = el.selectAll('path.box');
76513
76514 if(trace.type === 'candlestick') {
76515 allBoxes.each(function(boxData) {
76516 if(boxData.empty) return;
76517
76518 var thisBox = d3.select(this);
76519 var container = trace[boxData.dir]; // dir = 'increasing' or 'decreasing'
76520 styleBox(thisBox, container.line.width, container.line.color, container.fillcolor);
76521 // TODO: custom selection style for candlesticks
76522 thisBox.style('opacity', trace.selectedpoints && !boxData.selected ? 0.3 : 1);
76523 });
76524 } else {
76525 styleBox(allBoxes, lineWidth, trace.line.color, trace.fillcolor);
76526 el.selectAll('path.mean')
76527 .style({
76528 'stroke-width': lineWidth,
76529 'stroke-dasharray': (2 * lineWidth) + 'px,' + lineWidth + 'px'
76530 })
76531 .call(Color.stroke, trace.line.color);
76532
76533 var pts = el.selectAll('path.point');
76534 Drawing.pointStyle(pts, trace, gd);
76535 }
76536 });
76537}
76538
76539function styleOnSelect(gd, cd, sel) {
76540 var trace = cd[0].trace;
76541 var pts = sel.selectAll('path.point');
76542
76543 if(trace.selectedpoints) {
76544 Drawing.selectedPointStyle(pts, trace);
76545 } else {
76546 Drawing.pointStyle(pts, trace, gd);
76547 }
76548}
76549
76550module.exports = {
76551 style: style,
76552 styleOnSelect: styleOnSelect
76553};
76554
76555},{"../../components/color":52,"../../components/drawing":74,"d3":16}],308:[function(_dereq_,module,exports){
76556/**
76557* Copyright 2012-2020, Plotly, Inc.
76558* All rights reserved.
76559*
76560* This source code is licensed under the MIT license found in the
76561* LICENSE file in the root directory of this source tree.
76562*/
76563
76564'use strict';
76565
76566var heatmapAttrs = _dereq_('../heatmap/attributes');
76567var scatterAttrs = _dereq_('../scatter/attributes');
76568var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
76569var dash = _dereq_('../../components/drawing/attributes').dash;
76570var fontAttrs = _dereq_('../../plots/font_attributes');
76571var extendFlat = _dereq_('../../lib/extend').extendFlat;
76572
76573var filterOps = _dereq_('../../constants/filter_ops');
76574var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
76575var INTERVAL_OPS = filterOps.INTERVAL_OPS;
76576
76577var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK;
76578
76579var scatterLineAttrs = scatterAttrs.line;
76580
76581module.exports = extendFlat({
76582 z: heatmapAttrs.z,
76583 x: heatmapAttrs.x,
76584 x0: heatmapAttrs.x0,
76585 dx: heatmapAttrs.dx,
76586 y: heatmapAttrs.y,
76587 y0: heatmapAttrs.y0,
76588 dy: heatmapAttrs.dy,
76589 text: heatmapAttrs.text,
76590 hovertext: heatmapAttrs.hovertext,
76591 transpose: heatmapAttrs.transpose,
76592 xtype: heatmapAttrs.xtype,
76593 ytype: heatmapAttrs.ytype,
76594 zhoverformat: heatmapAttrs.zhoverformat,
76595 hovertemplate: heatmapAttrs.hovertemplate,
76596 hoverongaps: heatmapAttrs.hoverongaps,
76597 connectgaps: extendFlat({}, heatmapAttrs.connectgaps, {
76598
76599 }),
76600
76601 fillcolor: {
76602 valType: 'color',
76603
76604 editType: 'calc',
76605
76606 },
76607
76608 autocontour: {
76609 valType: 'boolean',
76610 dflt: true,
76611
76612 editType: 'calc',
76613 impliedEdits: {
76614 'contours.start': undefined,
76615 'contours.end': undefined,
76616 'contours.size': undefined
76617 },
76618
76619 },
76620 ncontours: {
76621 valType: 'integer',
76622 dflt: 15,
76623 min: 1,
76624
76625 editType: 'calc',
76626
76627 },
76628
76629 contours: {
76630 type: {
76631 valType: 'enumerated',
76632 values: ['levels', 'constraint'],
76633 dflt: 'levels',
76634
76635 editType: 'calc',
76636
76637 },
76638 start: {
76639 valType: 'number',
76640 dflt: null,
76641
76642 editType: 'plot',
76643 impliedEdits: {'^autocontour': false},
76644
76645 },
76646 end: {
76647 valType: 'number',
76648 dflt: null,
76649
76650 editType: 'plot',
76651 impliedEdits: {'^autocontour': false},
76652
76653 },
76654 size: {
76655 valType: 'number',
76656 dflt: null,
76657 min: 0,
76658
76659 editType: 'plot',
76660 impliedEdits: {'^autocontour': false},
76661
76662 },
76663 coloring: {
76664 valType: 'enumerated',
76665 values: ['fill', 'heatmap', 'lines', 'none'],
76666 dflt: 'fill',
76667
76668 editType: 'calc',
76669
76670 },
76671 showlines: {
76672 valType: 'boolean',
76673 dflt: true,
76674
76675 editType: 'plot',
76676
76677 },
76678 showlabels: {
76679 valType: 'boolean',
76680 dflt: false,
76681
76682 editType: 'plot',
76683
76684 },
76685 labelfont: fontAttrs({
76686 editType: 'plot',
76687 colorEditType: 'style',
76688
76689 }),
76690 labelformat: {
76691 valType: 'string',
76692 dflt: '',
76693
76694 editType: 'plot',
76695
76696 },
76697 operation: {
76698 valType: 'enumerated',
76699 values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS),
76700
76701 dflt: '=',
76702 editType: 'calc',
76703
76704 },
76705 value: {
76706 valType: 'any',
76707 dflt: 0,
76708
76709 editType: 'calc',
76710
76711 },
76712 editType: 'calc',
76713 impliedEdits: {'autocontour': false}
76714 },
76715
76716 line: {
76717 color: extendFlat({}, scatterLineAttrs.color, {
76718 editType: 'style+colorbars',
76719
76720 }),
76721 width: {
76722 valType: 'number',
76723 min: 0,
76724
76725 editType: 'style+colorbars',
76726
76727 },
76728 dash: dash,
76729 smoothing: extendFlat({}, scatterLineAttrs.smoothing, {
76730
76731 }),
76732 editType: 'plot'
76733 }
76734},
76735 colorScaleAttrs('', {
76736 cLetter: 'z',
76737 autoColorDflt: false,
76738 editTypeOverride: 'calc'
76739 })
76740);
76741
76742},{"../../components/colorscale/attributes":59,"../../components/drawing/attributes":73,"../../constants/docs":155,"../../constants/filter_ops":156,"../../lib/extend":173,"../../plots/font_attributes":250,"../heatmap/attributes":330,"../scatter/attributes":389}],309:[function(_dereq_,module,exports){
76743/**
76744* Copyright 2012-2020, Plotly, Inc.
76745* All rights reserved.
76746*
76747* This source code is licensed under the MIT license found in the
76748* LICENSE file in the root directory of this source tree.
76749*/
76750
76751'use strict';
76752
76753var Colorscale = _dereq_('../../components/colorscale');
76754
76755var heatmapCalc = _dereq_('../heatmap/calc');
76756var setContours = _dereq_('./set_contours');
76757var endPlus = _dereq_('./end_plus');
76758
76759// most is the same as heatmap calc, then adjust it
76760// though a few things inside heatmap calc still look for
76761// contour maps, because the makeBoundArray calls are too entangled
76762module.exports = function calc(gd, trace) {
76763 var cd = heatmapCalc(gd, trace);
76764
76765 var zOut = cd[0].z;
76766 setContours(trace, zOut);
76767
76768 var contours = trace.contours;
76769 var cOpts = Colorscale.extractOpts(trace);
76770 var cVals;
76771
76772 if(contours.coloring === 'heatmap' && cOpts.auto && trace.autocontour === false) {
76773 var start = contours.start;
76774 var end = endPlus(contours);
76775 var cs = contours.size || 1;
76776 var nc = Math.floor((end - start) / cs) + 1;
76777
76778 if(!isFinite(cs)) {
76779 cs = 1;
76780 nc = 1;
76781 }
76782
76783 var min0 = start - cs / 2;
76784 var max0 = min0 + nc * cs;
76785 cVals = [min0, max0];
76786 } else {
76787 cVals = zOut;
76788 }
76789
76790 Colorscale.calc(gd, trace, {vals: cVals, cLetter: 'z'});
76791
76792 return cd;
76793};
76794
76795},{"../../components/colorscale":64,"../heatmap/calc":331,"./end_plus":319,"./set_contours":327}],310:[function(_dereq_,module,exports){
76796/**
76797* Copyright 2012-2020, Plotly, Inc.
76798* All rights reserved.
76799*
76800* This source code is licensed under the MIT license found in the
76801* LICENSE file in the root directory of this source tree.
76802*/
76803
76804'use strict';
76805
76806module.exports = function(pathinfo, contours) {
76807 var pi0 = pathinfo[0];
76808 var z = pi0.z;
76809 var i;
76810
76811 switch(contours.type) {
76812 case 'levels':
76813 // Why (just) use z[0][0] and z[0][1]?
76814 //
76815 // N.B. using boundaryMin instead of edgeVal2 here makes the
76816 // `contour_scatter` mock fail
76817 var edgeVal2 = Math.min(z[0][0], z[0][1]);
76818
76819 for(i = 0; i < pathinfo.length; i++) {
76820 var pi = pathinfo[i];
76821 pi.prefixBoundary = !pi.edgepaths.length &&
76822 (edgeVal2 > pi.level || pi.starts.length && edgeVal2 === pi.level);
76823 }
76824 break;
76825 case 'constraint':
76826 // after convertToConstraints, pathinfo has length=0
76827 pi0.prefixBoundary = false;
76828
76829 // joinAllPaths does enough already when edgepaths are present
76830 if(pi0.edgepaths.length) return;
76831
76832 var na = pi0.x.length;
76833 var nb = pi0.y.length;
76834 var boundaryMax = -Infinity;
76835 var boundaryMin = Infinity;
76836
76837 for(i = 0; i < nb; i++) {
76838 boundaryMin = Math.min(boundaryMin, z[i][0]);
76839 boundaryMin = Math.min(boundaryMin, z[i][na - 1]);
76840 boundaryMax = Math.max(boundaryMax, z[i][0]);
76841 boundaryMax = Math.max(boundaryMax, z[i][na - 1]);
76842 }
76843 for(i = 1; i < na - 1; i++) {
76844 boundaryMin = Math.min(boundaryMin, z[0][i]);
76845 boundaryMin = Math.min(boundaryMin, z[nb - 1][i]);
76846 boundaryMax = Math.max(boundaryMax, z[0][i]);
76847 boundaryMax = Math.max(boundaryMax, z[nb - 1][i]);
76848 }
76849
76850 var contoursValue = contours.value;
76851 var v1, v2;
76852
76853 switch(contours._operation) {
76854 case '>':
76855 if(contoursValue > boundaryMax) {
76856 pi0.prefixBoundary = true;
76857 }
76858 break;
76859 case '<':
76860 if(contoursValue < boundaryMin ||
76861 (pi0.starts.length && contoursValue === boundaryMin)) {
76862 pi0.prefixBoundary = true;
76863 }
76864 break;
76865 case '[]':
76866 v1 = Math.min(contoursValue[0], contoursValue[1]);
76867 v2 = Math.max(contoursValue[0], contoursValue[1]);
76868 if(v2 < boundaryMin || v1 > boundaryMax ||
76869 (pi0.starts.length && v2 === boundaryMin)) {
76870 pi0.prefixBoundary = true;
76871 }
76872 break;
76873 case '][':
76874 v1 = Math.min(contoursValue[0], contoursValue[1]);
76875 v2 = Math.max(contoursValue[0], contoursValue[1]);
76876 if(v1 < boundaryMin && v2 > boundaryMax) {
76877 pi0.prefixBoundary = true;
76878 }
76879 break;
76880 }
76881 break;
76882 }
76883};
76884
76885},{}],311:[function(_dereq_,module,exports){
76886/**
76887* Copyright 2012-2020, Plotly, Inc.
76888* All rights reserved.
76889*
76890* This source code is licensed under the MIT license found in the
76891* LICENSE file in the root directory of this source tree.
76892*/
76893
76894'use strict';
76895
76896var Colorscale = _dereq_('../../components/colorscale');
76897var makeColorMap = _dereq_('./make_color_map');
76898var endPlus = _dereq_('./end_plus');
76899
76900function calc(gd, trace, opts) {
76901 var contours = trace.contours;
76902 var line = trace.line;
76903 var cs = contours.size || 1;
76904 var coloring = contours.coloring;
76905 var colorMap = makeColorMap(trace, {isColorbar: true});
76906
76907 if(coloring === 'heatmap') {
76908 var cOpts = Colorscale.extractOpts(trace);
76909 opts._fillgradient = cOpts.reversescale ?
76910 Colorscale.flipScale(cOpts.colorscale) :
76911 cOpts.colorscale;
76912 opts._zrange = [cOpts.min, cOpts.max];
76913 } else if(coloring === 'fill') {
76914 opts._fillcolor = colorMap;
76915 }
76916
76917 opts._line = {
76918 color: coloring === 'lines' ? colorMap : line.color,
76919 width: contours.showlines !== false ? line.width : 0,
76920 dash: line.dash
76921 };
76922
76923 opts._levels = {
76924 start: contours.start,
76925 end: endPlus(contours),
76926 size: cs
76927 };
76928}
76929
76930module.exports = {
76931 min: 'zmin',
76932 max: 'zmax',
76933 calc: calc
76934};
76935
76936},{"../../components/colorscale":64,"./end_plus":319,"./make_color_map":324}],312:[function(_dereq_,module,exports){
76937/**
76938* Copyright 2012-2020, Plotly, Inc.
76939* All rights reserved.
76940*
76941* This source code is licensed under the MIT license found in the
76942* LICENSE file in the root directory of this source tree.
76943*/
76944
76945'use strict';
76946module.exports = {
76947 // some constants to help with marching squares algorithm
76948 // where does the path start for each index?
76949 BOTTOMSTART: [1, 9, 13, 104, 713],
76950 TOPSTART: [4, 6, 7, 104, 713],
76951 LEFTSTART: [8, 12, 14, 208, 1114],
76952 RIGHTSTART: [2, 3, 11, 208, 1114],
76953
76954 // which way [dx,dy] do we leave a given index?
76955 // saddles are already disambiguated
76956 NEWDELTA: [
76957 null, [-1, 0], [0, -1], [-1, 0],
76958 [1, 0], null, [0, -1], [-1, 0],
76959 [0, 1], [0, 1], null, [0, 1],
76960 [1, 0], [1, 0], [0, -1]
76961 ],
76962
76963 // for each saddle, the first index here is used
76964 // for dx||dy<0, the second for dx||dy>0
76965 CHOOSESADDLE: {
76966 104: [4, 1],
76967 208: [2, 8],
76968 713: [7, 13],
76969 1114: [11, 14]
76970 },
76971
76972 // after one index has been used for a saddle, which do we
76973 // substitute to be used up later?
76974 SADDLEREMAINDER: {1: 4, 2: 8, 4: 1, 7: 13, 8: 2, 11: 14, 13: 7, 14: 11},
76975
76976 // length of a contour, as a multiple of the plot area diagonal, per label
76977 LABELDISTANCE: 2,
76978
76979 // number of contour levels after which we start increasing the number of
76980 // labels we draw. Many contours means they will generally be close
76981 // together, so it will be harder to follow a long way to find a label
76982 LABELINCREASE: 10,
76983
76984 // minimum length of a contour line, as a multiple of the label length,
76985 // at which we draw *any* labels
76986 LABELMIN: 3,
76987
76988 // max number of labels to draw on a single contour path, no matter how long
76989 LABELMAX: 10,
76990
76991 // constants for the label position cost function
76992 LABELOPTIMIZER: {
76993 // weight given to edge proximity
76994 EDGECOST: 1,
76995 // weight given to the angle off horizontal
76996 ANGLECOST: 1,
76997 // weight given to distance from already-placed labels
76998 NEIGHBORCOST: 5,
76999 // cost multiplier for labels on the same level
77000 SAMELEVELFACTOR: 10,
77001 // minimum distance (as a multiple of the label length)
77002 // for labels on the same level
77003 SAMELEVELDISTANCE: 5,
77004 // maximum cost before we won't even place the label
77005 MAXCOST: 100,
77006 // number of evenly spaced points to look at in the first
77007 // iteration of the search
77008 INITIALSEARCHPOINTS: 10,
77009 // number of binary search iterations after the initial wide search
77010 ITERATIONS: 5
77011 }
77012};
77013
77014},{}],313:[function(_dereq_,module,exports){
77015/**
77016* Copyright 2012-2020, Plotly, Inc.
77017* All rights reserved.
77018*
77019* This source code is licensed under the MIT license found in the
77020* LICENSE file in the root directory of this source tree.
77021*/
77022
77023
77024'use strict';
77025var isNumeric = _dereq_('fast-isnumeric');
77026
77027var handleLabelDefaults = _dereq_('./label_defaults');
77028
77029var Color = _dereq_('../../components/color');
77030var addOpacity = Color.addOpacity;
77031var opacity = Color.opacity;
77032
77033var filterOps = _dereq_('../../constants/filter_ops');
77034var CONSTRAINT_REDUCTION = filterOps.CONSTRAINT_REDUCTION;
77035var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
77036
77037module.exports = function handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor, opts) {
77038 var contours = traceOut.contours;
77039 var showLines, lineColor, fillColor;
77040
77041 var operation = coerce('contours.operation');
77042 contours._operation = CONSTRAINT_REDUCTION[operation];
77043
77044 handleConstraintValueDefaults(coerce, contours);
77045
77046 if(operation === '=') {
77047 showLines = contours.showlines = true;
77048 } else {
77049 showLines = coerce('contours.showlines');
77050 fillColor = coerce('fillcolor', addOpacity(
77051 (traceIn.line || {}).color || defaultColor, 0.5
77052 ));
77053 }
77054
77055 if(showLines) {
77056 var lineDfltColor = fillColor && opacity(fillColor) ?
77057 addOpacity(traceOut.fillcolor, 1) :
77058 defaultColor;
77059 lineColor = coerce('line.color', lineDfltColor);
77060 coerce('line.width', 2);
77061 coerce('line.dash');
77062 }
77063
77064 coerce('line.smoothing');
77065
77066 handleLabelDefaults(coerce, layout, lineColor, opts);
77067};
77068
77069function handleConstraintValueDefaults(coerce, contours) {
77070 var zvalue;
77071
77072 if(COMPARISON_OPS2.indexOf(contours.operation) === -1) {
77073 // Requires an array of two numbers:
77074 coerce('contours.value', [0, 1]);
77075
77076 if(!Array.isArray(contours.value)) {
77077 if(isNumeric(contours.value)) {
77078 zvalue = parseFloat(contours.value);
77079 contours.value = [zvalue, zvalue + 1];
77080 }
77081 } else if(contours.value.length > 2) {
77082 contours.value = contours.value.slice(2);
77083 } else if(contours.length === 0) {
77084 contours.value = [0, 1];
77085 } else if(contours.length < 2) {
77086 zvalue = parseFloat(contours.value[0]);
77087 contours.value = [zvalue, zvalue + 1];
77088 } else {
77089 contours.value = [
77090 parseFloat(contours.value[0]),
77091 parseFloat(contours.value[1])
77092 ];
77093 }
77094 } else {
77095 // Requires a single scalar:
77096 coerce('contours.value', 0);
77097
77098 if(!isNumeric(contours.value)) {
77099 if(Array.isArray(contours.value)) {
77100 contours.value = parseFloat(contours.value[0]);
77101 } else {
77102 contours.value = 0;
77103 }
77104 }
77105 }
77106}
77107
77108},{"../../components/color":52,"../../constants/filter_ops":156,"./label_defaults":323,"fast-isnumeric":18}],314:[function(_dereq_,module,exports){
77109/**
77110* Copyright 2012-2020, Plotly, Inc.
77111* All rights reserved.
77112*
77113* This source code is licensed under the MIT license found in the
77114* LICENSE file in the root directory of this source tree.
77115*/
77116
77117'use strict';
77118
77119var filterOps = _dereq_('../../constants/filter_ops');
77120var isNumeric = _dereq_('fast-isnumeric');
77121
77122// This syntax conforms to the existing filter transform syntax, but we don't care
77123// about open vs. closed intervals for simply drawing contours constraints:
77124module.exports = {
77125 '[]': makeRangeSettings('[]'),
77126 '][': makeRangeSettings(']['),
77127 '>': makeInequalitySettings('>'),
77128 '<': makeInequalitySettings('<'),
77129 '=': makeInequalitySettings('=')
77130};
77131
77132// This does not in any way shape or form support calendars. It's adapted from
77133// transforms/filter.js.
77134function coerceValue(operation, value) {
77135 var hasArrayValue = Array.isArray(value);
77136
77137 var coercedValue;
77138
77139 function coerce(value) {
77140 return isNumeric(value) ? (+value) : null;
77141 }
77142
77143 if(filterOps.COMPARISON_OPS2.indexOf(operation) !== -1) {
77144 coercedValue = hasArrayValue ? coerce(value[0]) : coerce(value);
77145 } else if(filterOps.INTERVAL_OPS.indexOf(operation) !== -1) {
77146 coercedValue = hasArrayValue ?
77147 [coerce(value[0]), coerce(value[1])] :
77148 [coerce(value), coerce(value)];
77149 } else if(filterOps.SET_OPS.indexOf(operation) !== -1) {
77150 coercedValue = hasArrayValue ? value.map(coerce) : [coerce(value)];
77151 }
77152
77153 return coercedValue;
77154}
77155
77156// Returns a parabola scaled so that the min/max is either +/- 1 and zero at the two values
77157// provided. The data is mapped by this function when constructing intervals so that it's
77158// very easy to construct contours as normal.
77159function makeRangeSettings(operation) {
77160 return function(value) {
77161 value = coerceValue(operation, value);
77162
77163 // Ensure proper ordering:
77164 var min = Math.min(value[0], value[1]);
77165 var max = Math.max(value[0], value[1]);
77166
77167 return {
77168 start: min,
77169 end: max,
77170 size: max - min
77171 };
77172 };
77173}
77174
77175function makeInequalitySettings(operation) {
77176 return function(value) {
77177 value = coerceValue(operation, value);
77178
77179 return {
77180 start: value,
77181 end: Infinity,
77182 size: Infinity
77183 };
77184 };
77185}
77186
77187},{"../../constants/filter_ops":156,"fast-isnumeric":18}],315:[function(_dereq_,module,exports){
77188/**
77189* Copyright 2012-2020, Plotly, Inc.
77190* All rights reserved.
77191*
77192* This source code is licensed under the MIT license found in the
77193* LICENSE file in the root directory of this source tree.
77194*/
77195
77196'use strict';
77197
77198module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerce2) {
77199 var contourStart = coerce2('contours.start');
77200 var contourEnd = coerce2('contours.end');
77201 var missingEnd = (contourStart === false) || (contourEnd === false);
77202
77203 // normally we only need size if autocontour is off. But contour.calc
77204 // pushes its calculated contour size back to the input trace, so for
77205 // things like restyle that can call supplyDefaults without calc
77206 // after the initial draw, we can just reuse the previous calculation
77207 var contourSize = coerce('contours.size');
77208 var autoContour;
77209
77210 if(missingEnd) autoContour = traceOut.autocontour = true;
77211 else autoContour = coerce('autocontour', false);
77212
77213 if(autoContour || !contourSize) coerce('ncontours');
77214};
77215
77216},{}],316:[function(_dereq_,module,exports){
77217/**
77218* Copyright 2012-2020, Plotly, Inc.
77219* All rights reserved.
77220*
77221* This source code is licensed under the MIT license found in the
77222* LICENSE file in the root directory of this source tree.
77223*/
77224
77225'use strict';
77226
77227var Lib = _dereq_('../../lib');
77228
77229// The contour extraction is great, except it totally fails for constraints because we
77230// need weird range loops and flipped contours instead of the usual format. This function
77231// does some weird manipulation of the extracted pathinfo data such that it magically
77232// draws contours correctly *as* constraints.
77233//
77234// ** I do not know which "weird range loops" the comment above is referring to.
77235module.exports = function(pathinfo, operation) {
77236 var i, pi0, pi1;
77237
77238 var op0 = function(arr) { return arr.reverse(); };
77239 var op1 = function(arr) { return arr; };
77240
77241 switch(operation) {
77242 case '=':
77243 case '<':
77244 return pathinfo;
77245 case '>':
77246 if(pathinfo.length !== 1) {
77247 Lib.warn('Contour data invalid for the specified inequality operation.');
77248 }
77249
77250 // In this case there should be exactly one contour levels in pathinfo.
77251 // We flip all of the data. This will draw the contour as closed.
77252 pi0 = pathinfo[0];
77253
77254 for(i = 0; i < pi0.edgepaths.length; i++) {
77255 pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
77256 }
77257 for(i = 0; i < pi0.paths.length; i++) {
77258 pi0.paths[i] = op0(pi0.paths[i]);
77259 }
77260 for(i = 0; i < pi0.starts.length; i++) {
77261 pi0.starts[i] = op0(pi0.starts[i]);
77262 }
77263
77264 return pathinfo;
77265 case '][':
77266 var tmp = op0;
77267 op0 = op1;
77268 op1 = tmp;
77269 // It's a nice rule, except this definitely *is* what's intended here.
77270 /* eslint-disable: no-fallthrough */
77271 case '[]':
77272 /* eslint-enable: no-fallthrough */
77273 if(pathinfo.length !== 2) {
77274 Lib.warn('Contour data invalid for the specified inequality range operation.');
77275 }
77276
77277 // In this case there should be exactly two contour levels in pathinfo.
77278 // - We concatenate the info into one pathinfo.
77279 // - We must also flip all of the data in the `[]` case.
77280 // This will draw the contours as closed.
77281 pi0 = copyPathinfo(pathinfo[0]);
77282 pi1 = copyPathinfo(pathinfo[1]);
77283
77284 for(i = 0; i < pi0.edgepaths.length; i++) {
77285 pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
77286 }
77287 for(i = 0; i < pi0.paths.length; i++) {
77288 pi0.paths[i] = op0(pi0.paths[i]);
77289 }
77290 for(i = 0; i < pi0.starts.length; i++) {
77291 pi0.starts[i] = op0(pi0.starts[i]);
77292 }
77293
77294 while(pi1.edgepaths.length) {
77295 pi0.edgepaths.push(op1(pi1.edgepaths.shift()));
77296 }
77297 while(pi1.paths.length) {
77298 pi0.paths.push(op1(pi1.paths.shift()));
77299 }
77300 while(pi1.starts.length) {
77301 pi0.starts.push(op1(pi1.starts.shift()));
77302 }
77303
77304 return [pi0];
77305 }
77306};
77307
77308function copyPathinfo(pi) {
77309 return Lib.extendFlat({}, pi, {
77310 edgepaths: Lib.extendDeep([], pi.edgepaths),
77311 paths: Lib.extendDeep([], pi.paths),
77312 starts: Lib.extendDeep([], pi.starts)
77313 });
77314}
77315
77316},{"../../lib":178}],317:[function(_dereq_,module,exports){
77317/**
77318* Copyright 2012-2020, Plotly, Inc.
77319* All rights reserved.
77320*
77321* This source code is licensed under the MIT license found in the
77322* LICENSE file in the root directory of this source tree.
77323*/
77324
77325'use strict';
77326
77327var Lib = _dereq_('../../lib');
77328
77329var handleXYZDefaults = _dereq_('../heatmap/xyz_defaults');
77330var handleConstraintDefaults = _dereq_('./constraint_defaults');
77331var handleContoursDefaults = _dereq_('./contours_defaults');
77332var handleStyleDefaults = _dereq_('./style_defaults');
77333var attributes = _dereq_('./attributes');
77334
77335
77336module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
77337 function coerce(attr, dflt) {
77338 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
77339 }
77340
77341 function coerce2(attr) {
77342 return Lib.coerce2(traceIn, traceOut, attributes, attr);
77343 }
77344
77345 var len = handleXYZDefaults(traceIn, traceOut, coerce, layout);
77346 if(!len) {
77347 traceOut.visible = false;
77348 return;
77349 }
77350
77351 coerce('text');
77352 coerce('hovertext');
77353 coerce('hovertemplate');
77354 coerce('hoverongaps');
77355
77356 var isConstraint = (coerce('contours.type') === 'constraint');
77357 coerce('connectgaps', Lib.isArray1D(traceOut.z));
77358
77359 if(isConstraint) {
77360 handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor);
77361 } else {
77362 handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
77363 handleStyleDefaults(traceIn, traceOut, coerce, layout);
77364 }
77365};
77366
77367},{"../../lib":178,"../heatmap/xyz_defaults":344,"./attributes":308,"./constraint_defaults":313,"./contours_defaults":315,"./style_defaults":329}],318:[function(_dereq_,module,exports){
77368/**
77369* Copyright 2012-2020, Plotly, Inc.
77370* All rights reserved.
77371*
77372* This source code is licensed under the MIT license found in the
77373* LICENSE file in the root directory of this source tree.
77374*/
77375
77376'use strict';
77377
77378var Lib = _dereq_('../../lib');
77379var constraintMapping = _dereq_('./constraint_mapping');
77380var endPlus = _dereq_('./end_plus');
77381
77382module.exports = function emptyPathinfo(contours, plotinfo, cd0) {
77383 var contoursFinal = (contours.type === 'constraint') ?
77384 constraintMapping[contours._operation](contours.value) :
77385 contours;
77386
77387 var cs = contoursFinal.size;
77388 var pathinfo = [];
77389 var end = endPlus(contoursFinal);
77390
77391 var carpet = cd0.trace._carpetTrace;
77392
77393 var basePathinfo = carpet ? {
77394 // store axes so we can convert to px
77395 xaxis: carpet.aaxis,
77396 yaxis: carpet.baxis,
77397 // full data arrays to use for interpolation
77398 x: cd0.a,
77399 y: cd0.b
77400 } : {
77401 xaxis: plotinfo.xaxis,
77402 yaxis: plotinfo.yaxis,
77403 x: cd0.x,
77404 y: cd0.y
77405 };
77406
77407 for(var ci = contoursFinal.start; ci < end; ci += cs) {
77408 pathinfo.push(Lib.extendFlat({
77409 level: ci,
77410 // all the cells with nontrivial marching index
77411 crossings: {},
77412 // starting points on the edges of the lattice for each contour
77413 starts: [],
77414 // all unclosed paths (may have less items than starts,
77415 // if a path is closed by rounding)
77416 edgepaths: [],
77417 // all closed paths
77418 paths: [],
77419 z: cd0.z,
77420 smoothing: cd0.trace.line.smoothing
77421 }, basePathinfo));
77422
77423 if(pathinfo.length > 1000) {
77424 Lib.warn('Too many contours, clipping at 1000', contours);
77425 break;
77426 }
77427 }
77428 return pathinfo;
77429};
77430
77431},{"../../lib":178,"./constraint_mapping":314,"./end_plus":319}],319:[function(_dereq_,module,exports){
77432/**
77433* Copyright 2012-2020, Plotly, Inc.
77434* All rights reserved.
77435*
77436* This source code is licensed under the MIT license found in the
77437* LICENSE file in the root directory of this source tree.
77438*/
77439
77440
77441'use strict';
77442
77443/*
77444 * tiny helper to move the end of the contours a little to prevent
77445 * losing the last contour to rounding errors
77446 */
77447module.exports = function endPlus(contours) {
77448 return contours.end + contours.size / 1e6;
77449};
77450
77451},{}],320:[function(_dereq_,module,exports){
77452/**
77453* Copyright 2012-2020, Plotly, Inc.
77454* All rights reserved.
77455*
77456* This source code is licensed under the MIT license found in the
77457* LICENSE file in the root directory of this source tree.
77458*/
77459
77460'use strict';
77461
77462var Lib = _dereq_('../../lib');
77463var constants = _dereq_('./constants');
77464
77465module.exports = function findAllPaths(pathinfo, xtol, ytol) {
77466 var cnt,
77467 startLoc,
77468 i,
77469 pi,
77470 j;
77471
77472 // Default just passes these values through as they were before:
77473 xtol = xtol || 0.01;
77474 ytol = ytol || 0.01;
77475
77476 for(i = 0; i < pathinfo.length; i++) {
77477 pi = pathinfo[i];
77478
77479 for(j = 0; j < pi.starts.length; j++) {
77480 startLoc = pi.starts[j];
77481 makePath(pi, startLoc, 'edge', xtol, ytol);
77482 }
77483
77484 cnt = 0;
77485 while(Object.keys(pi.crossings).length && cnt < 10000) {
77486 cnt++;
77487 startLoc = Object.keys(pi.crossings)[0].split(',').map(Number);
77488 makePath(pi, startLoc, undefined, xtol, ytol);
77489 }
77490 if(cnt === 10000) Lib.log('Infinite loop in contour?');
77491 }
77492};
77493
77494function equalPts(pt1, pt2, xtol, ytol) {
77495 return Math.abs(pt1[0] - pt2[0]) < xtol &&
77496 Math.abs(pt1[1] - pt2[1]) < ytol;
77497}
77498
77499// distance in index units - uses the 3rd and 4th items in points
77500function ptDist(pt1, pt2) {
77501 var dx = pt1[2] - pt2[2];
77502 var dy = pt1[3] - pt2[3];
77503 return Math.sqrt(dx * dx + dy * dy);
77504}
77505
77506function makePath(pi, loc, edgeflag, xtol, ytol) {
77507 var locStr = loc.join(',');
77508 var mi = pi.crossings[locStr];
77509 var marchStep = getStartStep(mi, edgeflag, loc);
77510 // start by going backward a half step and finding the crossing point
77511 var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])];
77512 var m = pi.z.length;
77513 var n = pi.z[0].length;
77514 var startLoc = loc.slice();
77515 var startStep = marchStep.slice();
77516 var cnt;
77517
77518 // now follow the path
77519 for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops
77520 if(mi > 20) {
77521 mi = constants.CHOOSESADDLE[mi][(marchStep[0] || marchStep[1]) < 0 ? 0 : 1];
77522 pi.crossings[locStr] = constants.SADDLEREMAINDER[mi];
77523 } else {
77524 delete pi.crossings[locStr];
77525 }
77526
77527 marchStep = constants.NEWDELTA[mi];
77528 if(!marchStep) {
77529 Lib.log('Found bad marching index:', mi, loc, pi.level);
77530 break;
77531 }
77532
77533 // find the crossing a half step forward, and then take the full step
77534 pts.push(getInterpPx(pi, loc, marchStep));
77535 loc[0] += marchStep[0];
77536 loc[1] += marchStep[1];
77537 locStr = loc.join(',');
77538
77539 // don't include the same point multiple times
77540 if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop();
77541
77542 var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) ||
77543 (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2));
77544
77545 var closedLoop = loc[0] === startLoc[0] && loc[1] === startLoc[1] &&
77546 marchStep[0] === startStep[0] && marchStep[1] === startStep[1];
77547
77548 // have we completed a loop, or reached an edge?
77549 if((closedLoop) || (edgeflag && atEdge)) break;
77550
77551 mi = pi.crossings[locStr];
77552 }
77553
77554 if(cnt === 10000) {
77555 Lib.log('Infinite loop in contour?');
77556 }
77557 var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol);
77558 var totaldist = 0;
77559 var distThresholdFactor = 0.2 * pi.smoothing;
77560 var alldists = [];
77561 var cropstart = 0;
77562 var distgroup, cnt2, cnt3, newpt, ptcnt, ptavg, thisdist,
77563 i, j, edgepathi, edgepathj;
77564
77565 /*
77566 * Check for points that are too close together (<1/5 the average dist
77567 * *in grid index units* (important for log axes and nonuniform grids),
77568 * less if less smoothed) and just take the center (or avg of center 2).
77569 * This cuts down on funny behavior when a point is very close to a
77570 * contour level.
77571 */
77572 for(cnt = 1; cnt < pts.length; cnt++) {
77573 thisdist = ptDist(pts[cnt], pts[cnt - 1]);
77574 totaldist += thisdist;
77575 alldists.push(thisdist);
77576 }
77577
77578 var distThreshold = totaldist / alldists.length * distThresholdFactor;
77579
77580 function getpt(i) { return pts[i % pts.length]; }
77581
77582 for(cnt = pts.length - 2; cnt >= cropstart; cnt--) {
77583 distgroup = alldists[cnt];
77584 if(distgroup < distThreshold) {
77585 cnt3 = 0;
77586 for(cnt2 = cnt - 1; cnt2 >= cropstart; cnt2--) {
77587 if(distgroup + alldists[cnt2] < distThreshold) {
77588 distgroup += alldists[cnt2];
77589 } else break;
77590 }
77591
77592 // closed path with close points wrapping around the boundary?
77593 if(closedpath && cnt === pts.length - 2) {
77594 for(cnt3 = 0; cnt3 < cnt2; cnt3++) {
77595 if(distgroup + alldists[cnt3] < distThreshold) {
77596 distgroup += alldists[cnt3];
77597 } else break;
77598 }
77599 }
77600 ptcnt = cnt - cnt2 + cnt3 + 1;
77601 ptavg = Math.floor((cnt + cnt2 + cnt3 + 2) / 2);
77602
77603 // either endpoint included: keep the endpoint
77604 if(!closedpath && cnt === pts.length - 2) newpt = pts[pts.length - 1];
77605 else if(!closedpath && cnt2 === -1) newpt = pts[0];
77606
77607 // odd # of points - just take the central one
77608 else if(ptcnt % 2) newpt = getpt(ptavg);
77609
77610 // even # of pts - average central two
77611 else {
77612 newpt = [(getpt(ptavg)[0] + getpt(ptavg + 1)[0]) / 2,
77613 (getpt(ptavg)[1] + getpt(ptavg + 1)[1]) / 2];
77614 }
77615
77616 pts.splice(cnt2 + 1, cnt - cnt2 + 1, newpt);
77617 cnt = cnt2 + 1;
77618 if(cnt3) cropstart = cnt3;
77619 if(closedpath) {
77620 if(cnt === pts.length - 2) pts[cnt3] = pts[pts.length - 1];
77621 else if(cnt === 0) pts[pts.length - 1] = pts[0];
77622 }
77623 }
77624 }
77625 pts.splice(0, cropstart);
77626
77627 // done with the index parts - remove them so path generation works right
77628 // because it depends on only having [xpx, ypx]
77629 for(cnt = 0; cnt < pts.length; cnt++) pts[cnt].length = 2;
77630
77631 // don't return single-point paths (ie all points were the same
77632 // so they got deleted?)
77633 if(pts.length < 2) return;
77634 else if(closedpath) {
77635 pts.pop();
77636 pi.paths.push(pts);
77637 } else {
77638 if(!edgeflag) {
77639 Lib.log('Unclosed interior contour?',
77640 pi.level, startLoc.join(','), pts.join('L'));
77641 }
77642
77643 // edge path - does it start where an existing edge path ends, or vice versa?
77644 var merged = false;
77645 for(i = 0; i < pi.edgepaths.length; i++) {
77646 edgepathi = pi.edgepaths[i];
77647 if(!merged && equalPts(edgepathi[0], pts[pts.length - 1], xtol, ytol)) {
77648 pts.pop();
77649 merged = true;
77650
77651 // now does it ALSO meet the end of another (or the same) path?
77652 var doublemerged = false;
77653 for(j = 0; j < pi.edgepaths.length; j++) {
77654 edgepathj = pi.edgepaths[j];
77655 if(equalPts(edgepathj[edgepathj.length - 1], pts[0], xtol, ytol)) {
77656 doublemerged = true;
77657 pts.shift();
77658 pi.edgepaths.splice(i, 1);
77659 if(j === i) {
77660 // the path is now closed
77661 pi.paths.push(pts.concat(edgepathj));
77662 } else {
77663 if(j > i) j--;
77664 pi.edgepaths[j] = edgepathj.concat(pts, edgepathi);
77665 }
77666 break;
77667 }
77668 }
77669 if(!doublemerged) {
77670 pi.edgepaths[i] = pts.concat(edgepathi);
77671 }
77672 }
77673 }
77674 for(i = 0; i < pi.edgepaths.length; i++) {
77675 if(merged) break;
77676 edgepathi = pi.edgepaths[i];
77677 if(equalPts(edgepathi[edgepathi.length - 1], pts[0], xtol, ytol)) {
77678 pts.shift();
77679 pi.edgepaths[i] = edgepathi.concat(pts);
77680 merged = true;
77681 }
77682 }
77683
77684 if(!merged) pi.edgepaths.push(pts);
77685 }
77686}
77687
77688// special function to get the marching step of the
77689// first point in the path (leading to loc)
77690function getStartStep(mi, edgeflag, loc) {
77691 var dx = 0;
77692 var dy = 0;
77693 if(mi > 20 && edgeflag) {
77694 // these saddles start at +/- x
77695 if(mi === 208 || mi === 1114) {
77696 // if we're starting at the left side, we must be going right
77697 dx = loc[0] === 0 ? 1 : -1;
77698 } else {
77699 // if we're starting at the bottom, we must be going up
77700 dy = loc[1] === 0 ? 1 : -1;
77701 }
77702 } else if(constants.BOTTOMSTART.indexOf(mi) !== -1) dy = 1;
77703 else if(constants.LEFTSTART.indexOf(mi) !== -1) dx = 1;
77704 else if(constants.TOPSTART.indexOf(mi) !== -1) dy = -1;
77705 else dx = -1;
77706 return [dx, dy];
77707}
77708
77709/*
77710 * Find the pixel coordinates of a particular crossing
77711 *
77712 * @param {object} pi: the pathinfo object at this level
77713 * @param {array} loc: the grid index [x, y] of the crossing
77714 * @param {array} step: the direction [dx, dy] we're moving on the grid
77715 *
77716 * @return {array} [xpx, ypx, xi, yi]: the first two are the pixel location,
77717 * the next two are the interpolated grid indices, which we use for
77718 * distance calculations to delete points that are too close together.
77719 * This is important when the grid is nonuniform (and most dramatically when
77720 * we're on log axes and include invalid (0 or negative) values.
77721 * It's crucial to delete these extra two before turning an array of these
77722 * points into a path, because those routines require length-2 points.
77723 */
77724function getInterpPx(pi, loc, step) {
77725 var locx = loc[0] + Math.max(step[0], 0);
77726 var locy = loc[1] + Math.max(step[1], 0);
77727 var zxy = pi.z[locy][locx];
77728 var xa = pi.xaxis;
77729 var ya = pi.yaxis;
77730
77731 if(step[1]) {
77732 var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy);
77733
77734 return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true),
77735 ya.c2p(pi.y[locy], true),
77736 locx + dx, locy];
77737 } else {
77738 var dy = (pi.level - zxy) / (pi.z[locy + 1][locx] - zxy);
77739 return [xa.c2p(pi.x[locx], true),
77740 ya.c2p((1 - dy) * pi.y[locy] + dy * pi.y[locy + 1], true),
77741 locx, locy + dy];
77742 }
77743}
77744
77745},{"../../lib":178,"./constants":312}],321:[function(_dereq_,module,exports){
77746/**
77747* Copyright 2012-2020, Plotly, Inc.
77748* All rights reserved.
77749*
77750* This source code is licensed under the MIT license found in the
77751* LICENSE file in the root directory of this source tree.
77752*/
77753
77754
77755'use strict';
77756
77757var Color = _dereq_('../../components/color');
77758
77759var heatmapHoverPoints = _dereq_('../heatmap/hover');
77760
77761module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer) {
77762 var hoverData = heatmapHoverPoints(pointData, xval, yval, hovermode, hoverLayer, true);
77763
77764 if(hoverData) {
77765 hoverData.forEach(function(hoverPt) {
77766 var trace = hoverPt.trace;
77767 if(trace.contours.type === 'constraint') {
77768 if(trace.fillcolor && Color.opacity(trace.fillcolor)) {
77769 hoverPt.color = Color.addOpacity(trace.fillcolor, 1);
77770 } else if(trace.contours.showlines && Color.opacity(trace.line.color)) {
77771 hoverPt.color = Color.addOpacity(trace.line.color, 1);
77772 }
77773 }
77774 });
77775 }
77776
77777 return hoverData;
77778};
77779
77780},{"../../components/color":52,"../heatmap/hover":337}],322:[function(_dereq_,module,exports){
77781/**
77782* Copyright 2012-2020, Plotly, Inc.
77783* All rights reserved.
77784*
77785* This source code is licensed under the MIT license found in the
77786* LICENSE file in the root directory of this source tree.
77787*/
77788
77789'use strict';
77790
77791module.exports = {
77792 attributes: _dereq_('./attributes'),
77793 supplyDefaults: _dereq_('./defaults'),
77794 calc: _dereq_('./calc'),
77795 plot: _dereq_('./plot').plot,
77796 style: _dereq_('./style'),
77797 colorbar: _dereq_('./colorbar'),
77798 hoverPoints: _dereq_('./hover'),
77799
77800 moduleType: 'trace',
77801 name: 'contour',
77802 basePlotModule: _dereq_('../../plots/cartesian'),
77803 categories: ['cartesian', 'svg', '2dMap', 'contour', 'showLegend'],
77804 meta: {
77805
77806 }
77807};
77808
77809},{"../../plots/cartesian":235,"./attributes":308,"./calc":309,"./colorbar":311,"./defaults":317,"./hover":321,"./plot":326,"./style":328}],323:[function(_dereq_,module,exports){
77810/**
77811* Copyright 2012-2020, Plotly, Inc.
77812* All rights reserved.
77813*
77814* This source code is licensed under the MIT license found in the
77815* LICENSE file in the root directory of this source tree.
77816*/
77817
77818
77819'use strict';
77820
77821var Lib = _dereq_('../../lib');
77822
77823module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) {
77824 if(!opts) opts = {};
77825 var showLabels = coerce('contours.showlabels');
77826 if(showLabels) {
77827 var globalFont = layout.font;
77828 Lib.coerceFont(coerce, 'contours.labelfont', {
77829 family: globalFont.family,
77830 size: globalFont.size,
77831 color: lineColor
77832 });
77833 coerce('contours.labelformat');
77834 }
77835
77836 if(opts.hasHover !== false) coerce('zhoverformat');
77837};
77838
77839},{"../../lib":178}],324:[function(_dereq_,module,exports){
77840/**
77841* Copyright 2012-2020, Plotly, Inc.
77842* All rights reserved.
77843*
77844* This source code is licensed under the MIT license found in the
77845* LICENSE file in the root directory of this source tree.
77846*/
77847
77848'use strict';
77849
77850var d3 = _dereq_('d3');
77851
77852var Colorscale = _dereq_('../../components/colorscale');
77853var endPlus = _dereq_('./end_plus');
77854
77855module.exports = function makeColorMap(trace) {
77856 var contours = trace.contours;
77857 var start = contours.start;
77858 var end = endPlus(contours);
77859 var cs = contours.size || 1;
77860 var nc = Math.floor((end - start) / cs) + 1;
77861 var extra = contours.coloring === 'lines' ? 0 : 1;
77862 var cOpts = Colorscale.extractOpts(trace);
77863
77864 if(!isFinite(cs)) {
77865 cs = 1;
77866 nc = 1;
77867 }
77868
77869 var scl = cOpts.reversescale ?
77870 Colorscale.flipScale(cOpts.colorscale) :
77871 cOpts.colorscale;
77872
77873 var len = scl.length;
77874 var domain = new Array(len);
77875 var range = new Array(len);
77876
77877 var si, i;
77878
77879 if(contours.coloring === 'heatmap') {
77880 var zmin0 = cOpts.min;
77881 var zmax0 = cOpts.max;
77882
77883 for(i = 0; i < len; i++) {
77884 si = scl[i];
77885 domain[i] = si[0] * (zmax0 - zmin0) + zmin0;
77886 range[i] = si[1];
77887 }
77888
77889 // do the contours extend beyond the colorscale?
77890 // if so, extend the colorscale with constants
77891 var zRange = d3.extent([
77892 zmin0,
77893 zmax0,
77894 contours.start,
77895 contours.start + cs * (nc - 1)
77896 ]);
77897 var zmin = zRange[zmin0 < zmax0 ? 0 : 1];
77898 var zmax = zRange[zmin0 < zmax0 ? 1 : 0];
77899
77900 if(zmin !== zmin0) {
77901 domain.splice(0, 0, zmin);
77902 range.splice(0, 0, range[0]);
77903 }
77904
77905 if(zmax !== zmax0) {
77906 domain.push(zmax);
77907 range.push(range[range.length - 1]);
77908 }
77909 } else {
77910 for(i = 0; i < len; i++) {
77911 si = scl[i];
77912 domain[i] = (si[0] * (nc + extra - 1) - (extra / 2)) * cs + start;
77913 range[i] = si[1];
77914 }
77915 }
77916
77917 return Colorscale.makeColorScaleFunc(
77918 {domain: domain, range: range},
77919 {noNumericCheck: true}
77920 );
77921};
77922
77923},{"../../components/colorscale":64,"./end_plus":319,"d3":16}],325:[function(_dereq_,module,exports){
77924/**
77925* Copyright 2012-2020, Plotly, Inc.
77926* All rights reserved.
77927*
77928* This source code is licensed under the MIT license found in the
77929* LICENSE file in the root directory of this source tree.
77930*/
77931
77932'use strict';
77933
77934var constants = _dereq_('./constants');
77935
77936// Calculate all the marching indices, for ALL levels at once.
77937// since we want to be exhaustive we'll check for contour crossings
77938// at every intersection, rather than just following a path
77939// TODO: shorten the inner loop to only the relevant levels
77940module.exports = function makeCrossings(pathinfo) {
77941 var z = pathinfo[0].z;
77942 var m = z.length;
77943 var n = z[0].length; // we already made sure z isn't ragged in interp2d
77944 var twoWide = m === 2 || n === 2;
77945 var xi;
77946 var yi;
77947 var startIndices;
77948 var ystartIndices;
77949 var label;
77950 var corners;
77951 var mi;
77952 var pi;
77953 var i;
77954
77955 for(yi = 0; yi < m - 1; yi++) {
77956 ystartIndices = [];
77957 if(yi === 0) ystartIndices = ystartIndices.concat(constants.BOTTOMSTART);
77958 if(yi === m - 2) ystartIndices = ystartIndices.concat(constants.TOPSTART);
77959
77960 for(xi = 0; xi < n - 1; xi++) {
77961 startIndices = ystartIndices.slice();
77962 if(xi === 0) startIndices = startIndices.concat(constants.LEFTSTART);
77963 if(xi === n - 2) startIndices = startIndices.concat(constants.RIGHTSTART);
77964
77965 label = xi + ',' + yi;
77966 corners = [[z[yi][xi], z[yi][xi + 1]],
77967 [z[yi + 1][xi], z[yi + 1][xi + 1]]];
77968 for(i = 0; i < pathinfo.length; i++) {
77969 pi = pathinfo[i];
77970 mi = getMarchingIndex(pi.level, corners);
77971 if(!mi) continue;
77972
77973 pi.crossings[label] = mi;
77974 if(startIndices.indexOf(mi) !== -1) {
77975 pi.starts.push([xi, yi]);
77976 if(twoWide && startIndices.indexOf(mi,
77977 startIndices.indexOf(mi) + 1) !== -1) {
77978 // the same square has starts from opposite sides
77979 // it's not possible to have starts on opposite edges
77980 // of a corner, only a start and an end...
77981 // but if the array is only two points wide (either way)
77982 // you can have starts on opposite sides.
77983 pi.starts.push([xi, yi]);
77984 }
77985 }
77986 }
77987 }
77988 }
77989};
77990
77991// modified marching squares algorithm,
77992// so we disambiguate the saddle points from the start
77993// and we ignore the cases with no crossings
77994// the index I'm using is based on:
77995// http://en.wikipedia.org/wiki/Marching_squares
77996// except that the saddles bifurcate and I represent them
77997// as the decimal combination of the two appropriate
77998// non-saddle indices
77999function getMarchingIndex(val, corners) {
78000 var mi = (corners[0][0] > val ? 0 : 1) +
78001 (corners[0][1] > val ? 0 : 2) +
78002 (corners[1][1] > val ? 0 : 4) +
78003 (corners[1][0] > val ? 0 : 8);
78004 if(mi === 5 || mi === 10) {
78005 var avg = (corners[0][0] + corners[0][1] +
78006 corners[1][0] + corners[1][1]) / 4;
78007 // two peaks with a big valley
78008 if(val > avg) return (mi === 5) ? 713 : 1114;
78009 // two valleys with a big ridge
78010 return (mi === 5) ? 104 : 208;
78011 }
78012 return (mi === 15) ? 0 : mi;
78013}
78014
78015},{"./constants":312}],326:[function(_dereq_,module,exports){
78016/**
78017* Copyright 2012-2020, Plotly, Inc.
78018* All rights reserved.
78019*
78020* This source code is licensed under the MIT license found in the
78021* LICENSE file in the root directory of this source tree.
78022*/
78023
78024
78025'use strict';
78026
78027var d3 = _dereq_('d3');
78028
78029var Lib = _dereq_('../../lib');
78030var Drawing = _dereq_('../../components/drawing');
78031var Colorscale = _dereq_('../../components/colorscale');
78032var svgTextUtils = _dereq_('../../lib/svg_text_utils');
78033var Axes = _dereq_('../../plots/cartesian/axes');
78034var setConvert = _dereq_('../../plots/cartesian/set_convert');
78035
78036var heatmapPlot = _dereq_('../heatmap/plot');
78037var makeCrossings = _dereq_('./make_crossings');
78038var findAllPaths = _dereq_('./find_all_paths');
78039var emptyPathinfo = _dereq_('./empty_pathinfo');
78040var convertToConstraints = _dereq_('./convert_to_constraints');
78041var closeBoundaries = _dereq_('./close_boundaries');
78042var constants = _dereq_('./constants');
78043var costConstants = constants.LABELOPTIMIZER;
78044
78045exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
78046 var xa = plotinfo.xaxis;
78047 var ya = plotinfo.yaxis;
78048
78049 Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) {
78050 var plotGroup = d3.select(this);
78051 var cd0 = cd[0];
78052 var trace = cd0.trace;
78053 var x = cd0.x;
78054 var y = cd0.y;
78055 var contours = trace.contours;
78056 var pathinfo = emptyPathinfo(contours, plotinfo, cd0);
78057
78058 // use a heatmap to fill - draw it behind the lines
78059 var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring');
78060 var cdheatmaps = [];
78061 if(contours.coloring === 'heatmap') {
78062 cdheatmaps = [cd];
78063 }
78064 heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer);
78065
78066 makeCrossings(pathinfo);
78067 findAllPaths(pathinfo);
78068
78069 var leftedge = xa.c2p(x[0], true);
78070 var rightedge = xa.c2p(x[x.length - 1], true);
78071 var bottomedge = ya.c2p(y[0], true);
78072 var topedge = ya.c2p(y[y.length - 1], true);
78073 var perimeter = [
78074 [leftedge, topedge],
78075 [rightedge, topedge],
78076 [rightedge, bottomedge],
78077 [leftedge, bottomedge]
78078 ];
78079
78080 var fillPathinfo = pathinfo;
78081 if(contours.type === 'constraint') {
78082 // N.B. this also mutates pathinfo
78083 fillPathinfo = convertToConstraints(pathinfo, contours._operation);
78084 }
78085
78086 // draw everything
78087 makeBackground(plotGroup, perimeter, contours);
78088 makeFills(plotGroup, fillPathinfo, perimeter, contours);
78089 makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours);
78090 clipGaps(plotGroup, plotinfo, gd, cd0, perimeter);
78091 });
78092};
78093
78094function makeBackground(plotgroup, perimeter, contours) {
78095 var bggroup = Lib.ensureSingle(plotgroup, 'g', 'contourbg');
78096
78097 var bgfill = bggroup.selectAll('path')
78098 .data(contours.coloring === 'fill' ? [0] : []);
78099 bgfill.enter().append('path');
78100 bgfill.exit().remove();
78101 bgfill
78102 .attr('d', 'M' + perimeter.join('L') + 'Z')
78103 .style('stroke', 'none');
78104}
78105
78106function makeFills(plotgroup, pathinfo, perimeter, contours) {
78107 var hasFills = contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '=');
78108 var boundaryPath = 'M' + perimeter.join('L') + 'Z';
78109
78110 // fills prefixBoundary in pathinfo items
78111 if(hasFills) {
78112 closeBoundaries(pathinfo, contours);
78113 }
78114
78115 var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill');
78116
78117 var fillitems = fillgroup.selectAll('path').data(hasFills ? pathinfo : []);
78118 fillitems.enter().append('path');
78119 fillitems.exit().remove();
78120 fillitems.each(function(pi) {
78121 // join all paths for this level together into a single path
78122 // first follow clockwise around the perimeter to close any open paths
78123 // if the whole perimeter is above this level, start with a path
78124 // enclosing the whole thing. With all that, the parity should mean
78125 // that we always fill everything above the contour, nothing below
78126 var fullpath = (pi.prefixBoundary ? boundaryPath : '') +
78127 joinAllPaths(pi, perimeter);
78128
78129 if(!fullpath) {
78130 d3.select(this).remove();
78131 } else {
78132 d3.select(this)
78133 .attr('d', fullpath)
78134 .style('stroke', 'none');
78135 }
78136 });
78137}
78138
78139function joinAllPaths(pi, perimeter) {
78140 var fullpath = '';
78141 var i = 0;
78142 var startsleft = pi.edgepaths.map(function(v, i) { return i; });
78143 var newloop = true;
78144 var endpt;
78145 var newendpt;
78146 var cnt;
78147 var nexti;
78148 var possiblei;
78149 var addpath;
78150
78151 function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < 0.01; }
78152 function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < 0.01; }
78153 function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < 0.01; }
78154 function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < 0.01; }
78155
78156 while(startsleft.length) {
78157 addpath = Drawing.smoothopen(pi.edgepaths[i], pi.smoothing);
78158 fullpath += newloop ? addpath : addpath.replace(/^M/, 'L');
78159 startsleft.splice(startsleft.indexOf(i), 1);
78160 endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1];
78161 nexti = -1;
78162
78163 // now loop through sides, moving our endpoint until we find a new start
78164 for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops
78165 if(!endpt) {
78166 Lib.log('Missing end?', i, pi);
78167 break;
78168 }
78169
78170 if(istop(endpt) && !isright(endpt)) newendpt = perimeter[1]; // right top
78171 else if(isleft(endpt)) newendpt = perimeter[0]; // left top
78172 else if(isbottom(endpt)) newendpt = perimeter[3]; // right bottom
78173 else if(isright(endpt)) newendpt = perimeter[2]; // left bottom
78174
78175 for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) {
78176 var ptNew = pi.edgepaths[possiblei][0];
78177 // is ptNew on the (horz. or vert.) segment from endpt to newendpt?
78178 if(Math.abs(endpt[0] - newendpt[0]) < 0.01) {
78179 if(Math.abs(endpt[0] - ptNew[0]) < 0.01 &&
78180 (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) {
78181 newendpt = ptNew;
78182 nexti = possiblei;
78183 }
78184 } else if(Math.abs(endpt[1] - newendpt[1]) < 0.01) {
78185 if(Math.abs(endpt[1] - ptNew[1]) < 0.01 &&
78186 (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) {
78187 newendpt = ptNew;
78188 nexti = possiblei;
78189 }
78190 } else {
78191 Lib.log('endpt to newendpt is not vert. or horz.',
78192 endpt, newendpt, ptNew);
78193 }
78194 }
78195
78196 endpt = newendpt;
78197
78198 if(nexti >= 0) break;
78199 fullpath += 'L' + newendpt;
78200 }
78201
78202 if(nexti === pi.edgepaths.length) {
78203 Lib.log('unclosed perimeter path');
78204 break;
78205 }
78206
78207 i = nexti;
78208
78209 // if we closed back on a loop we already included,
78210 // close it and start a new loop
78211 newloop = (startsleft.indexOf(i) === -1);
78212 if(newloop) {
78213 i = startsleft[0];
78214 fullpath += 'Z';
78215 }
78216 }
78217
78218 // finally add the interior paths
78219 for(i = 0; i < pi.paths.length; i++) {
78220 fullpath += Drawing.smoothclosed(pi.paths[i], pi.smoothing);
78221 }
78222
78223 return fullpath;
78224}
78225
78226function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours) {
78227 var lineContainer = Lib.ensureSingle(plotgroup, 'g', 'contourlines');
78228 var showLines = contours.showlines !== false;
78229 var showLabels = contours.showlabels;
78230 var clipLinesForLabels = showLines && showLabels;
78231
78232 // Even if we're not going to show lines, we need to create them
78233 // if we're showing labels, because the fill paths include the perimeter
78234 // so can't be used to position the labels correctly.
78235 // In this case we'll remove the lines after making the labels.
78236 var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo);
78237
78238 var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);
78239
78240 var labelGroup = plotgroup.selectAll('g.contourlabels')
78241 .data(showLabels ? [0] : []);
78242
78243 labelGroup.exit().remove();
78244
78245 labelGroup.enter().append('g')
78246 .classed('contourlabels', true);
78247
78248 if(showLabels) {
78249 var labelClipPathData = [];
78250 var labelData = [];
78251
78252 // invalidate the getTextLocation cache in case paths changed
78253 Lib.clearLocationCache();
78254
78255 var contourFormat = exports.labelFormatter(gd, cd0);
78256
78257 var dummyText = Drawing.tester.append('text')
78258 .attr('data-notex', 1)
78259 .call(Drawing.font, contours.labelfont);
78260
78261 var xa = pathinfo[0].xaxis;
78262 var ya = pathinfo[0].yaxis;
78263 var xLen = xa._length;
78264 var yLen = ya._length;
78265 var xRng = xa.range;
78266 var yRng = ya.range;
78267 var xMin = Lib.aggNums(Math.min, null, cd0.x);
78268 var xMax = Lib.aggNums(Math.max, null, cd0.x);
78269 var yMin = Lib.aggNums(Math.min, null, cd0.y);
78270 var yMax = Lib.aggNums(Math.max, null, cd0.y);
78271 var x0 = Math.max(xa.c2p(xMin, true), 0);
78272 var x1 = Math.min(xa.c2p(xMax, true), xLen);
78273 var y0 = Math.max(ya.c2p(yMax, true), 0);
78274 var y1 = Math.min(ya.c2p(yMin, true), yLen);
78275
78276 // visible bounds of the contour trace (and the midpoints, to
78277 // help with cost calculations)
78278 var bounds = {};
78279
78280 if(xRng[0] < xRng[1]) {
78281 bounds.left = x0;
78282 bounds.right = x1;
78283 } else {
78284 bounds.left = x1;
78285 bounds.right = x0;
78286 }
78287
78288 if(yRng[0] < yRng[1]) {
78289 bounds.top = y0;
78290 bounds.bottom = y1;
78291 } else {
78292 bounds.top = y1;
78293 bounds.bottom = y0;
78294 }
78295
78296 bounds.middle = (bounds.top + bounds.bottom) / 2;
78297 bounds.center = (bounds.left + bounds.right) / 2;
78298
78299 labelClipPathData.push([
78300 [bounds.left, bounds.top],
78301 [bounds.right, bounds.top],
78302 [bounds.right, bounds.bottom],
78303 [bounds.left, bounds.bottom]
78304 ]);
78305
78306 var plotDiagonal = Math.sqrt(xLen * xLen + yLen * yLen);
78307
78308 // the path length to use to scale the number of labels to draw:
78309 var normLength = constants.LABELDISTANCE * plotDiagonal /
78310 Math.max(1, pathinfo.length / constants.LABELINCREASE);
78311
78312 linegroup.each(function(d) {
78313 var textOpts = exports.calcTextOpts(d.level, contourFormat, dummyText, gd);
78314
78315 d3.select(this).selectAll('path').each(function() {
78316 var path = this;
78317 var pathBounds = Lib.getVisibleSegment(path, bounds, textOpts.height / 2);
78318 if(!pathBounds) return;
78319
78320 if(pathBounds.len < (textOpts.width + textOpts.height) * constants.LABELMIN) return;
78321
78322 var maxLabels = Math.min(Math.ceil(pathBounds.len / normLength),
78323 constants.LABELMAX);
78324
78325 for(var i = 0; i < maxLabels; i++) {
78326 var loc = exports.findBestTextLocation(path, pathBounds, textOpts,
78327 labelData, bounds);
78328
78329 if(!loc) break;
78330
78331 exports.addLabelData(loc, textOpts, labelData, labelClipPathData);
78332 }
78333 });
78334 });
78335
78336 dummyText.remove();
78337
78338 exports.drawLabels(labelGroup, labelData, gd, lineClip,
78339 clipLinesForLabels ? labelClipPathData : null);
78340 }
78341
78342 if(showLabels && !showLines) linegroup.remove();
78343}
78344
78345exports.createLines = function(lineContainer, makeLines, pathinfo) {
78346 var smoothing = pathinfo[0].smoothing;
78347
78348 var linegroup = lineContainer.selectAll('g.contourlevel')
78349 .data(makeLines ? pathinfo : []);
78350
78351 linegroup.exit().remove();
78352 linegroup.enter().append('g')
78353 .classed('contourlevel', true);
78354
78355 if(makeLines) {
78356 // pedgepaths / ppaths are used by contourcarpet, for the paths transformed from a/b to x/y
78357 // edgepaths / paths are used by contour since it's in x/y from the start
78358 var opencontourlines = linegroup.selectAll('path.openline')
78359 .data(function(d) { return d.pedgepaths || d.edgepaths; });
78360
78361 opencontourlines.exit().remove();
78362 opencontourlines.enter().append('path')
78363 .classed('openline', true);
78364
78365 opencontourlines
78366 .attr('d', function(d) {
78367 return Drawing.smoothopen(d, smoothing);
78368 })
78369 .style('stroke-miterlimit', 1)
78370 .style('vector-effect', 'non-scaling-stroke');
78371
78372 var closedcontourlines = linegroup.selectAll('path.closedline')
78373 .data(function(d) { return d.ppaths || d.paths; });
78374
78375 closedcontourlines.exit().remove();
78376 closedcontourlines.enter().append('path')
78377 .classed('closedline', true);
78378
78379 closedcontourlines
78380 .attr('d', function(d) {
78381 return Drawing.smoothclosed(d, smoothing);
78382 })
78383 .style('stroke-miterlimit', 1)
78384 .style('vector-effect', 'non-scaling-stroke');
78385 }
78386
78387 return linegroup;
78388};
78389
78390exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) {
78391 var clips = gd._fullLayout._clips;
78392 var clipId = clipLinesForLabels ? ('clipline' + uid) : null;
78393
78394 var lineClip = clips.selectAll('#' + clipId)
78395 .data(clipLinesForLabels ? [0] : []);
78396 lineClip.exit().remove();
78397
78398 lineClip.enter().append('clipPath')
78399 .classed('contourlineclip', true)
78400 .attr('id', clipId);
78401
78402 Drawing.setClipUrl(lineContainer, clipId, gd);
78403
78404 return lineClip;
78405};
78406
78407exports.labelFormatter = function(gd, cd0) {
78408 var fullLayout = gd._fullLayout;
78409 var trace = cd0.trace;
78410 var contours = trace.contours;
78411
78412 var formatAxis = {
78413 type: 'linear',
78414 _id: 'ycontour',
78415 showexponent: 'all',
78416 exponentformat: 'B'
78417 };
78418
78419 if(contours.labelformat) {
78420 formatAxis.tickformat = contours.labelformat;
78421 setConvert(formatAxis, fullLayout);
78422 } else {
78423 var cOpts = Colorscale.extractOpts(trace);
78424 if(cOpts && cOpts.colorbar && cOpts.colorbar._axis) {
78425 formatAxis = cOpts.colorbar._axis;
78426 } else {
78427 if(contours.type === 'constraint') {
78428 var value = contours.value;
78429 if(Array.isArray(value)) {
78430 formatAxis.range = [value[0], value[value.length - 1]];
78431 } else formatAxis.range = [value, value];
78432 } else {
78433 formatAxis.range = [contours.start, contours.end];
78434 formatAxis.nticks = (contours.end - contours.start) / contours.size;
78435 }
78436
78437 if(formatAxis.range[0] === formatAxis.range[1]) {
78438 formatAxis.range[1] += formatAxis.range[0] || 1;
78439 }
78440 if(!formatAxis.nticks) formatAxis.nticks = 1000;
78441
78442 setConvert(formatAxis, fullLayout);
78443 Axes.prepTicks(formatAxis);
78444 formatAxis._tmin = null;
78445 formatAxis._tmax = null;
78446 }
78447 }
78448
78449 return function(v) { return Axes.tickText(formatAxis, v).text; };
78450};
78451
78452exports.calcTextOpts = function(level, contourFormat, dummyText, gd) {
78453 var text = contourFormat(level);
78454 dummyText.text(text)
78455 .call(svgTextUtils.convertToTspans, gd);
78456
78457 var el = dummyText.node();
78458 var bBox = Drawing.bBox(el, true);
78459
78460 return {
78461 text: text,
78462 width: bBox.width,
78463 height: bBox.height,
78464 fontSize: +(el.style['font-size'].replace('px', '')),
78465 level: level,
78466 dy: (bBox.top + bBox.bottom) / 2
78467 };
78468};
78469
78470exports.findBestTextLocation = function(path, pathBounds, textOpts, labelData, plotBounds) {
78471 var textWidth = textOpts.width;
78472
78473 var p0, dp, pMax, pMin, loc;
78474 if(pathBounds.isClosed) {
78475 dp = pathBounds.len / costConstants.INITIALSEARCHPOINTS;
78476 p0 = pathBounds.min + dp / 2;
78477 pMax = pathBounds.max;
78478 } else {
78479 dp = (pathBounds.len - textWidth) / (costConstants.INITIALSEARCHPOINTS + 1);
78480 p0 = pathBounds.min + dp + textWidth / 2;
78481 pMax = pathBounds.max - (dp + textWidth) / 2;
78482 }
78483
78484 var cost = Infinity;
78485 for(var j = 0; j < costConstants.ITERATIONS; j++) {
78486 for(var p = p0; p < pMax; p += dp) {
78487 var newLocation = Lib.getTextLocation(path, pathBounds.total, p, textWidth);
78488 var newCost = locationCost(newLocation, textOpts, labelData, plotBounds);
78489 if(newCost < cost) {
78490 cost = newCost;
78491 loc = newLocation;
78492 pMin = p;
78493 }
78494 }
78495 if(cost > costConstants.MAXCOST * 2) break;
78496
78497 // subsequent iterations just look half steps away from the
78498 // best we found in the previous iteration
78499 if(j) dp /= 2;
78500 p0 = pMin - dp / 2;
78501 pMax = p0 + dp * 1.5;
78502 }
78503 if(cost <= costConstants.MAXCOST) return loc;
78504};
78505
78506/*
78507 * locationCost: a cost function for label locations
78508 * composed of three kinds of penalty:
78509 * - for open paths, being close to the end of the path
78510 * - the angle away from horizontal
78511 * - being too close to already placed neighbors
78512 */
78513function locationCost(loc, textOpts, labelData, bounds) {
78514 var halfWidth = textOpts.width / 2;
78515 var halfHeight = textOpts.height / 2;
78516 var x = loc.x;
78517 var y = loc.y;
78518 var theta = loc.theta;
78519 var dx = Math.cos(theta) * halfWidth;
78520 var dy = Math.sin(theta) * halfWidth;
78521
78522 // cost for being near an edge
78523 var normX = ((x > bounds.center) ? (bounds.right - x) : (x - bounds.left)) /
78524 (dx + Math.abs(Math.sin(theta) * halfHeight));
78525 var normY = ((y > bounds.middle) ? (bounds.bottom - y) : (y - bounds.top)) /
78526 (Math.abs(dy) + Math.cos(theta) * halfHeight);
78527 if(normX < 1 || normY < 1) return Infinity;
78528 var cost = costConstants.EDGECOST * (1 / (normX - 1) + 1 / (normY - 1));
78529
78530 // cost for not being horizontal
78531 cost += costConstants.ANGLECOST * theta * theta;
78532
78533 // cost for being close to other labels
78534 var x1 = x - dx;
78535 var y1 = y - dy;
78536 var x2 = x + dx;
78537 var y2 = y + dy;
78538 for(var i = 0; i < labelData.length; i++) {
78539 var labeli = labelData[i];
78540 var dxd = Math.cos(labeli.theta) * labeli.width / 2;
78541 var dyd = Math.sin(labeli.theta) * labeli.width / 2;
78542 var dist = Lib.segmentDistance(
78543 x1, y1,
78544 x2, y2,
78545 labeli.x - dxd, labeli.y - dyd,
78546 labeli.x + dxd, labeli.y + dyd
78547 ) * 2 / (textOpts.height + labeli.height);
78548
78549 var sameLevel = labeli.level === textOpts.level;
78550 var distOffset = sameLevel ? costConstants.SAMELEVELDISTANCE : 1;
78551
78552 if(dist <= distOffset) return Infinity;
78553
78554 var distFactor = costConstants.NEIGHBORCOST *
78555 (sameLevel ? costConstants.SAMELEVELFACTOR : 1);
78556
78557 cost += distFactor / (dist - distOffset);
78558 }
78559
78560 return cost;
78561}
78562
78563exports.addLabelData = function(loc, textOpts, labelData, labelClipPathData) {
78564 var fontSize = textOpts.fontSize;
78565 var w = textOpts.width + fontSize / 3;
78566 var h = Math.max(0, textOpts.height - fontSize / 3);
78567
78568 var x = loc.x;
78569 var y = loc.y;
78570 var theta = loc.theta;
78571
78572 var sin = Math.sin(theta);
78573 var cos = Math.cos(theta);
78574
78575 var rotateXY = function(dx, dy) {
78576 return [
78577 x + dx * cos - dy * sin,
78578 y + dx * sin + dy * cos
78579 ];
78580 };
78581
78582 var bBoxPts = [
78583 rotateXY(-w / 2, -h / 2),
78584 rotateXY(-w / 2, h / 2),
78585 rotateXY(w / 2, h / 2),
78586 rotateXY(w / 2, -h / 2)
78587 ];
78588
78589 labelData.push({
78590 text: textOpts.text,
78591 x: x,
78592 y: y,
78593 dy: textOpts.dy,
78594 theta: theta,
78595 level: textOpts.level,
78596 width: w,
78597 height: h
78598 });
78599
78600 labelClipPathData.push(bBoxPts);
78601};
78602
78603exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPathData) {
78604 var labels = labelGroup.selectAll('text')
78605 .data(labelData, function(d) {
78606 return d.text + ',' + d.x + ',' + d.y + ',' + d.theta;
78607 });
78608
78609 labels.exit().remove();
78610
78611 labels.enter().append('text')
78612 .attr({
78613 'data-notex': 1,
78614 'text-anchor': 'middle'
78615 })
78616 .each(function(d) {
78617 var x = d.x + Math.sin(d.theta) * d.dy;
78618 var y = d.y - Math.cos(d.theta) * d.dy;
78619 d3.select(this)
78620 .text(d.text)
78621 .attr({
78622 x: x,
78623 y: y,
78624 transform: 'rotate(' + (180 * d.theta / Math.PI) + ' ' + x + ' ' + y + ')'
78625 })
78626 .call(svgTextUtils.convertToTspans, gd);
78627 });
78628
78629 if(labelClipPathData) {
78630 var clipPath = '';
78631 for(var i = 0; i < labelClipPathData.length; i++) {
78632 clipPath += 'M' + labelClipPathData[i].join('L') + 'Z';
78633 }
78634
78635 var lineClipPath = Lib.ensureSingle(lineClip, 'path', '');
78636 lineClipPath.attr('d', clipPath);
78637 }
78638};
78639
78640function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
78641 var trace = cd0.trace;
78642 var clips = gd._fullLayout._clips;
78643 var clipId = 'clip' + trace.uid;
78644
78645 var clipPath = clips.selectAll('#' + clipId)
78646 .data(trace.connectgaps ? [] : [0]);
78647 clipPath.enter().append('clipPath')
78648 .classed('contourclip', true)
78649 .attr('id', clipId);
78650 clipPath.exit().remove();
78651
78652 if(trace.connectgaps === false) {
78653 var clipPathInfo = {
78654 // fraction of the way from missing to present point
78655 // to draw the boundary.
78656 // if you make this 1 (or 1-epsilon) then a point in
78657 // a sea of missing data will disappear entirely.
78658 level: 0.9,
78659 crossings: {},
78660 starts: [],
78661 edgepaths: [],
78662 paths: [],
78663 xaxis: plotinfo.xaxis,
78664 yaxis: plotinfo.yaxis,
78665 x: cd0.x,
78666 y: cd0.y,
78667 // 0 = no data, 1 = data
78668 z: makeClipMask(cd0),
78669 smoothing: 0
78670 };
78671
78672 makeCrossings([clipPathInfo]);
78673 findAllPaths([clipPathInfo]);
78674 closeBoundaries([clipPathInfo], {type: 'levels'});
78675
78676 var path = Lib.ensureSingle(clipPath, 'path', '');
78677 path.attr('d',
78678 (clipPathInfo.prefixBoundary ? 'M' + perimeter.join('L') + 'Z' : '') +
78679 joinAllPaths(clipPathInfo, perimeter)
78680 );
78681 } else clipId = null;
78682
78683 Drawing.setClipUrl(plotGroup, clipId, gd);
78684}
78685
78686function makeClipMask(cd0) {
78687 var empties = cd0.trace._emptypoints;
78688 var z = [];
78689 var m = cd0.z.length;
78690 var n = cd0.z[0].length;
78691 var i;
78692 var row = [];
78693 var emptyPoint;
78694
78695 for(i = 0; i < n; i++) row.push(1);
78696 for(i = 0; i < m; i++) z.push(row.slice());
78697 for(i = 0; i < empties.length; i++) {
78698 emptyPoint = empties[i];
78699 z[emptyPoint[0]][emptyPoint[1]] = 0;
78700 }
78701 // save this mask to determine whether to show this data in hover
78702 cd0.zmask = z;
78703 return z;
78704}
78705
78706},{"../../components/colorscale":64,"../../components/drawing":74,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/cartesian/axes":222,"../../plots/cartesian/set_convert":242,"../heatmap/plot":341,"./close_boundaries":310,"./constants":312,"./convert_to_constraints":316,"./empty_pathinfo":318,"./find_all_paths":320,"./make_crossings":325,"d3":16}],327:[function(_dereq_,module,exports){
78707/**
78708* Copyright 2012-2020, Plotly, Inc.
78709* All rights reserved.
78710*
78711* This source code is licensed under the MIT license found in the
78712* LICENSE file in the root directory of this source tree.
78713*/
78714
78715'use strict';
78716
78717var Axes = _dereq_('../../plots/cartesian/axes');
78718var Lib = _dereq_('../../lib');
78719
78720module.exports = function setContours(trace, vals) {
78721 var contours = trace.contours;
78722
78723 // check if we need to auto-choose contour levels
78724 if(trace.autocontour) {
78725 // N.B. do not try to use coloraxis cmin/cmax,
78726 // these values here are meant to remain "per-trace" for now
78727 var zmin = trace.zmin;
78728 var zmax = trace.zmax;
78729 if(trace.zauto || zmin === undefined) {
78730 zmin = Lib.aggNums(Math.min, null, vals);
78731 }
78732 if(trace.zauto || zmax === undefined) {
78733 zmax = Lib.aggNums(Math.max, null, vals);
78734 }
78735
78736 var dummyAx = autoContours(zmin, zmax, trace.ncontours);
78737 contours.size = dummyAx.dtick;
78738 contours.start = Axes.tickFirst(dummyAx);
78739 dummyAx.range.reverse();
78740 contours.end = Axes.tickFirst(dummyAx);
78741
78742 if(contours.start === zmin) contours.start += contours.size;
78743 if(contours.end === zmax) contours.end -= contours.size;
78744
78745 // if you set a small ncontours, *and* the ends are exactly on zmin/zmax
78746 // there's an edge case where start > end now. Make sure there's at least
78747 // one meaningful contour, put it midway between the crossed values
78748 if(contours.start > contours.end) {
78749 contours.start = contours.end = (contours.start + contours.end) / 2;
78750 }
78751
78752 // copy auto-contour info back to the source data.
78753 // previously we copied the whole contours object back, but that had
78754 // other info (coloring, showlines) that should be left to supplyDefaults
78755 if(!trace._input.contours) trace._input.contours = {};
78756 Lib.extendFlat(trace._input.contours, {
78757 start: contours.start,
78758 end: contours.end,
78759 size: contours.size
78760 });
78761 trace._input.autocontour = true;
78762 } else if(contours.type !== 'constraint') {
78763 // sanity checks on manually-supplied start/end/size
78764 var start = contours.start;
78765 var end = contours.end;
78766 var inputContours = trace._input.contours;
78767
78768 if(start > end) {
78769 contours.start = inputContours.start = end;
78770 end = contours.end = inputContours.end = start;
78771 start = contours.start;
78772 }
78773
78774 if(!(contours.size > 0)) {
78775 var sizeOut;
78776 if(start === end) sizeOut = 1;
78777 else sizeOut = autoContours(start, end, trace.ncontours).dtick;
78778
78779 inputContours.size = contours.size = sizeOut;
78780 }
78781 }
78782};
78783
78784
78785/*
78786 * autoContours: make a dummy axis object with dtick we can use
78787 * as contours.size, and if needed we can use Axes.tickFirst
78788 * with this axis object to calculate the start and end too
78789 *
78790 * start: the value to start the contours at
78791 * end: the value to end at (must be > start)
78792 * ncontours: max number of contours to make, like roughDTick
78793 *
78794 * returns: an axis object
78795 */
78796function autoContours(start, end, ncontours) {
78797 var dummyAx = {
78798 type: 'linear',
78799 range: [start, end]
78800 };
78801
78802 Axes.autoTicks(
78803 dummyAx,
78804 (end - start) / (ncontours || 15)
78805 );
78806
78807 return dummyAx;
78808}
78809
78810},{"../../lib":178,"../../plots/cartesian/axes":222}],328:[function(_dereq_,module,exports){
78811/**
78812* Copyright 2012-2020, Plotly, Inc.
78813* All rights reserved.
78814*
78815* This source code is licensed under the MIT license found in the
78816* LICENSE file in the root directory of this source tree.
78817*/
78818
78819
78820'use strict';
78821
78822var d3 = _dereq_('d3');
78823
78824var Drawing = _dereq_('../../components/drawing');
78825var heatmapStyle = _dereq_('../heatmap/style');
78826
78827var makeColorMap = _dereq_('./make_color_map');
78828
78829
78830module.exports = function style(gd) {
78831 var contours = d3.select(gd).selectAll('g.contour');
78832
78833 contours.style('opacity', function(d) {
78834 return d[0].trace.opacity;
78835 });
78836
78837 contours.each(function(d) {
78838 var c = d3.select(this);
78839 var trace = d[0].trace;
78840 var contours = trace.contours;
78841 var line = trace.line;
78842 var cs = contours.size || 1;
78843 var start = contours.start;
78844
78845 // for contourcarpet only - is this a constraint-type contour trace?
78846 var isConstraintType = contours.type === 'constraint';
78847 var colorLines = !isConstraintType && contours.coloring === 'lines';
78848 var colorFills = !isConstraintType && contours.coloring === 'fill';
78849
78850 var colorMap = (colorLines || colorFills) ? makeColorMap(trace) : null;
78851
78852 c.selectAll('g.contourlevel').each(function(d) {
78853 d3.select(this).selectAll('path')
78854 .call(Drawing.lineGroupStyle,
78855 line.width,
78856 colorLines ? colorMap(d.level) : line.color,
78857 line.dash);
78858 });
78859
78860 var labelFont = contours.labelfont;
78861 c.selectAll('g.contourlabels text').each(function(d) {
78862 Drawing.font(d3.select(this), {
78863 family: labelFont.family,
78864 size: labelFont.size,
78865 color: labelFont.color || (colorLines ? colorMap(d.level) : line.color)
78866 });
78867 });
78868
78869 if(isConstraintType) {
78870 c.selectAll('g.contourfill path')
78871 .style('fill', trace.fillcolor);
78872 } else if(colorFills) {
78873 var firstFill;
78874
78875 c.selectAll('g.contourfill path')
78876 .style('fill', function(d) {
78877 if(firstFill === undefined) firstFill = d.level;
78878 return colorMap(d.level + 0.5 * cs);
78879 });
78880
78881 if(firstFill === undefined) firstFill = start;
78882
78883 c.selectAll('g.contourbg path')
78884 .style('fill', colorMap(firstFill - 0.5 * cs));
78885 }
78886 });
78887
78888 heatmapStyle(gd);
78889};
78890
78891},{"../../components/drawing":74,"../heatmap/style":342,"./make_color_map":324,"d3":16}],329:[function(_dereq_,module,exports){
78892/**
78893* Copyright 2012-2020, Plotly, Inc.
78894* All rights reserved.
78895*
78896* This source code is licensed under the MIT license found in the
78897* LICENSE file in the root directory of this source tree.
78898*/
78899
78900
78901'use strict';
78902
78903var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
78904var handleLabelDefaults = _dereq_('./label_defaults');
78905
78906
78907module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, opts) {
78908 var coloring = coerce('contours.coloring');
78909
78910 var showLines;
78911 var lineColor = '';
78912 if(coloring === 'fill') showLines = coerce('contours.showlines');
78913
78914 if(showLines !== false) {
78915 if(coloring !== 'lines') lineColor = coerce('line.color', '#000');
78916 coerce('line.width', 0.5);
78917 coerce('line.dash');
78918 }
78919
78920 if(coloring !== 'none') {
78921 // plots/plots always coerces showlegend to true, but in this case
78922 // we default to false and (by default) show a colorbar instead
78923 if(traceIn.showlegend !== true) traceOut.showlegend = false;
78924 traceOut._dfltShowLegend = false;
78925
78926 colorscaleDefaults(
78927 traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}
78928 );
78929 }
78930
78931 coerce('line.smoothing');
78932
78933 handleLabelDefaults(coerce, layout, lineColor, opts);
78934};
78935
78936},{"../../components/colorscale/defaults":62,"./label_defaults":323}],330:[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'use strict';
78946
78947var scatterAttrs = _dereq_('../scatter/attributes');
78948var baseAttrs = _dereq_('../../plots/attributes');
78949var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
78950var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
78951var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK;
78952
78953var extendFlat = _dereq_('../../lib/extend').extendFlat;
78954
78955module.exports = extendFlat({
78956 z: {
78957 valType: 'data_array',
78958 editType: 'calc',
78959
78960 },
78961 x: extendFlat({}, scatterAttrs.x, {impliedEdits: {xtype: 'array'}}),
78962 x0: extendFlat({}, scatterAttrs.x0, {impliedEdits: {xtype: 'scaled'}}),
78963 dx: extendFlat({}, scatterAttrs.dx, {impliedEdits: {xtype: 'scaled'}}),
78964 y: extendFlat({}, scatterAttrs.y, {impliedEdits: {ytype: 'array'}}),
78965 y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}),
78966 dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}),
78967
78968 text: {
78969 valType: 'data_array',
78970 editType: 'calc',
78971
78972 },
78973 hovertext: {
78974 valType: 'data_array',
78975 editType: 'calc',
78976
78977 },
78978 transpose: {
78979 valType: 'boolean',
78980 dflt: false,
78981
78982 editType: 'calc',
78983
78984 },
78985 xtype: {
78986 valType: 'enumerated',
78987 values: ['array', 'scaled'],
78988
78989 editType: 'calc+clearAxisTypes',
78990
78991 },
78992 ytype: {
78993 valType: 'enumerated',
78994 values: ['array', 'scaled'],
78995
78996 editType: 'calc+clearAxisTypes',
78997
78998 },
78999 zsmooth: {
79000 valType: 'enumerated',
79001 values: ['fast', 'best', false],
79002 dflt: false,
79003
79004 editType: 'calc',
79005
79006 },
79007 hoverongaps: {
79008 valType: 'boolean',
79009 dflt: true,
79010
79011 editType: 'none',
79012
79013 },
79014 connectgaps: {
79015 valType: 'boolean',
79016
79017 editType: 'calc',
79018
79019 },
79020 xgap: {
79021 valType: 'number',
79022 dflt: 0,
79023 min: 0,
79024
79025 editType: 'plot',
79026
79027 },
79028 ygap: {
79029 valType: 'number',
79030 dflt: 0,
79031 min: 0,
79032
79033 editType: 'plot',
79034
79035 },
79036 zhoverformat: {
79037 valType: 'string',
79038 dflt: '',
79039
79040 editType: 'none',
79041
79042 },
79043 hovertemplate: hovertemplateAttrs(),
79044 showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
79045}, {
79046 transforms: undefined
79047},
79048 colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false})
79049);
79050
79051},{"../../components/colorscale/attributes":59,"../../constants/docs":155,"../../lib/extend":173,"../../plots/attributes":219,"../../plots/template_attributes":264,"../scatter/attributes":389}],331:[function(_dereq_,module,exports){
79052/**
79053* Copyright 2012-2020, Plotly, Inc.
79054* All rights reserved.
79055*
79056* This source code is licensed under the MIT license found in the
79057* LICENSE file in the root directory of this source tree.
79058*/
79059
79060'use strict';
79061
79062var Registry = _dereq_('../../registry');
79063var Lib = _dereq_('../../lib');
79064var Axes = _dereq_('../../plots/cartesian/axes');
79065
79066var histogram2dCalc = _dereq_('../histogram2d/calc');
79067var colorscaleCalc = _dereq_('../../components/colorscale/calc');
79068var convertColumnData = _dereq_('./convert_column_xyz');
79069var clean2dArray = _dereq_('./clean_2d_array');
79070var interp2d = _dereq_('./interp2d');
79071var findEmpties = _dereq_('./find_empties');
79072var makeBoundArray = _dereq_('./make_bound_array');
79073
79074module.exports = function calc(gd, trace) {
79075 // prepare the raw data
79076 // run makeCalcdata on x and y even for heatmaps, in case of category mappings
79077 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
79078 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
79079 var isContour = Registry.traceIs(trace, 'contour');
79080 var isHist = Registry.traceIs(trace, 'histogram');
79081 var isGL2D = Registry.traceIs(trace, 'gl2d');
79082 var zsmooth = isContour ? 'best' : trace.zsmooth;
79083 var x;
79084 var x0;
79085 var dx;
79086 var y;
79087 var y0;
79088 var dy;
79089 var z;
79090 var i;
79091 var binned;
79092
79093 // cancel minimum tick spacings (only applies to bars and boxes)
79094 xa._minDtick = 0;
79095 ya._minDtick = 0;
79096
79097 if(isHist) {
79098 binned = histogram2dCalc(gd, trace);
79099 x = binned.x;
79100 x0 = binned.x0;
79101 dx = binned.dx;
79102 y = binned.y;
79103 y0 = binned.y0;
79104 dy = binned.dy;
79105 z = binned.z;
79106 } else {
79107 var zIn = trace.z;
79108 if(Lib.isArray1D(zIn)) {
79109 convertColumnData(trace, xa, ya, 'x', 'y', ['z']);
79110 x = trace._x;
79111 y = trace._y;
79112 zIn = trace._z;
79113 } else {
79114 x = trace._x = trace.x ? xa.makeCalcdata(trace, 'x') : [];
79115 y = trace._y = trace.y ? ya.makeCalcdata(trace, 'y') : [];
79116 }
79117
79118 x0 = trace.x0;
79119 dx = trace.dx;
79120 y0 = trace.y0;
79121 dy = trace.dy;
79122
79123 z = clean2dArray(zIn, trace, xa, ya);
79124
79125 if(isContour || trace.connectgaps) {
79126 trace._emptypoints = findEmpties(z);
79127 interp2d(z, trace._emptypoints);
79128 }
79129 }
79130
79131 function noZsmooth(msg) {
79132 zsmooth = trace._input.zsmooth = trace.zsmooth = false;
79133 Lib.warn('cannot use zsmooth: "fast": ' + msg);
79134 }
79135
79136 // check whether we really can smooth (ie all boxes are about the same size)
79137 if(zsmooth === 'fast') {
79138 if(xa.type === 'log' || ya.type === 'log') {
79139 noZsmooth('log axis found');
79140 } else if(!isHist) {
79141 if(x.length) {
79142 var avgdx = (x[x.length - 1] - x[0]) / (x.length - 1);
79143 var maxErrX = Math.abs(avgdx / 100);
79144 for(i = 0; i < x.length - 1; i++) {
79145 if(Math.abs(x[i + 1] - x[i] - avgdx) > maxErrX) {
79146 noZsmooth('x scale is not linear');
79147 break;
79148 }
79149 }
79150 }
79151 if(y.length && zsmooth === 'fast') {
79152 var avgdy = (y[y.length - 1] - y[0]) / (y.length - 1);
79153 var maxErrY = Math.abs(avgdy / 100);
79154 for(i = 0; i < y.length - 1; i++) {
79155 if(Math.abs(y[i + 1] - y[i] - avgdy) > maxErrY) {
79156 noZsmooth('y scale is not linear');
79157 break;
79158 }
79159 }
79160 }
79161 }
79162 }
79163
79164 // create arrays of brick boundaries, to be used by autorange and heatmap.plot
79165 var xlen = Lib.maxRowLength(z);
79166 var xIn = trace.xtype === 'scaled' ? '' : x;
79167 var xArray = makeBoundArray(trace, xIn, x0, dx, xlen, xa);
79168 var yIn = trace.ytype === 'scaled' ? '' : y;
79169 var yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya);
79170
79171 // handled in gl2d convert step
79172 if(!isGL2D) {
79173 trace._extremes[xa._id] = Axes.findExtremes(xa, xArray);
79174 trace._extremes[ya._id] = Axes.findExtremes(ya, yArray);
79175 }
79176
79177 var cd0 = {
79178 x: xArray,
79179 y: yArray,
79180 z: z,
79181 text: trace._text || trace.text,
79182 hovertext: trace._hovertext || trace.hovertext
79183 };
79184
79185 if(xIn && xIn.length === xArray.length - 1) cd0.xCenter = xIn;
79186 if(yIn && yIn.length === yArray.length - 1) cd0.yCenter = yIn;
79187
79188 if(isHist) {
79189 cd0.xRanges = binned.xRanges;
79190 cd0.yRanges = binned.yRanges;
79191 cd0.pts = binned.pts;
79192 }
79193
79194 if(!isContour) {
79195 colorscaleCalc(gd, trace, {vals: z, cLetter: 'z'});
79196 }
79197
79198 if(isContour && trace.contours && trace.contours.coloring === 'heatmap') {
79199 var dummyTrace = {
79200 type: trace.type === 'contour' ? 'heatmap' : 'histogram2d',
79201 xcalendar: trace.xcalendar,
79202 ycalendar: trace.ycalendar
79203 };
79204 cd0.xfill = makeBoundArray(dummyTrace, xIn, x0, dx, xlen, xa);
79205 cd0.yfill = makeBoundArray(dummyTrace, yIn, y0, dy, z.length, ya);
79206 }
79207
79208 return [cd0];
79209};
79210
79211},{"../../components/colorscale/calc":60,"../../lib":178,"../../plots/cartesian/axes":222,"../../registry":269,"../histogram2d/calc":359,"./clean_2d_array":332,"./convert_column_xyz":334,"./find_empties":336,"./interp2d":339,"./make_bound_array":340}],332:[function(_dereq_,module,exports){
79212/**
79213* Copyright 2012-2020, Plotly, Inc.
79214* All rights reserved.
79215*
79216* This source code is licensed under the MIT license found in the
79217* LICENSE file in the root directory of this source tree.
79218*/
79219
79220'use strict';
79221
79222var isNumeric = _dereq_('fast-isnumeric');
79223var Lib = _dereq_('../../lib');
79224var BADNUM = _dereq_('../../constants/numerical').BADNUM;
79225
79226module.exports = function clean2dArray(zOld, trace, xa, ya) {
79227 var rowlen, collen, getCollen, old2new, i, j;
79228
79229 function cleanZvalue(v) {
79230 if(!isNumeric(v)) return undefined;
79231 return +v;
79232 }
79233
79234 if(trace && trace.transpose) {
79235 rowlen = 0;
79236 for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length);
79237 if(rowlen === 0) return false;
79238 getCollen = function(zOld) { return zOld.length; };
79239 old2new = function(zOld, i, j) { return (zOld[j] || [])[i]; };
79240 } else {
79241 rowlen = zOld.length;
79242 getCollen = function(zOld, i) { return zOld[i].length; };
79243 old2new = function(zOld, i, j) { return (zOld[i] || [])[j]; };
79244 }
79245
79246 var padOld2new = function(zOld, i, j) {
79247 if(i === BADNUM || j === BADNUM) return BADNUM;
79248 return old2new(zOld, i, j);
79249 };
79250
79251 function axisMapping(ax) {
79252 if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' &&
79253 ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) {
79254 var axLetter = ax._id.charAt(0);
79255 var axMapping = {};
79256 var traceCategories = trace['_' + axLetter + 'CategoryMap'] || trace[axLetter];
79257 for(i = 0; i < traceCategories.length; i++) {
79258 axMapping[traceCategories[i]] = i;
79259 }
79260 return function(i) {
79261 var ind = axMapping[ax._categories[i]];
79262 return ind + 1 ? ind : BADNUM;
79263 };
79264 } else {
79265 return Lib.identity;
79266 }
79267 }
79268
79269 var xMap = axisMapping(xa);
79270 var yMap = axisMapping(ya);
79271
79272 if(ya && ya.type === 'category') rowlen = ya._categories.length;
79273 var zNew = new Array(rowlen);
79274
79275 for(i = 0; i < rowlen; i++) {
79276 if(xa && xa.type === 'category') {
79277 collen = xa._categories.length;
79278 } else {
79279 collen = getCollen(zOld, i);
79280 }
79281 zNew[i] = new Array(collen);
79282 for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(padOld2new(zOld, yMap(i), xMap(j)));
79283 }
79284
79285 return zNew;
79286};
79287
79288},{"../../constants/numerical":158,"../../lib":178,"fast-isnumeric":18}],333:[function(_dereq_,module,exports){
79289/**
79290* Copyright 2012-2020, Plotly, Inc.
79291* All rights reserved.
79292*
79293* This source code is licensed under the MIT license found in the
79294* LICENSE file in the root directory of this source tree.
79295*/
79296
79297'use strict';
79298
79299module.exports = {
79300 min: 'zmin',
79301 max: 'zmax'
79302};
79303
79304},{}],334:[function(_dereq_,module,exports){
79305/**
79306* Copyright 2012-2020, Plotly, Inc.
79307* All rights reserved.
79308*
79309* This source code is licensed under the MIT license found in the
79310* LICENSE file in the root directory of this source tree.
79311*/
79312
79313
79314'use strict';
79315
79316var Lib = _dereq_('../../lib');
79317var BADNUM = _dereq_('../../constants/numerical').BADNUM;
79318
79319module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) {
79320 var colLen = trace._length;
79321 var col1 = ax1.makeCalcdata(trace, var1Name);
79322 var col2 = ax2.makeCalcdata(trace, var2Name);
79323 var textCol = trace.text;
79324 var hasColumnText = (textCol !== undefined && Lib.isArray1D(textCol));
79325 var hoverTextCol = trace.hovertext;
79326 var hasColumnHoverText = (hoverTextCol !== undefined && Lib.isArray1D(hoverTextCol));
79327 var i, j;
79328
79329 var col1dv = Lib.distinctVals(col1);
79330 var col1vals = col1dv.vals;
79331 var col2dv = Lib.distinctVals(col2);
79332 var col2vals = col2dv.vals;
79333 var newArrays = [];
79334 var text;
79335 var hovertext;
79336
79337 for(i = 0; i < arrayVarNames.length; i++) {
79338 newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length);
79339 }
79340
79341 if(hasColumnText) {
79342 text = Lib.init2dArray(col2vals.length, col1vals.length);
79343 }
79344 if(hasColumnHoverText) {
79345 hovertext = Lib.init2dArray(col2vals.length, col1vals.length);
79346 }
79347
79348 var after2before = Lib.init2dArray(col2vals.length, col1vals.length);
79349
79350 for(i = 0; i < colLen; i++) {
79351 if(col1[i] !== BADNUM && col2[i] !== BADNUM) {
79352 var i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals);
79353 var i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals);
79354
79355 for(j = 0; j < arrayVarNames.length; j++) {
79356 var arrayVarName = arrayVarNames[j];
79357 var arrayVar = trace[arrayVarName];
79358 var newArray = newArrays[j];
79359 newArray[i2][i1] = arrayVar[i];
79360 after2before[i2][i1] = i;
79361 }
79362
79363 if(hasColumnText) text[i2][i1] = textCol[i];
79364 if(hasColumnHoverText) hovertext[i2][i1] = hoverTextCol[i];
79365 }
79366 }
79367
79368 trace['_' + var1Name] = col1vals;
79369 trace['_' + var2Name] = col2vals;
79370 for(j = 0; j < arrayVarNames.length; j++) {
79371 trace['_' + arrayVarNames[j]] = newArrays[j];
79372 }
79373 if(hasColumnText) trace._text = text;
79374 if(hasColumnHoverText) trace._hovertext = hovertext;
79375
79376 if(ax1 && ax1.type === 'category') {
79377 trace['_' + var1Name + 'CategoryMap'] = col1vals.map(function(v) { return ax1._categories[v];});
79378 }
79379
79380 if(ax2 && ax2.type === 'category') {
79381 trace['_' + var2Name + 'CategoryMap'] = col2vals.map(function(v) { return ax2._categories[v];});
79382 }
79383
79384 trace._after2before = after2before;
79385};
79386
79387},{"../../constants/numerical":158,"../../lib":178}],335:[function(_dereq_,module,exports){
79388/**
79389* Copyright 2012-2020, Plotly, Inc.
79390* All rights reserved.
79391*
79392* This source code is licensed under the MIT license found in the
79393* LICENSE file in the root directory of this source tree.
79394*/
79395
79396
79397'use strict';
79398
79399var Lib = _dereq_('../../lib');
79400
79401var handleXYZDefaults = _dereq_('./xyz_defaults');
79402var handleStyleDefaults = _dereq_('./style_defaults');
79403var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
79404var attributes = _dereq_('./attributes');
79405
79406
79407module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
79408 function coerce(attr, dflt) {
79409 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
79410 }
79411
79412 var validData = handleXYZDefaults(traceIn, traceOut, coerce, layout);
79413 if(!validData) {
79414 traceOut.visible = false;
79415 return;
79416 }
79417
79418 coerce('text');
79419 coerce('hovertext');
79420 coerce('hovertemplate');
79421
79422 handleStyleDefaults(traceIn, traceOut, coerce, layout);
79423
79424 coerce('hoverongaps');
79425 coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false));
79426
79427 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
79428};
79429
79430},{"../../components/colorscale/defaults":62,"../../lib":178,"./attributes":330,"./style_defaults":343,"./xyz_defaults":344}],336:[function(_dereq_,module,exports){
79431/**
79432* Copyright 2012-2020, Plotly, Inc.
79433* All rights reserved.
79434*
79435* This source code is licensed under the MIT license found in the
79436* LICENSE file in the root directory of this source tree.
79437*/
79438
79439'use strict';
79440
79441var maxRowLength = _dereq_('../../lib').maxRowLength;
79442
79443/* Return a list of empty points in 2D array z
79444 * each empty point z[i][j] gives an array [i, j, neighborCount]
79445 * neighborCount is the count of 4 nearest neighbors that DO exist
79446 * this is to give us an order of points to evaluate for interpolation.
79447 * if no neighbors exist, we iteratively look for neighbors that HAVE
79448 * neighbors, and add a fractional neighborCount
79449 */
79450module.exports = function findEmpties(z) {
79451 var empties = [];
79452 var neighborHash = {};
79453 var noNeighborList = [];
79454 var nextRow = z[0];
79455 var row = [];
79456 var blank = [0, 0, 0];
79457 var rowLength = maxRowLength(z);
79458 var prevRow;
79459 var i;
79460 var j;
79461 var thisPt;
79462 var p;
79463 var neighborCount;
79464 var newNeighborHash;
79465 var foundNewNeighbors;
79466
79467 for(i = 0; i < z.length; i++) {
79468 prevRow = row;
79469 row = nextRow;
79470 nextRow = z[i + 1] || [];
79471 for(j = 0; j < rowLength; j++) {
79472 if(row[j] === undefined) {
79473 neighborCount = (row[j - 1] !== undefined ? 1 : 0) +
79474 (row[j + 1] !== undefined ? 1 : 0) +
79475 (prevRow[j] !== undefined ? 1 : 0) +
79476 (nextRow[j] !== undefined ? 1 : 0);
79477
79478 if(neighborCount) {
79479 // for this purpose, don't count off-the-edge points
79480 // as undefined neighbors
79481 if(i === 0) neighborCount++;
79482 if(j === 0) neighborCount++;
79483 if(i === z.length - 1) neighborCount++;
79484 if(j === row.length - 1) neighborCount++;
79485
79486 // if all neighbors that could exist do, we don't
79487 // need this for finding farther neighbors
79488 if(neighborCount < 4) {
79489 neighborHash[[i, j]] = [i, j, neighborCount];
79490 }
79491
79492 empties.push([i, j, neighborCount]);
79493 } else noNeighborList.push([i, j]);
79494 }
79495 }
79496 }
79497
79498 while(noNeighborList.length) {
79499 newNeighborHash = {};
79500 foundNewNeighbors = false;
79501
79502 // look for cells that now have neighbors but didn't before
79503 for(p = noNeighborList.length - 1; p >= 0; p--) {
79504 thisPt = noNeighborList[p];
79505 i = thisPt[0];
79506 j = thisPt[1];
79507
79508 neighborCount = ((neighborHash[[i - 1, j]] || blank)[2] +
79509 (neighborHash[[i + 1, j]] || blank)[2] +
79510 (neighborHash[[i, j - 1]] || blank)[2] +
79511 (neighborHash[[i, j + 1]] || blank)[2]) / 20;
79512
79513 if(neighborCount) {
79514 newNeighborHash[thisPt] = [i, j, neighborCount];
79515 noNeighborList.splice(p, 1);
79516 foundNewNeighbors = true;
79517 }
79518 }
79519
79520 if(!foundNewNeighbors) {
79521 throw 'findEmpties iterated with no new neighbors';
79522 }
79523
79524 // put these new cells into the main neighbor list
79525 for(thisPt in newNeighborHash) {
79526 neighborHash[thisPt] = newNeighborHash[thisPt];
79527 empties.push(newNeighborHash[thisPt]);
79528 }
79529 }
79530
79531 // sort the full list in descending order of neighbor count
79532 return empties.sort(function(a, b) { return b[2] - a[2]; });
79533};
79534
79535},{"../../lib":178}],337:[function(_dereq_,module,exports){
79536/**
79537* Copyright 2012-2020, Plotly, Inc.
79538* All rights reserved.
79539*
79540* This source code is licensed under the MIT license found in the
79541* LICENSE file in the root directory of this source tree.
79542*/
79543
79544'use strict';
79545
79546var Fx = _dereq_('../../components/fx');
79547var Lib = _dereq_('../../lib');
79548var Axes = _dereq_('../../plots/cartesian/axes');
79549var extractOpts = _dereq_('../../components/colorscale').extractOpts;
79550
79551module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) {
79552 var cd0 = pointData.cd[0];
79553 var trace = cd0.trace;
79554 var xa = pointData.xa;
79555 var ya = pointData.ya;
79556 var x = cd0.x;
79557 var y = cd0.y;
79558 var z = cd0.z;
79559 var xc = cd0.xCenter;
79560 var yc = cd0.yCenter;
79561 var zmask = cd0.zmask;
79562 var zhoverformat = trace.zhoverformat;
79563 var x2 = x;
79564 var y2 = y;
79565
79566 var xl, yl, nx, ny;
79567
79568 if(pointData.index !== false) {
79569 try {
79570 nx = Math.round(pointData.index[1]);
79571 ny = Math.round(pointData.index[0]);
79572 } catch(e) {
79573 Lib.error('Error hovering on heatmap, ' +
79574 'pointNumber must be [row,col], found:', pointData.index);
79575 return;
79576 }
79577 if(nx < 0 || nx >= z[0].length || ny < 0 || ny > z.length) {
79578 return;
79579 }
79580 } else if(Fx.inbox(xval - x[0], xval - x[x.length - 1], 0) > 0 ||
79581 Fx.inbox(yval - y[0], yval - y[y.length - 1], 0) > 0) {
79582 return;
79583 } else {
79584 if(contour) {
79585 var i2;
79586 x2 = [2 * x[0] - x[1]];
79587
79588 for(i2 = 1; i2 < x.length; i2++) {
79589 x2.push((x[i2] + x[i2 - 1]) / 2);
79590 }
79591 x2.push([2 * x[x.length - 1] - x[x.length - 2]]);
79592
79593 y2 = [2 * y[0] - y[1]];
79594 for(i2 = 1; i2 < y.length; i2++) {
79595 y2.push((y[i2] + y[i2 - 1]) / 2);
79596 }
79597 y2.push([2 * y[y.length - 1] - y[y.length - 2]]);
79598 }
79599 nx = Math.max(0, Math.min(x2.length - 2, Lib.findBin(xval, x2)));
79600 ny = Math.max(0, Math.min(y2.length - 2, Lib.findBin(yval, y2)));
79601 }
79602
79603 var x0 = xa.c2p(x[nx]);
79604 var x1 = xa.c2p(x[nx + 1]);
79605 var y0 = ya.c2p(y[ny]);
79606 var y1 = ya.c2p(y[ny + 1]);
79607
79608 if(contour) {
79609 x1 = x0;
79610 xl = x[nx];
79611 y1 = y0;
79612 yl = y[ny];
79613 } else {
79614 xl = xc ? xc[nx] : ((x[nx] + x[nx + 1]) / 2);
79615 yl = yc ? yc[ny] : ((y[ny] + y[ny + 1]) / 2);
79616
79617 if(xa && xa.type === 'category') xl = x[nx];
79618 if(ya && ya.type === 'category') yl = y[ny];
79619
79620 if(trace.zsmooth) {
79621 x0 = x1 = xa.c2p(xl);
79622 y0 = y1 = ya.c2p(yl);
79623 }
79624 }
79625
79626 var zVal = z[ny][nx];
79627 if(zmask && !zmask[ny][nx]) zVal = undefined;
79628
79629 if(zVal === undefined && !trace.hoverongaps) return;
79630
79631 var text;
79632 if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) {
79633 text = cd0.hovertext[ny][nx];
79634 } else if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) {
79635 text = cd0.text[ny][nx];
79636 }
79637
79638 // dummy axis for formatting the z value
79639 var cOpts = extractOpts(trace);
79640 var dummyAx = {
79641 type: 'linear',
79642 range: [cOpts.min, cOpts.max],
79643 hoverformat: zhoverformat,
79644 _separators: xa._separators,
79645 _numFormat: xa._numFormat
79646 };
79647 var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text;
79648
79649 return [Lib.extendFlat(pointData, {
79650 index: trace._after2before ? trace._after2before[ny][nx] : [ny, nx],
79651 // never let a 2D override 1D type as closest point
79652 distance: pointData.maxHoverDistance,
79653 spikeDistance: pointData.maxSpikeDistance,
79654 x0: x0,
79655 x1: x1,
79656 y0: y0,
79657 y1: y1,
79658 xLabelVal: xl,
79659 yLabelVal: yl,
79660 zLabelVal: zVal,
79661 zLabel: zLabel,
79662 text: text
79663 })];
79664};
79665
79666},{"../../components/colorscale":64,"../../components/fx":92,"../../lib":178,"../../plots/cartesian/axes":222}],338:[function(_dereq_,module,exports){
79667/**
79668* Copyright 2012-2020, Plotly, Inc.
79669* All rights reserved.
79670*
79671* This source code is licensed under the MIT license found in the
79672* LICENSE file in the root directory of this source tree.
79673*/
79674
79675'use strict';
79676
79677module.exports = {
79678 attributes: _dereq_('./attributes'),
79679 supplyDefaults: _dereq_('./defaults'),
79680 calc: _dereq_('./calc'),
79681 plot: _dereq_('./plot'),
79682 colorbar: _dereq_('./colorbar'),
79683 style: _dereq_('./style'),
79684 hoverPoints: _dereq_('./hover'),
79685
79686 moduleType: 'trace',
79687 name: 'heatmap',
79688 basePlotModule: _dereq_('../../plots/cartesian'),
79689 categories: ['cartesian', 'svg', '2dMap', 'showLegend'],
79690 meta: {
79691
79692 }
79693};
79694
79695},{"../../plots/cartesian":235,"./attributes":330,"./calc":331,"./colorbar":333,"./defaults":335,"./hover":337,"./plot":341,"./style":342}],339:[function(_dereq_,module,exports){
79696/**
79697* Copyright 2012-2020, Plotly, Inc.
79698* All rights reserved.
79699*
79700* This source code is licensed under the MIT license found in the
79701* LICENSE file in the root directory of this source tree.
79702*/
79703
79704'use strict';
79705
79706var Lib = _dereq_('../../lib');
79707
79708var INTERPTHRESHOLD = 1e-2;
79709var NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]];
79710
79711function correctionOvershoot(maxFractionalChange) {
79712 // start with less overshoot, until we know it's converging,
79713 // then ramp up the overshoot for faster convergence
79714 return 0.5 - 0.25 * Math.min(1, maxFractionalChange * 0.5);
79715}
79716
79717/*
79718 * interp2d: Fill in missing data from a 2D array using an iterative
79719 * poisson equation solver with zero-derivative BC at edges.
79720 * Amazingly, this just amounts to repeatedly averaging all the existing
79721 * nearest neighbors, at least if we don't take x/y scaling into account,
79722 * which is the right approach here where x and y may not even have the
79723 * same units.
79724 *
79725 * @param {array of arrays} z
79726 * The 2D array to fill in. Will be mutated here. Assumed to already be
79727 * cleaned, so all entries are numbers except gaps, which are `undefined`.
79728 * @param {array of arrays} emptyPoints
79729 * Each entry [i, j, neighborCount] for empty points z[i][j] and the number
79730 * of neighbors that are *not* missing. Assumed to be sorted from most to
79731 * least neighbors, as produced by heatmap/find_empties.
79732 */
79733module.exports = function interp2d(z, emptyPoints) {
79734 var maxFractionalChange = 1;
79735 var i;
79736
79737 // one pass to fill in a starting value for all the empties
79738 iterateInterp2d(z, emptyPoints);
79739
79740 // we're don't need to iterate lone empties - remove them
79741 for(i = 0; i < emptyPoints.length; i++) {
79742 if(emptyPoints[i][2] < 4) break;
79743 }
79744 // but don't remove these points from the original array,
79745 // we'll use them for masking, so make a copy.
79746 emptyPoints = emptyPoints.slice(i);
79747
79748 for(i = 0; i < 100 && maxFractionalChange > INTERPTHRESHOLD; i++) {
79749 maxFractionalChange = iterateInterp2d(z, emptyPoints,
79750 correctionOvershoot(maxFractionalChange));
79751 }
79752 if(maxFractionalChange > INTERPTHRESHOLD) {
79753 Lib.log('interp2d didn\'t converge quickly', maxFractionalChange);
79754 }
79755
79756 return z;
79757};
79758
79759function iterateInterp2d(z, emptyPoints, overshoot) {
79760 var maxFractionalChange = 0;
79761 var thisPt;
79762 var i;
79763 var j;
79764 var p;
79765 var q;
79766 var neighborShift;
79767 var neighborRow;
79768 var neighborVal;
79769 var neighborCount;
79770 var neighborSum;
79771 var initialVal;
79772 var minNeighbor;
79773 var maxNeighbor;
79774
79775 for(p = 0; p < emptyPoints.length; p++) {
79776 thisPt = emptyPoints[p];
79777 i = thisPt[0];
79778 j = thisPt[1];
79779 initialVal = z[i][j];
79780 neighborSum = 0;
79781 neighborCount = 0;
79782
79783 for(q = 0; q < 4; q++) {
79784 neighborShift = NEIGHBORSHIFTS[q];
79785 neighborRow = z[i + neighborShift[0]];
79786 if(!neighborRow) continue;
79787 neighborVal = neighborRow[j + neighborShift[1]];
79788 if(neighborVal !== undefined) {
79789 if(neighborSum === 0) {
79790 minNeighbor = maxNeighbor = neighborVal;
79791 } else {
79792 minNeighbor = Math.min(minNeighbor, neighborVal);
79793 maxNeighbor = Math.max(maxNeighbor, neighborVal);
79794 }
79795 neighborCount++;
79796 neighborSum += neighborVal;
79797 }
79798 }
79799
79800 if(neighborCount === 0) {
79801 throw 'iterateInterp2d order is wrong: no defined neighbors';
79802 }
79803
79804 // this is the laplace equation interpolation:
79805 // each point is just the average of its neighbors
79806 // note that this ignores differential x/y scaling
79807 // which I think is the right approach, since we
79808 // don't know what that scaling means
79809 z[i][j] = neighborSum / neighborCount;
79810
79811 if(initialVal === undefined) {
79812 if(neighborCount < 4) maxFractionalChange = 1;
79813 } else {
79814 // we can make large empty regions converge faster
79815 // if we overshoot the change vs the previous value
79816 z[i][j] = (1 + overshoot) * z[i][j] - overshoot * initialVal;
79817
79818 if(maxNeighbor > minNeighbor) {
79819 maxFractionalChange = Math.max(maxFractionalChange,
79820 Math.abs(z[i][j] - initialVal) / (maxNeighbor - minNeighbor));
79821 }
79822 }
79823 }
79824
79825 return maxFractionalChange;
79826}
79827
79828},{"../../lib":178}],340:[function(_dereq_,module,exports){
79829/**
79830* Copyright 2012-2020, Plotly, Inc.
79831* All rights reserved.
79832*
79833* This source code is licensed under the MIT license found in the
79834* LICENSE file in the root directory of this source tree.
79835*/
79836
79837'use strict';
79838
79839var Registry = _dereq_('../../registry');
79840var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
79841
79842module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) {
79843 var arrayOut = [];
79844 var isContour = Registry.traceIs(trace, 'contour');
79845 var isHist = Registry.traceIs(trace, 'histogram');
79846 var isGL2D = Registry.traceIs(trace, 'gl2d');
79847 var v0;
79848 var dv;
79849 var i;
79850
79851 var isArrayOfTwoItemsOrMore = isArrayOrTypedArray(arrayIn) && arrayIn.length > 1;
79852
79853 if(isArrayOfTwoItemsOrMore && !isHist && (ax.type !== 'category')) {
79854 var len = arrayIn.length;
79855
79856 // given vals are brick centers
79857 // hopefully length === numbricks, but use this method even if too few are supplied
79858 // and extend it linearly based on the last two points
79859 if(len <= numbricks) {
79860 // contour plots only want the centers
79861 if(isContour || isGL2D) arrayOut = arrayIn.slice(0, numbricks);
79862 else if(numbricks === 1) {
79863 arrayOut = [arrayIn[0] - 0.5, arrayIn[0] + 0.5];
79864 } else {
79865 arrayOut = [1.5 * arrayIn[0] - 0.5 * arrayIn[1]];
79866
79867 for(i = 1; i < len; i++) {
79868 arrayOut.push((arrayIn[i - 1] + arrayIn[i]) * 0.5);
79869 }
79870
79871 arrayOut.push(1.5 * arrayIn[len - 1] - 0.5 * arrayIn[len - 2]);
79872 }
79873
79874 if(len < numbricks) {
79875 var lastPt = arrayOut[arrayOut.length - 1];
79876 var delta = lastPt - arrayOut[arrayOut.length - 2];
79877
79878 for(i = len; i < numbricks; i++) {
79879 lastPt += delta;
79880 arrayOut.push(lastPt);
79881 }
79882 }
79883 } else {
79884 // hopefully length === numbricks+1, but do something regardless:
79885 // given vals are brick boundaries
79886 return isContour ?
79887 arrayIn.slice(0, numbricks) : // we must be strict for contours
79888 arrayIn.slice(0, numbricks + 1);
79889 }
79890 } else {
79891 var calendar = trace[ax._id.charAt(0) + 'calendar'];
79892
79893 if(isHist) {
79894 v0 = ax.r2c(v0In, 0, calendar);
79895 } else {
79896 if(isArrayOrTypedArray(arrayIn) && arrayIn.length === 1) {
79897 v0 = arrayIn[0];
79898 } else if(v0In === undefined) {
79899 v0 = 0;
79900 } else {
79901 var fn = ax.type === 'log' ? ax.d2c : ax.r2c;
79902 v0 = fn(v0In, 0, calendar);
79903 }
79904 }
79905
79906 dv = dvIn || 1;
79907
79908 for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) {
79909 arrayOut.push(v0 + dv * i);
79910 }
79911 }
79912
79913 return arrayOut;
79914};
79915
79916},{"../../lib":178,"../../registry":269}],341:[function(_dereq_,module,exports){
79917/**
79918* Copyright 2012-2020, Plotly, Inc.
79919* All rights reserved.
79920*
79921* This source code is licensed under the MIT license found in the
79922* LICENSE file in the root directory of this source tree.
79923*/
79924
79925
79926'use strict';
79927
79928var d3 = _dereq_('d3');
79929var tinycolor = _dereq_('tinycolor2');
79930
79931var Registry = _dereq_('../../registry');
79932var Lib = _dereq_('../../lib');
79933var makeColorScaleFuncFromTrace = _dereq_('../../components/colorscale').makeColorScaleFuncFromTrace;
79934var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
79935
79936module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
79937 var xa = plotinfo.xaxis;
79938 var ya = plotinfo.yaxis;
79939
79940 Lib.makeTraceGroups(heatmapLayer, cdheatmaps, 'hm').each(function(cd) {
79941 var plotGroup = d3.select(this);
79942 var cd0 = cd[0];
79943 var trace = cd0.trace;
79944
79945 var z = cd0.z;
79946 var x = cd0.x;
79947 var y = cd0.y;
79948 var xc = cd0.xCenter;
79949 var yc = cd0.yCenter;
79950 var isContour = Registry.traceIs(trace, 'contour');
79951 var zsmooth = isContour ? 'best' : trace.zsmooth;
79952
79953 // get z dims
79954 var m = z.length;
79955 var n = Lib.maxRowLength(z);
79956 var xrev = false;
79957 var yrev = false;
79958
79959 var left, right, temp, top, bottom, i;
79960
79961 // TODO: if there are multiple overlapping categorical heatmaps,
79962 // or if we allow category sorting, then the categories may not be
79963 // sequential... may need to reorder and/or expand z
79964
79965 // Get edges of png in pixels (xa.c2p() maps axes coordinates to pixel coordinates)
79966 // figure out if either axis is reversed (y is usually reversed, in pixel coords)
79967 // also clip the image to maximum 50% outside the visible plot area
79968 // bigger image lets you pan more naturally, but slows performance.
79969 // TODO: use low-resolution images outside the visible plot for panning
79970 // these while loops find the first and last brick bounds that are defined
79971 // (in case of log of a negative)
79972 i = 0;
79973 while(left === undefined && i < x.length - 1) {
79974 left = xa.c2p(x[i]);
79975 i++;
79976 }
79977 i = x.length - 1;
79978 while(right === undefined && i > 0) {
79979 right = xa.c2p(x[i]);
79980 i--;
79981 }
79982
79983 if(right < left) {
79984 temp = right;
79985 right = left;
79986 left = temp;
79987 xrev = true;
79988 }
79989
79990 i = 0;
79991 while(top === undefined && i < y.length - 1) {
79992 top = ya.c2p(y[i]);
79993 i++;
79994 }
79995 i = y.length - 1;
79996 while(bottom === undefined && i > 0) {
79997 bottom = ya.c2p(y[i]);
79998 i--;
79999 }
80000
80001 if(bottom < top) {
80002 temp = top;
80003 top = bottom;
80004 bottom = temp;
80005 yrev = true;
80006 }
80007
80008 // for contours with heatmap fill, we generate the boundaries based on
80009 // brick centers but then use the brick edges for drawing the bricks
80010 if(isContour) {
80011 xc = x;
80012 yc = y;
80013 x = cd0.xfill;
80014 y = cd0.yfill;
80015 }
80016
80017 // make an image that goes at most half a screen off either side, to keep
80018 // time reasonable when you zoom in. if zsmooth is true/fast, don't worry
80019 // about this, because zooming doesn't increase number of pixels
80020 // if zsmooth is best, don't include anything off screen because it takes too long
80021 if(zsmooth !== 'fast') {
80022 var extra = zsmooth === 'best' ? 0 : 0.5;
80023 left = Math.max(-extra * xa._length, left);
80024 right = Math.min((1 + extra) * xa._length, right);
80025 top = Math.max(-extra * ya._length, top);
80026 bottom = Math.min((1 + extra) * ya._length, bottom);
80027 }
80028
80029 var imageWidth = Math.round(right - left);
80030 var imageHeight = Math.round(bottom - top);
80031
80032 // setup image nodes
80033
80034 // if image is entirely off-screen, don't even draw it
80035 var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
80036
80037 if(isOffScreen) {
80038 var noImage = plotGroup.selectAll('image').data([]);
80039 noImage.exit().remove();
80040 return;
80041 }
80042
80043 // generate image data
80044
80045 var canvasW, canvasH;
80046 if(zsmooth === 'fast') {
80047 canvasW = n;
80048 canvasH = m;
80049 } else {
80050 canvasW = imageWidth;
80051 canvasH = imageHeight;
80052 }
80053
80054 var canvas = document.createElement('canvas');
80055 canvas.width = canvasW;
80056 canvas.height = canvasH;
80057 var context = canvas.getContext('2d');
80058
80059 var sclFunc = makeColorScaleFuncFromTrace(trace, {noNumericCheck: true, returnArray: true});
80060
80061 // map brick boundaries to image pixels
80062 var xpx,
80063 ypx;
80064 if(zsmooth === 'fast') {
80065 xpx = xrev ?
80066 function(index) { return n - 1 - index; } :
80067 Lib.identity;
80068 ypx = yrev ?
80069 function(index) { return m - 1 - index; } :
80070 Lib.identity;
80071 } else {
80072 xpx = function(index) {
80073 return Lib.constrain(Math.round(xa.c2p(x[index]) - left),
80074 0, imageWidth);
80075 };
80076 ypx = function(index) {
80077 return Lib.constrain(Math.round(ya.c2p(y[index]) - top),
80078 0, imageHeight);
80079 };
80080 }
80081
80082 // build the pixel map brick-by-brick
80083 // cruise through z-matrix row-by-row
80084 // build a brick at each z-matrix value
80085 var yi = ypx(0);
80086 var yb = [yi, yi];
80087 var xbi = xrev ? 0 : 1;
80088 var ybi = yrev ? 0 : 1;
80089 // for collecting an average luminosity of the heatmap
80090 var pixcount = 0;
80091 var rcount = 0;
80092 var gcount = 0;
80093 var bcount = 0;
80094
80095 var xb, j, xi, v, row, c;
80096
80097 function setColor(v, pixsize) {
80098 if(v !== undefined) {
80099 var c = sclFunc(v);
80100 c[0] = Math.round(c[0]);
80101 c[1] = Math.round(c[1]);
80102 c[2] = Math.round(c[2]);
80103
80104 pixcount += pixsize;
80105 rcount += c[0] * pixsize;
80106 gcount += c[1] * pixsize;
80107 bcount += c[2] * pixsize;
80108 return c;
80109 }
80110 return [0, 0, 0, 0];
80111 }
80112
80113 function interpColor(r0, r1, xinterp, yinterp) {
80114 var z00 = r0[xinterp.bin0];
80115 if(z00 === undefined) return setColor(undefined, 1);
80116
80117 var z01 = r0[xinterp.bin1];
80118 var z10 = r1[xinterp.bin0];
80119 var z11 = r1[xinterp.bin1];
80120 var dx = (z01 - z00) || 0;
80121 var dy = (z10 - z00) || 0;
80122 var dxy;
80123
80124 // the bilinear interpolation term needs different calculations
80125 // for all the different permutations of missing data
80126 // among the neighbors of the main point, to ensure
80127 // continuity across brick boundaries.
80128 if(z01 === undefined) {
80129 if(z11 === undefined) dxy = 0;
80130 else if(z10 === undefined) dxy = 2 * (z11 - z00);
80131 else dxy = (2 * z11 - z10 - z00) * 2 / 3;
80132 } else if(z11 === undefined) {
80133 if(z10 === undefined) dxy = 0;
80134 else dxy = (2 * z00 - z01 - z10) * 2 / 3;
80135 } else if(z10 === undefined) dxy = (2 * z11 - z01 - z00) * 2 / 3;
80136 else dxy = (z11 + z00 - z01 - z10);
80137
80138 return setColor(z00 + xinterp.frac * dx + yinterp.frac * (dy + xinterp.frac * dxy));
80139 }
80140
80141 if(zsmooth) { // best or fast, works fastest with imageData
80142 var pxIndex = 0;
80143 var pixels;
80144
80145 try {
80146 pixels = new Uint8Array(imageWidth * imageHeight * 4);
80147 } catch(e) {
80148 pixels = new Array(imageWidth * imageHeight * 4);
80149 }
80150
80151 if(zsmooth === 'best') {
80152 var xForPx = xc || x;
80153 var yForPx = yc || y;
80154 var xPixArray = new Array(xForPx.length);
80155 var yPixArray = new Array(yForPx.length);
80156 var xinterpArray = new Array(imageWidth);
80157 var findInterpX = xc ? findInterpFromCenters : findInterp;
80158 var findInterpY = yc ? findInterpFromCenters : findInterp;
80159 var yinterp, r0, r1;
80160
80161 // first make arrays of x and y pixel locations of brick boundaries
80162 for(i = 0; i < xForPx.length; i++) xPixArray[i] = Math.round(xa.c2p(xForPx[i]) - left);
80163 for(i = 0; i < yForPx.length; i++) yPixArray[i] = Math.round(ya.c2p(yForPx[i]) - top);
80164
80165 // then make arrays of interpolations
80166 // (bin0=closest, bin1=next, frac=fractional dist.)
80167 for(i = 0; i < imageWidth; i++) xinterpArray[i] = findInterpX(i, xPixArray);
80168
80169 // now do the interpolations and fill the png
80170 for(j = 0; j < imageHeight; j++) {
80171 yinterp = findInterpY(j, yPixArray);
80172 r0 = z[yinterp.bin0];
80173 r1 = z[yinterp.bin1];
80174 for(i = 0; i < imageWidth; i++, pxIndex += 4) {
80175 c = interpColor(r0, r1, xinterpArray[i], yinterp);
80176 putColor(pixels, pxIndex, c);
80177 }
80178 }
80179 } else { // zsmooth = fast
80180 for(j = 0; j < m; j++) {
80181 row = z[j];
80182 yb = ypx(j);
80183 for(i = 0; i < imageWidth; i++) {
80184 c = setColor(row[i], 1);
80185 pxIndex = (yb * imageWidth + xpx(i)) * 4;
80186 putColor(pixels, pxIndex, c);
80187 }
80188 }
80189 }
80190
80191 var imageData = context.createImageData(imageWidth, imageHeight);
80192 try {
80193 imageData.data.set(pixels);
80194 } catch(e) {
80195 var pxArray = imageData.data;
80196 var dlen = pxArray.length;
80197 for(j = 0; j < dlen; j ++) {
80198 pxArray[j] = pixels[j];
80199 }
80200 }
80201
80202 context.putImageData(imageData, 0, 0);
80203 } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
80204 // gaps do not need to be exact integers, but if they *are* we will get
80205 // cleaner edges by rounding at least one edge
80206 var xGap = trace.xgap;
80207 var yGap = trace.ygap;
80208 var xGapLeft = Math.floor(xGap / 2);
80209 var yGapTop = Math.floor(yGap / 2);
80210
80211 for(j = 0; j < m; j++) {
80212 row = z[j];
80213 yb.reverse();
80214 yb[ybi] = ypx(j + 1);
80215 if(yb[0] === yb[1] || yb[0] === undefined || yb[1] === undefined) {
80216 continue;
80217 }
80218 xi = xpx(0);
80219 xb = [xi, xi];
80220 for(i = 0; i < n; i++) {
80221 // build one color brick!
80222 xb.reverse();
80223 xb[xbi] = xpx(i + 1);
80224 if(xb[0] === xb[1] || xb[0] === undefined || xb[1] === undefined) {
80225 continue;
80226 }
80227 v = row[i];
80228 c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
80229 context.fillStyle = 'rgba(' + c.join(',') + ')';
80230
80231 context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop,
80232 xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap);
80233 }
80234 }
80235 }
80236
80237 rcount = Math.round(rcount / pixcount);
80238 gcount = Math.round(gcount / pixcount);
80239 bcount = Math.round(bcount / pixcount);
80240 var avgColor = tinycolor('rgb(' + rcount + ',' + gcount + ',' + bcount + ')');
80241
80242 gd._hmpixcount = (gd._hmpixcount||0) + pixcount;
80243 gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance();
80244
80245 var image3 = plotGroup.selectAll('image')
80246 .data(cd);
80247
80248 image3.enter().append('svg:image').attr({
80249 xmlns: xmlnsNamespaces.svg,
80250 preserveAspectRatio: 'none'
80251 });
80252
80253 image3.attr({
80254 height: imageHeight,
80255 width: imageWidth,
80256 x: left,
80257 y: top,
80258 'xlink:href': canvas.toDataURL('image/png')
80259 });
80260 });
80261};
80262
80263// get interpolated bin value. Returns {bin0:closest bin, frac:fractional dist to next, bin1:next bin}
80264function findInterp(pixel, pixArray) {
80265 var maxBin = pixArray.length - 2;
80266 var bin = Lib.constrain(Lib.findBin(pixel, pixArray), 0, maxBin);
80267 var pix0 = pixArray[bin];
80268 var pix1 = pixArray[bin + 1];
80269 var interp = Lib.constrain(bin + (pixel - pix0) / (pix1 - pix0) - 0.5, 0, maxBin);
80270 var bin0 = Math.round(interp);
80271 var frac = Math.abs(interp - bin0);
80272
80273 if(!interp || interp === maxBin || !frac) {
80274 return {
80275 bin0: bin0,
80276 bin1: bin0,
80277 frac: 0
80278 };
80279 }
80280 return {
80281 bin0: bin0,
80282 frac: frac,
80283 bin1: Math.round(bin0 + frac / (interp - bin0))
80284 };
80285}
80286
80287function findInterpFromCenters(pixel, centerPixArray) {
80288 var maxBin = centerPixArray.length - 1;
80289 var bin = Lib.constrain(Lib.findBin(pixel, centerPixArray), 0, maxBin);
80290 var pix0 = centerPixArray[bin];
80291 var pix1 = centerPixArray[bin + 1];
80292 var frac = ((pixel - pix0) / (pix1 - pix0)) || 0;
80293 if(frac <= 0) {
80294 return {
80295 bin0: bin,
80296 bin1: bin,
80297 frac: 0
80298 };
80299 }
80300 if(frac < 0.5) {
80301 return {
80302 bin0: bin,
80303 bin1: bin + 1,
80304 frac: frac
80305 };
80306 }
80307 return {
80308 bin0: bin + 1,
80309 bin1: bin,
80310 frac: 1 - frac
80311 };
80312}
80313
80314function putColor(pixels, pxIndex, c) {
80315 pixels[pxIndex] = c[0];
80316 pixels[pxIndex + 1] = c[1];
80317 pixels[pxIndex + 2] = c[2];
80318 pixels[pxIndex + 3] = Math.round(c[3] * 255);
80319}
80320
80321},{"../../components/colorscale":64,"../../constants/xmlns_namespaces":159,"../../lib":178,"../../registry":269,"d3":16,"tinycolor2":35}],342:[function(_dereq_,module,exports){
80322/**
80323* Copyright 2012-2020, Plotly, Inc.
80324* All rights reserved.
80325*
80326* This source code is licensed under the MIT license found in the
80327* LICENSE file in the root directory of this source tree.
80328*/
80329
80330
80331'use strict';
80332
80333var d3 = _dereq_('d3');
80334
80335module.exports = function style(gd) {
80336 d3.select(gd).selectAll('.hm image')
80337 .style('opacity', function(d) {
80338 return d.trace.opacity;
80339 });
80340};
80341
80342},{"d3":16}],343:[function(_dereq_,module,exports){
80343/**
80344* Copyright 2012-2020, Plotly, Inc.
80345* All rights reserved.
80346*
80347* This source code is licensed under the MIT license found in the
80348* LICENSE file in the root directory of this source tree.
80349*/
80350
80351
80352'use strict';
80353
80354module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) {
80355 var zsmooth = coerce('zsmooth');
80356 if(zsmooth === false) {
80357 // ensure that xgap and ygap are coerced only when zsmooth allows them to have an effect.
80358 coerce('xgap');
80359 coerce('ygap');
80360 }
80361
80362 coerce('zhoverformat');
80363};
80364
80365},{}],344:[function(_dereq_,module,exports){
80366/**
80367* Copyright 2012-2020, Plotly, Inc.
80368* All rights reserved.
80369*
80370* This source code is licensed under the MIT license found in the
80371* LICENSE file in the root directory of this source tree.
80372*/
80373
80374'use strict';
80375
80376var isNumeric = _dereq_('fast-isnumeric');
80377var Lib = _dereq_('../../lib');
80378
80379var Registry = _dereq_('../../registry');
80380
80381module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, xName, yName) {
80382 var z = coerce('z');
80383 xName = xName || 'x';
80384 yName = yName || 'y';
80385 var x, y;
80386
80387 if(z === undefined || !z.length) return 0;
80388
80389 if(Lib.isArray1D(traceIn.z)) {
80390 x = coerce(xName);
80391 y = coerce(yName);
80392
80393 var xlen = Lib.minRowLength(x);
80394 var ylen = Lib.minRowLength(y);
80395
80396 // column z must be accompanied by xName and yName arrays
80397 if(xlen === 0 || ylen === 0) return 0;
80398
80399 traceOut._length = Math.min(xlen, ylen, z.length);
80400 } else {
80401 x = coordDefaults(xName, coerce);
80402 y = coordDefaults(yName, coerce);
80403
80404 // TODO put z validation elsewhere
80405 if(!isValidZ(z)) return 0;
80406
80407 coerce('transpose');
80408
80409 traceOut._length = null;
80410 }
80411
80412 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
80413 handleCalendarDefaults(traceIn, traceOut, [xName, yName], layout);
80414
80415 return true;
80416};
80417
80418function coordDefaults(coordStr, coerce) {
80419 var coord = coerce(coordStr);
80420 var coordType = coord ? coerce(coordStr + 'type', 'array') : 'scaled';
80421
80422 if(coordType === 'scaled') {
80423 coerce(coordStr + '0');
80424 coerce('d' + coordStr);
80425 }
80426
80427 return coord;
80428}
80429
80430function isValidZ(z) {
80431 var allRowsAreArrays = true;
80432 var oneRowIsFilled = false;
80433 var hasOneNumber = false;
80434 var zi;
80435
80436 /*
80437 * Without this step:
80438 *
80439 * hasOneNumber = false breaks contour but not heatmap
80440 * allRowsAreArrays = false breaks contour but not heatmap
80441 * oneRowIsFilled = false breaks both
80442 */
80443
80444 for(var i = 0; i < z.length; i++) {
80445 zi = z[i];
80446 if(!Lib.isArrayOrTypedArray(zi)) {
80447 allRowsAreArrays = false;
80448 break;
80449 }
80450 if(zi.length > 0) oneRowIsFilled = true;
80451 for(var j = 0; j < zi.length; j++) {
80452 if(isNumeric(zi[j])) {
80453 hasOneNumber = true;
80454 break;
80455 }
80456 }
80457 }
80458
80459 return (allRowsAreArrays && oneRowIsFilled && hasOneNumber);
80460}
80461
80462},{"../../lib":178,"../../registry":269,"fast-isnumeric":18}],345:[function(_dereq_,module,exports){
80463/**
80464* Copyright 2012-2020, Plotly, Inc.
80465* All rights reserved.
80466*
80467* This source code is licensed under the MIT license found in the
80468* LICENSE file in the root directory of this source tree.
80469*/
80470
80471'use strict';
80472
80473var barAttrs = _dereq_('../bar/attributes');
80474var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
80475var makeBinAttrs = _dereq_('./bin_attributes');
80476var constants = _dereq_('./constants');
80477var extendFlat = _dereq_('../../lib/extend').extendFlat;
80478
80479module.exports = {
80480 x: {
80481 valType: 'data_array',
80482 editType: 'calc+clearAxisTypes',
80483
80484 },
80485 y: {
80486 valType: 'data_array',
80487 editType: 'calc+clearAxisTypes',
80488
80489 },
80490
80491 text: extendFlat({}, barAttrs.text, {
80492
80493 }),
80494 hovertext: extendFlat({}, barAttrs.hovertext, {
80495
80496 }),
80497 orientation: barAttrs.orientation,
80498
80499 histfunc: {
80500 valType: 'enumerated',
80501 values: ['count', 'sum', 'avg', 'min', 'max'],
80502
80503 dflt: 'count',
80504 editType: 'calc',
80505
80506 },
80507 histnorm: {
80508 valType: 'enumerated',
80509 values: ['', 'percent', 'probability', 'density', 'probability density'],
80510 dflt: '',
80511
80512 editType: 'calc',
80513
80514 },
80515
80516 cumulative: {
80517 enabled: {
80518 valType: 'boolean',
80519 dflt: false,
80520
80521 editType: 'calc',
80522
80523 },
80524
80525 direction: {
80526 valType: 'enumerated',
80527 values: ['increasing', 'decreasing'],
80528 dflt: 'increasing',
80529
80530 editType: 'calc',
80531
80532 },
80533
80534 currentbin: {
80535 valType: 'enumerated',
80536 values: ['include', 'exclude', 'half'],
80537 dflt: 'include',
80538
80539 editType: 'calc',
80540
80541 },
80542 editType: 'calc'
80543 },
80544 nbinsx: {
80545 valType: 'integer',
80546 min: 0,
80547 dflt: 0,
80548
80549 editType: 'calc',
80550
80551 },
80552 xbins: makeBinAttrs('x', true),
80553
80554 nbinsy: {
80555 valType: 'integer',
80556 min: 0,
80557 dflt: 0,
80558
80559 editType: 'calc',
80560
80561 },
80562 ybins: makeBinAttrs('y', true),
80563 autobinx: {
80564 valType: 'boolean',
80565 dflt: null,
80566
80567 editType: 'calc',
80568
80569 },
80570 autobiny: {
80571 valType: 'boolean',
80572 dflt: null,
80573
80574 editType: 'calc',
80575
80576 },
80577
80578 bingroup: {
80579 valType: 'string',
80580
80581 dflt: '',
80582 editType: 'calc',
80583
80584 },
80585
80586 hovertemplate: hovertemplateAttrs({}, {
80587 keys: constants.eventDataKeys
80588 }),
80589
80590 marker: barAttrs.marker,
80591
80592 offsetgroup: barAttrs.offsetgroup,
80593 alignmentgroup: barAttrs.alignmentgroup,
80594
80595 selected: barAttrs.selected,
80596 unselected: barAttrs.unselected,
80597
80598 _deprecated: {
80599 bardir: barAttrs._deprecated.bardir
80600 }
80601};
80602
80603},{"../../lib/extend":173,"../../plots/template_attributes":264,"../bar/attributes":279,"./bin_attributes":347,"./constants":351}],346:[function(_dereq_,module,exports){
80604/**
80605* Copyright 2012-2020, Plotly, Inc.
80606* All rights reserved.
80607*
80608* This source code is licensed under the MIT license found in the
80609* LICENSE file in the root directory of this source tree.
80610*/
80611
80612
80613'use strict';
80614
80615
80616module.exports = function doAvg(size, counts) {
80617 var nMax = size.length;
80618 var total = 0;
80619 for(var i = 0; i < nMax; i++) {
80620 if(counts[i]) {
80621 size[i] /= counts[i];
80622 total += size[i];
80623 } else size[i] = null;
80624 }
80625 return total;
80626};
80627
80628},{}],347:[function(_dereq_,module,exports){
80629/**
80630* Copyright 2012-2020, Plotly, Inc.
80631* All rights reserved.
80632*
80633* This source code is licensed under the MIT license found in the
80634* LICENSE file in the root directory of this source tree.
80635*/
80636
80637'use strict';
80638
80639module.exports = function makeBinAttrs(axLetter, match) {
80640 return {
80641 start: {
80642 valType: 'any', // for date axes
80643
80644 editType: 'calc',
80645
80646 },
80647 end: {
80648 valType: 'any', // for date axes
80649
80650 editType: 'calc',
80651
80652 },
80653 size: {
80654 valType: 'any', // for date axes
80655
80656 editType: 'calc',
80657
80658 },
80659 editType: 'calc'
80660 };
80661};
80662
80663},{}],348:[function(_dereq_,module,exports){
80664/**
80665* Copyright 2012-2020, Plotly, Inc.
80666* All rights reserved.
80667*
80668* This source code is licensed under the MIT license found in the
80669* LICENSE file in the root directory of this source tree.
80670*/
80671
80672
80673'use strict';
80674
80675var isNumeric = _dereq_('fast-isnumeric');
80676
80677
80678module.exports = {
80679 count: function(n, i, size) {
80680 size[n]++;
80681 return 1;
80682 },
80683
80684 sum: function(n, i, size, counterData) {
80685 var v = counterData[i];
80686 if(isNumeric(v)) {
80687 v = Number(v);
80688 size[n] += v;
80689 return v;
80690 }
80691 return 0;
80692 },
80693
80694 avg: function(n, i, size, counterData, counts) {
80695 var v = counterData[i];
80696 if(isNumeric(v)) {
80697 v = Number(v);
80698 size[n] += v;
80699 counts[n]++;
80700 }
80701 return 0;
80702 },
80703
80704 min: function(n, i, size, counterData) {
80705 var v = counterData[i];
80706 if(isNumeric(v)) {
80707 v = Number(v);
80708 if(!isNumeric(size[n])) {
80709 size[n] = v;
80710 return v;
80711 } else if(size[n] > v) {
80712 var delta = v - size[n];
80713 size[n] = v;
80714 return delta;
80715 }
80716 }
80717 return 0;
80718 },
80719
80720 max: function(n, i, size, counterData) {
80721 var v = counterData[i];
80722 if(isNumeric(v)) {
80723 v = Number(v);
80724 if(!isNumeric(size[n])) {
80725 size[n] = v;
80726 return v;
80727 } else if(size[n] < v) {
80728 var delta = v - size[n];
80729 size[n] = v;
80730 return delta;
80731 }
80732 }
80733 return 0;
80734 }
80735};
80736
80737},{"fast-isnumeric":18}],349:[function(_dereq_,module,exports){
80738/**
80739* Copyright 2012-2020, Plotly, Inc.
80740* All rights reserved.
80741*
80742* This source code is licensed under the MIT license found in the
80743* LICENSE file in the root directory of this source tree.
80744*/
80745
80746
80747'use strict';
80748
80749var numConstants = _dereq_('../../constants/numerical');
80750var oneYear = numConstants.ONEAVGYEAR;
80751var oneMonth = numConstants.ONEAVGMONTH;
80752var oneDay = numConstants.ONEDAY;
80753var oneHour = numConstants.ONEHOUR;
80754var oneMin = numConstants.ONEMIN;
80755var oneSec = numConstants.ONESEC;
80756var tickIncrement = _dereq_('../../plots/cartesian/axes').tickIncrement;
80757
80758
80759/*
80760 * make a function that will find rounded bin edges
80761 * @param {number} leftGap: how far from the left edge of any bin is the closest data value?
80762 * @param {number} rightGap: how far from the right edge of any bin is the closest data value?
80763 * @param {Array[number]} binEdges: the actual edge values used in binning
80764 * @param {object} pa: the position axis
80765 * @param {string} calendar: the data calendar
80766 *
80767 * @return {function(v, isRightEdge)}:
80768 * find the start (isRightEdge is falsy) or end (truthy) label value for a bin edge `v`
80769 */
80770module.exports = function getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar) {
80771 // the rounding digit is the largest digit that changes in *all* of 4 regions:
80772 // - inside the rightGap before binEdges[0] (shifted 10% to the left)
80773 // - inside the leftGap after binEdges[0] (expanded by 10% of rightGap on each end)
80774 // - same for binEdges[1]
80775 var dv0 = -1.1 * rightGap;
80776 var dv1 = -0.1 * rightGap;
80777 var dv2 = leftGap - dv1;
80778 var edge0 = binEdges[0];
80779 var edge1 = binEdges[1];
80780 var leftDigit = Math.min(
80781 biggestDigitChanged(edge0 + dv1, edge0 + dv2, pa, calendar),
80782 biggestDigitChanged(edge1 + dv1, edge1 + dv2, pa, calendar)
80783 );
80784 var rightDigit = Math.min(
80785 biggestDigitChanged(edge0 + dv0, edge0 + dv1, pa, calendar),
80786 biggestDigitChanged(edge1 + dv0, edge1 + dv1, pa, calendar)
80787 );
80788
80789 // normally we try to make the label for the right edge different from
80790 // the left edge label, so it's unambiguous which bin gets data on the edge.
80791 // but if this results in more than 3 extra digits (or for dates, more than
80792 // 2 fields ie hr&min or min&sec, which is 3600x), it'll be more clutter than
80793 // useful so keep the label cleaner instead
80794 var digit, disambiguateEdges;
80795 if(leftDigit > rightDigit && rightDigit < Math.abs(edge1 - edge0) / 4000) {
80796 digit = leftDigit;
80797 disambiguateEdges = false;
80798 } else {
80799 digit = Math.min(leftDigit, rightDigit);
80800 disambiguateEdges = true;
80801 }
80802
80803 if(pa.type === 'date' && digit > oneDay) {
80804 var dashExclude = (digit === oneYear) ? 1 : 6;
80805 var increment = (digit === oneYear) ? 'M12' : 'M1';
80806
80807 return function(v, isRightEdge) {
80808 var dateStr = pa.c2d(v, oneYear, calendar);
80809 var dashPos = dateStr.indexOf('-', dashExclude);
80810 if(dashPos > 0) dateStr = dateStr.substr(0, dashPos);
80811 var roundedV = pa.d2c(dateStr, 0, calendar);
80812
80813 if(roundedV < v) {
80814 var nextV = tickIncrement(roundedV, increment, false, calendar);
80815 if((roundedV + nextV) / 2 < v + leftGap) roundedV = nextV;
80816 }
80817
80818 if(isRightEdge && disambiguateEdges) {
80819 return tickIncrement(roundedV, increment, true, calendar);
80820 }
80821
80822 return roundedV;
80823 };
80824 }
80825
80826 return function(v, isRightEdge) {
80827 var roundedV = digit * Math.round(v / digit);
80828 // if we rounded down and we could round up and still be < leftGap
80829 // (or what leftGap values round to), do that
80830 if(roundedV + (digit / 10) < v && roundedV + (digit * 0.9) < v + leftGap) {
80831 roundedV += digit;
80832 }
80833 // finally for the right edge back off one digit - but only if we can do that
80834 // and not clip off any data that's potentially in the bin
80835 if(isRightEdge && disambiguateEdges) {
80836 roundedV -= digit;
80837 }
80838 return roundedV;
80839 };
80840};
80841
80842/*
80843 * Find the largest digit that changes within a (calcdata) region [v1, v2]
80844 * if dates, "digit" means date/time part when it's bigger than a second
80845 * returns the unit value to round to this digit, eg 0.01 to round to hundredths, or
80846 * 100 to round to hundreds. returns oneMonth or oneYear for month or year rounding,
80847 * so that Math.min will work, rather than 'M1' and 'M12'
80848 */
80849function biggestDigitChanged(v1, v2, pa, calendar) {
80850 // are we crossing zero? can't say anything.
80851 // in principle this doesn't apply to dates but turns out this doesn't matter.
80852 if(v1 * v2 <= 0) return Infinity;
80853
80854 var dv = Math.abs(v2 - v1);
80855 var isDate = pa.type === 'date';
80856 var digit = biggestGuaranteedDigitChanged(dv, isDate);
80857 // see if a larger digit also changed
80858 for(var i = 0; i < 10; i++) {
80859 // numbers: next digit needs to be >10x but <100x then gets rounded down.
80860 // dates: next digit can be as much as 60x (then rounded down)
80861 var nextDigit = biggestGuaranteedDigitChanged(digit * 80, isDate);
80862 // if we get to years, the chain stops
80863 if(digit === nextDigit) break;
80864 if(didDigitChange(nextDigit, v1, v2, isDate, pa, calendar)) digit = nextDigit;
80865 else break;
80866 }
80867 return digit;
80868}
80869
80870/*
80871 * Find the largest digit that *definitely* changes in a region [v, v + dv] for any v
80872 * for nonuniform date regions (months/years) pick the largest
80873 */
80874function biggestGuaranteedDigitChanged(dv, isDate) {
80875 if(isDate && dv > oneSec) {
80876 // this is supposed to be the biggest *guaranteed* change
80877 // so compare to the longest month and year across any calendar,
80878 // and we'll iterate back up later
80879 // note: does not support rounding larger than one year. We could add
80880 // that if anyone wants it, but seems unusual and not strictly necessary.
80881 if(dv > oneDay) {
80882 if(dv > oneYear * 1.1) return oneYear;
80883 if(dv > oneMonth * 1.1) return oneMonth;
80884 return oneDay;
80885 }
80886
80887 if(dv > oneHour) return oneHour;
80888 if(dv > oneMin) return oneMin;
80889 return oneSec;
80890 }
80891 return Math.pow(10, Math.floor(Math.log(dv) / Math.LN10));
80892}
80893
80894function didDigitChange(digit, v1, v2, isDate, pa, calendar) {
80895 if(isDate && digit > oneDay) {
80896 var dateParts1 = dateParts(v1, pa, calendar);
80897 var dateParts2 = dateParts(v2, pa, calendar);
80898 var parti = (digit === oneYear) ? 0 : 1;
80899 return dateParts1[parti] !== dateParts2[parti];
80900 }
80901 return Math.floor(v2 / digit) - Math.floor(v1 / digit) > 0.1;
80902}
80903
80904function dateParts(v, pa, calendar) {
80905 var parts = pa.c2d(v, oneYear, calendar).split('-');
80906 if(parts[0] === '') {
80907 parts.unshift();
80908 parts[0] = '-' + parts[0];
80909 }
80910 return parts;
80911}
80912
80913},{"../../constants/numerical":158,"../../plots/cartesian/axes":222}],350:[function(_dereq_,module,exports){
80914/**
80915* Copyright 2012-2020, Plotly, Inc.
80916* All rights reserved.
80917*
80918* This source code is licensed under the MIT license found in the
80919* LICENSE file in the root directory of this source tree.
80920*/
80921
80922'use strict';
80923
80924var isNumeric = _dereq_('fast-isnumeric');
80925
80926var Lib = _dereq_('../../lib');
80927var Registry = _dereq_('../../registry');
80928var Axes = _dereq_('../../plots/cartesian/axes');
80929
80930var arraysToCalcdata = _dereq_('../bar/arrays_to_calcdata');
80931var binFunctions = _dereq_('./bin_functions');
80932var normFunctions = _dereq_('./norm_functions');
80933var doAvg = _dereq_('./average');
80934var getBinSpanLabelRound = _dereq_('./bin_label_vals');
80935
80936function calc(gd, trace) {
80937 var pos = [];
80938 var size = [];
80939 var pa = Axes.getFromId(gd, trace.orientation === 'h' ? trace.yaxis : trace.xaxis);
80940 var mainData = trace.orientation === 'h' ? 'y' : 'x';
80941 var counterData = {x: 'y', y: 'x'}[mainData];
80942 var calendar = trace[mainData + 'calendar'];
80943 var cumulativeSpec = trace.cumulative;
80944 var i;
80945
80946 var binsAndPos = calcAllAutoBins(gd, trace, pa, mainData);
80947 var binSpec = binsAndPos[0];
80948 var pos0 = binsAndPos[1];
80949
80950 var nonuniformBins = typeof binSpec.size === 'string';
80951 var binEdges = [];
80952 var bins = nonuniformBins ? binEdges : binSpec;
80953 // make the empty bin array
80954 var inc = [];
80955 var counts = [];
80956 var inputPoints = [];
80957 var total = 0;
80958 var norm = trace.histnorm;
80959 var func = trace.histfunc;
80960 var densityNorm = norm.indexOf('density') !== -1;
80961 var i2, binEnd, n;
80962
80963 if(cumulativeSpec.enabled && densityNorm) {
80964 // we treat "cumulative" like it means "integral" if you use a density norm,
80965 // which in the end means it's the same as without "density"
80966 norm = norm.replace(/ ?density$/, '');
80967 densityNorm = false;
80968 }
80969
80970 var extremeFunc = func === 'max' || func === 'min';
80971 var sizeInit = extremeFunc ? null : 0;
80972 var binFunc = binFunctions.count;
80973 var normFunc = normFunctions[norm];
80974 var isAvg = false;
80975 var pr2c = function(v) { return pa.r2c(v, 0, calendar); };
80976 var rawCounterData;
80977
80978 if(Lib.isArrayOrTypedArray(trace[counterData]) && func !== 'count') {
80979 rawCounterData = trace[counterData];
80980 isAvg = func === 'avg';
80981 binFunc = binFunctions[func];
80982 }
80983
80984 // create the bins (and any extra arrays needed)
80985 // assume more than 1e6 bins is an error, so we don't crash the browser
80986 i = pr2c(binSpec.start);
80987
80988 // decrease end a little in case of rounding errors
80989 binEnd = pr2c(binSpec.end) + (i - Axes.tickIncrement(i, binSpec.size, false, calendar)) / 1e6;
80990
80991 while(i < binEnd && pos.length < 1e6) {
80992 i2 = Axes.tickIncrement(i, binSpec.size, false, calendar);
80993 pos.push((i + i2) / 2);
80994 size.push(sizeInit);
80995 inputPoints.push([]);
80996 // nonuniform bins (like months) we need to search,
80997 // rather than straight calculate the bin we're in
80998 binEdges.push(i);
80999 // nonuniform bins also need nonuniform normalization factors
81000 if(densityNorm) inc.push(1 / (i2 - i));
81001 if(isAvg) counts.push(0);
81002 // break to avoid infinite loops
81003 if(i2 <= i) break;
81004 i = i2;
81005 }
81006 binEdges.push(i);
81007
81008 // for date axes we need bin bounds to be calcdata. For nonuniform bins
81009 // we already have this, but uniform with start/end/size they're still strings.
81010 if(!nonuniformBins && pa.type === 'date') {
81011 bins = {
81012 start: pr2c(bins.start),
81013 end: pr2c(bins.end),
81014 size: bins.size
81015 };
81016 }
81017
81018 // stash left and right gaps by group
81019 if(!gd._fullLayout._roundFnOpts) gd._fullLayout._roundFnOpts = {};
81020 var groupName = trace['_' + mainData + 'bingroup'];
81021 var roundFnOpts = {leftGap: Infinity, rightGap: Infinity};
81022 if(groupName) {
81023 if(!gd._fullLayout._roundFnOpts[groupName]) gd._fullLayout._roundFnOpts[groupName] = roundFnOpts;
81024 roundFnOpts = gd._fullLayout._roundFnOpts[groupName];
81025 }
81026
81027 // bin the data
81028 // and make histogram-specific pt-number-to-cd-index map object
81029 var nMax = size.length;
81030 var uniqueValsPerBin = true;
81031 var leftGap = roundFnOpts.leftGap;
81032 var rightGap = roundFnOpts.rightGap;
81033 var ptNumber2cdIndex = {};
81034 for(i = 0; i < pos0.length; i++) {
81035 var posi = pos0[i];
81036 n = Lib.findBin(posi, bins);
81037 if(n >= 0 && n < nMax) {
81038 total += binFunc(n, i, size, rawCounterData, counts);
81039 if(uniqueValsPerBin && inputPoints[n].length && posi !== pos0[inputPoints[n][0]]) {
81040 uniqueValsPerBin = false;
81041 }
81042 inputPoints[n].push(i);
81043 ptNumber2cdIndex[i] = n;
81044
81045 leftGap = Math.min(leftGap, posi - binEdges[n]);
81046 rightGap = Math.min(rightGap, binEdges[n + 1] - posi);
81047 }
81048 }
81049 roundFnOpts.leftGap = leftGap;
81050 roundFnOpts.rightGap = rightGap;
81051
81052 var roundFn;
81053 if(!uniqueValsPerBin) {
81054 roundFn = function(v, isRightEdge) {
81055 return function() {
81056 var roundFnOpts = gd._fullLayout._roundFnOpts[groupName];
81057 return getBinSpanLabelRound(
81058 roundFnOpts.leftGap,
81059 roundFnOpts.rightGap,
81060 binEdges, pa, calendar
81061 )(v, isRightEdge);
81062 };
81063 };
81064 }
81065
81066 // average and/or normalize the data, if needed
81067 if(isAvg) total = doAvg(size, counts);
81068 if(normFunc) normFunc(size, total, inc);
81069
81070 // after all normalization etc, now we can accumulate if desired
81071 if(cumulativeSpec.enabled) cdf(size, cumulativeSpec.direction, cumulativeSpec.currentbin);
81072
81073 var seriesLen = Math.min(pos.length, size.length);
81074 var cd = [];
81075 var firstNonzero = 0;
81076 var lastNonzero = seriesLen - 1;
81077
81078 // look for empty bins at the ends to remove, so autoscale omits them
81079 for(i = 0; i < seriesLen; i++) {
81080 if(size[i]) {
81081 firstNonzero = i;
81082 break;
81083 }
81084 }
81085 for(i = seriesLen - 1; i >= firstNonzero; i--) {
81086 if(size[i]) {
81087 lastNonzero = i;
81088 break;
81089 }
81090 }
81091
81092 // create the "calculated data" to plot
81093 for(i = firstNonzero; i <= lastNonzero; i++) {
81094 if((isNumeric(pos[i]) && isNumeric(size[i]))) {
81095 var cdi = {
81096 p: pos[i],
81097 s: size[i],
81098 b: 0
81099 };
81100
81101 // setup hover and event data fields,
81102 // N.B. pts and "hover" positions ph0/ph1 don't seem to make much sense
81103 // for cumulative distributions
81104 if(!cumulativeSpec.enabled) {
81105 cdi.pts = inputPoints[i];
81106 if(uniqueValsPerBin) {
81107 cdi.ph0 = cdi.ph1 = (inputPoints[i].length) ? pos0[inputPoints[i][0]] : pos[i];
81108 } else {
81109 // Defer evaluation of ph(0|1) in crossTraceCalc
81110 trace._computePh = true;
81111 cdi.ph0 = roundFn(binEdges[i]);
81112 cdi.ph1 = roundFn(binEdges[i + 1], true);
81113 }
81114 }
81115 cd.push(cdi);
81116 }
81117 }
81118
81119 if(cd.length === 1) {
81120 // when we collapse to a single bin, calcdata no longer describes bin size
81121 // so we need to explicitly specify it
81122 cd[0].width1 = Axes.tickIncrement(cd[0].p, binSpec.size, false, calendar) - cd[0].p;
81123 }
81124
81125 arraysToCalcdata(cd, trace);
81126
81127 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
81128 Lib.tagSelected(cd, trace, ptNumber2cdIndex);
81129 }
81130
81131 return cd;
81132}
81133
81134/*
81135 * calcAllAutoBins: we want all histograms inside the same bingroup
81136 * (see logic in Histogram.crossTraceDefaults) to share bin specs
81137 *
81138 * If the user has explicitly specified differing
81139 * bin specs, there's nothing we can do, but if possible we will try to use the
81140 * smallest bins of any of the auto values for all histograms inside the same
81141 * bingroup.
81142 */
81143function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) {
81144 var binAttr = mainData + 'bins';
81145 var fullLayout = gd._fullLayout;
81146 var groupName = trace['_' + mainData + 'bingroup'];
81147 var binOpts = fullLayout._histogramBinOpts[groupName];
81148 var isOverlay = fullLayout.barmode === 'overlay';
81149 var i, traces, tracei, calendar, pos0, autoVals, cumulativeSpec;
81150
81151 var r2c = function(v) { return pa.r2c(v, 0, calendar); };
81152 var c2r = function(v) { return pa.c2r(v, 0, calendar); };
81153
81154 var cleanBound = pa.type === 'date' ?
81155 function(v) { return (v || v === 0) ? Lib.cleanDate(v, null, calendar) : null; } :
81156 function(v) { return isNumeric(v) ? Number(v) : null; };
81157
81158 function setBound(attr, bins, newBins) {
81159 if(bins[attr + 'Found']) {
81160 bins[attr] = cleanBound(bins[attr]);
81161 if(bins[attr] === null) bins[attr] = newBins[attr];
81162 } else {
81163 autoVals[attr] = bins[attr] = newBins[attr];
81164 Lib.nestedProperty(traces[0], binAttr + '.' + attr).set(newBins[attr]);
81165 }
81166 }
81167
81168 // all but the first trace in this group has already been marked finished
81169 // clear this flag, so next time we run calc we will run autobin again
81170 if(trace['_' + mainData + 'autoBinFinished']) {
81171 delete trace['_' + mainData + 'autoBinFinished'];
81172 } else {
81173 traces = binOpts.traces;
81174 var allPos = [];
81175
81176 // Note: we're including `legendonly` traces here for autobin purposes,
81177 // so that showing & hiding from the legend won't affect bins.
81178 // But this complicates things a bit since those traces don't `calc`,
81179 // hence `isFirstVisible`.
81180 var isFirstVisible = true;
81181 var has2dMap = false;
81182 var hasHist2dContour = false;
81183 for(i = 0; i < traces.length; i++) {
81184 tracei = traces[i];
81185
81186 if(tracei.visible) {
81187 var mainDatai = binOpts.dirs[i];
81188 pos0 = tracei['_' + mainDatai + 'pos0'] = pa.makeCalcdata(tracei, mainDatai);
81189
81190 allPos = Lib.concat(allPos, pos0);
81191 delete tracei['_' + mainData + 'autoBinFinished'];
81192
81193 if(trace.visible === true) {
81194 if(isFirstVisible) {
81195 isFirstVisible = false;
81196 } else {
81197 delete tracei._autoBin;
81198 tracei['_' + mainData + 'autoBinFinished'] = 1;
81199 }
81200 if(Registry.traceIs(tracei, '2dMap')) {
81201 has2dMap = true;
81202 }
81203 if(tracei.type === 'histogram2dcontour') {
81204 hasHist2dContour = true;
81205 }
81206 }
81207 }
81208 }
81209
81210 calendar = traces[0][mainData + 'calendar'];
81211 var newBinSpec = Axes.autoBin(allPos, pa, binOpts.nbins, has2dMap, calendar, binOpts.sizeFound && binOpts.size);
81212
81213 var autoBin = traces[0]._autoBin = {};
81214 autoVals = autoBin[binOpts.dirs[0]] = {};
81215
81216 if(hasHist2dContour) {
81217 // the "true" 2nd argument reverses the tick direction (which we can't
81218 // just do with a minus sign because of month bins)
81219 if(!binOpts.size) {
81220 newBinSpec.start = c2r(Axes.tickIncrement(
81221 r2c(newBinSpec.start), newBinSpec.size, true, calendar));
81222 }
81223 if(binOpts.end === undefined) {
81224 newBinSpec.end = c2r(Axes.tickIncrement(
81225 r2c(newBinSpec.end), newBinSpec.size, false, calendar));
81226 }
81227 }
81228
81229 // Edge case: single-valued histogram overlaying others
81230 // Use them all together to calculate the bin size for the single-valued one
81231 if(isOverlay && !Registry.traceIs(trace, '2dMap') && newBinSpec._dataSpan === 0 &&
81232 pa.type !== 'category' && pa.type !== 'multicategory') {
81233 // Several single-valued histograms! Stop infinite recursion,
81234 // just return an extra flag that tells handleSingleValueOverlays
81235 // to sort out this trace too
81236 if(_overlayEdgeCase) return [newBinSpec, pos0, true];
81237
81238 newBinSpec = handleSingleValueOverlays(gd, trace, pa, mainData, binAttr);
81239 }
81240
81241 // adjust for CDF edge cases
81242 cumulativeSpec = tracei.cumulative || {};
81243 if(cumulativeSpec.enabled && (cumulativeSpec.currentbin !== 'include')) {
81244 if(cumulativeSpec.direction === 'decreasing') {
81245 newBinSpec.start = c2r(Axes.tickIncrement(
81246 r2c(newBinSpec.start), newBinSpec.size, true, calendar));
81247 } else {
81248 newBinSpec.end = c2r(Axes.tickIncrement(
81249 r2c(newBinSpec.end), newBinSpec.size, false, calendar));
81250 }
81251 }
81252
81253 binOpts.size = newBinSpec.size;
81254 if(!binOpts.sizeFound) {
81255 autoVals.size = newBinSpec.size;
81256 Lib.nestedProperty(traces[0], binAttr + '.size').set(newBinSpec.size);
81257 }
81258
81259 setBound('start', binOpts, newBinSpec);
81260 setBound('end', binOpts, newBinSpec);
81261 }
81262
81263 pos0 = trace['_' + mainData + 'pos0'];
81264 delete trace['_' + mainData + 'pos0'];
81265
81266 // Each trace can specify its own start/end, or if omitted
81267 // we ensure they're beyond the bounds of this trace's data,
81268 // and we need to make sure start is aligned with the main start
81269 var traceInputBins = trace._input[binAttr] || {};
81270 var traceBinOptsCalc = Lib.extendFlat({}, binOpts);
81271 var mainStart = binOpts.start;
81272 var startIn = pa.r2l(traceInputBins.start);
81273 var hasStart = startIn !== undefined;
81274 if((binOpts.startFound || hasStart) && startIn !== pa.r2l(mainStart)) {
81275 // We have an explicit start to reconcile across traces
81276 // if this trace has an explicit start, shift it down to a bin edge
81277 // if another trace had an explicit start, shift it down to a
81278 // bin edge past our data
81279 var traceStart = hasStart ?
81280 startIn :
81281 Lib.aggNums(Math.min, null, pos0);
81282
81283 var dummyAx = {
81284 type: (pa.type === 'category' || pa.type === 'multicategory') ? 'linear' : pa.type,
81285 r2l: pa.r2l,
81286 dtick: binOpts.size,
81287 tick0: mainStart,
81288 calendar: calendar,
81289 range: ([traceStart, Axes.tickIncrement(traceStart, binOpts.size, false, calendar)]).map(pa.l2r)
81290 };
81291 var newStart = Axes.tickFirst(dummyAx);
81292 if(newStart > pa.r2l(traceStart)) {
81293 newStart = Axes.tickIncrement(newStart, binOpts.size, true, calendar);
81294 }
81295 traceBinOptsCalc.start = pa.l2r(newStart);
81296 if(!hasStart) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.start);
81297 }
81298
81299 var mainEnd = binOpts.end;
81300 var endIn = pa.r2l(traceInputBins.end);
81301 var hasEnd = endIn !== undefined;
81302 if((binOpts.endFound || hasEnd) && endIn !== pa.r2l(mainEnd)) {
81303 // Reconciling an explicit end is easier, as it doesn't need to
81304 // match bin edges
81305 var traceEnd = hasEnd ?
81306 endIn :
81307 Lib.aggNums(Math.max, null, pos0);
81308
81309 traceBinOptsCalc.end = pa.l2r(traceEnd);
81310 if(!hasEnd) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.end);
81311 }
81312
81313 // Backward compatibility for one-time autobinning.
81314 // autobin: true is handled in cleanData, but autobin: false
81315 // needs to be here where we have determined the values.
81316 var autoBinAttr = 'autobin' + mainData;
81317 if(trace._input[autoBinAttr] === false) {
81318 trace._input[binAttr] = Lib.extendFlat({}, trace[binAttr] || {});
81319 delete trace._input[autoBinAttr];
81320 delete trace[autoBinAttr];
81321 }
81322
81323 return [traceBinOptsCalc, pos0];
81324}
81325
81326/*
81327 * Adjust single-value histograms in overlay mode to make as good a
81328 * guess as we can at autobin values the user would like.
81329 *
81330 * Returns the binSpec for the trace that sparked all this
81331 */
81332function handleSingleValueOverlays(gd, trace, pa, mainData, binAttr) {
81333 var fullLayout = gd._fullLayout;
81334 var overlaidTraceGroup = getConnectedHistograms(gd, trace);
81335 var pastThisTrace = false;
81336 var minSize = Infinity;
81337 var singleValuedTraces = [trace];
81338 var i, tracei, binOpts;
81339
81340 // first collect all the:
81341 // - min bin size from all multi-valued traces
81342 // - single-valued traces
81343 for(i = 0; i < overlaidTraceGroup.length; i++) {
81344 tracei = overlaidTraceGroup[i];
81345
81346 if(tracei === trace) {
81347 pastThisTrace = true;
81348 } else if(!pastThisTrace) {
81349 // This trace has already had its autobins calculated, so either:
81350 // - it is part of a bingroup
81351 // - it is NOT a single-valued trace
81352 binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']];
81353 minSize = Math.min(minSize, binOpts.size || tracei[binAttr].size);
81354 } else {
81355 var resulti = calcAllAutoBins(gd, tracei, pa, mainData, true);
81356 var binSpeci = resulti[0];
81357 var isSingleValued = resulti[2];
81358
81359 // so we can use this result when we get to tracei in the normal
81360 // course of events, mark it as done and put _pos0 back
81361 tracei['_' + mainData + 'autoBinFinished'] = 1;
81362 tracei['_' + mainData + 'pos0'] = resulti[1];
81363
81364 if(isSingleValued) {
81365 singleValuedTraces.push(tracei);
81366 } else {
81367 minSize = Math.min(minSize, binSpeci.size);
81368 }
81369 }
81370 }
81371
81372 // find the real data values for each single-valued trace
81373 // hunt through pos0 for the first valid value
81374 var dataVals = new Array(singleValuedTraces.length);
81375 for(i = 0; i < singleValuedTraces.length; i++) {
81376 var pos0 = singleValuedTraces[i]['_' + mainData + 'pos0'];
81377 for(var j = 0; j < pos0.length; j++) {
81378 if(pos0[j] !== undefined) {
81379 dataVals[i] = pos0[j];
81380 break;
81381 }
81382 }
81383 }
81384
81385 // are ALL traces are single-valued? use the min difference between
81386 // all of their values (which defaults to 1 if there's still only one)
81387 if(!isFinite(minSize)) {
81388 minSize = Lib.distinctVals(dataVals).minDiff;
81389 }
81390
81391 // now apply the min size we found to all single-valued traces
81392 for(i = 0; i < singleValuedTraces.length; i++) {
81393 tracei = singleValuedTraces[i];
81394 var calendar = tracei[mainData + 'calendar'];
81395
81396 var newBins = {
81397 start: pa.c2r(dataVals[i] - minSize / 2, 0, calendar),
81398 end: pa.c2r(dataVals[i] + minSize / 2, 0, calendar),
81399 size: minSize
81400 };
81401
81402 tracei._input[binAttr] = tracei[binAttr] = newBins;
81403
81404 binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']];
81405 if(binOpts) Lib.extendFlat(binOpts, newBins);
81406 }
81407
81408 return trace[binAttr];
81409}
81410
81411/*
81412 * Return an array of histograms that share axes and orientation.
81413 *
81414 * Only considers histograms. In principle we could include bars in a
81415 * similar way to how we do manually binned histograms, though this
81416 * would have tons of edge cases and value judgments to make.
81417 */
81418function getConnectedHistograms(gd, trace) {
81419 var xid = trace.xaxis;
81420 var yid = trace.yaxis;
81421 var orientation = trace.orientation;
81422
81423 var out = [];
81424 var fullData = gd._fullData;
81425 for(var i = 0; i < fullData.length; i++) {
81426 var tracei = fullData[i];
81427 if(tracei.type === 'histogram' &&
81428 tracei.visible === true &&
81429 tracei.orientation === orientation &&
81430 tracei.xaxis === xid && tracei.yaxis === yid
81431 ) {
81432 out.push(tracei);
81433 }
81434 }
81435
81436 return out;
81437}
81438
81439function cdf(size, direction, currentBin) {
81440 var i, vi, prevSum;
81441
81442 function firstHalfPoint(i) {
81443 prevSum = size[i];
81444 size[i] /= 2;
81445 }
81446
81447 function nextHalfPoint(i) {
81448 vi = size[i];
81449 size[i] = prevSum + vi / 2;
81450 prevSum += vi;
81451 }
81452
81453 if(currentBin === 'half') {
81454 if(direction === 'increasing') {
81455 firstHalfPoint(0);
81456 for(i = 1; i < size.length; i++) {
81457 nextHalfPoint(i);
81458 }
81459 } else {
81460 firstHalfPoint(size.length - 1);
81461 for(i = size.length - 2; i >= 0; i--) {
81462 nextHalfPoint(i);
81463 }
81464 }
81465 } else if(direction === 'increasing') {
81466 for(i = 1; i < size.length; i++) {
81467 size[i] += size[i - 1];
81468 }
81469
81470 // 'exclude' is identical to 'include' just shifted one bin over
81471 if(currentBin === 'exclude') {
81472 size.unshift(0);
81473 size.pop();
81474 }
81475 } else {
81476 for(i = size.length - 2; i >= 0; i--) {
81477 size[i] += size[i + 1];
81478 }
81479
81480 if(currentBin === 'exclude') {
81481 size.push(0);
81482 size.shift();
81483 }
81484 }
81485}
81486
81487module.exports = {
81488 calc: calc,
81489 calcAllAutoBins: calcAllAutoBins
81490};
81491
81492},{"../../lib":178,"../../plots/cartesian/axes":222,"../../registry":269,"../bar/arrays_to_calcdata":278,"./average":346,"./bin_functions":348,"./bin_label_vals":349,"./norm_functions":357,"fast-isnumeric":18}],351:[function(_dereq_,module,exports){
81493/**
81494* Copyright 2012-2020, Plotly, Inc.
81495* All rights reserved.
81496*
81497* This source code is licensed under the MIT license found in the
81498* LICENSE file in the root directory of this source tree.
81499*/
81500
81501
81502'use strict';
81503
81504module.exports = {
81505 eventDataKeys: ['binNumber']
81506};
81507
81508},{}],352:[function(_dereq_,module,exports){
81509/**
81510* Copyright 2012-2020, Plotly, Inc.
81511* All rights reserved.
81512*
81513* This source code is licensed under the MIT license found in the
81514* LICENSE file in the root directory of this source tree.
81515*/
81516
81517'use strict';
81518
81519var Lib = _dereq_('../../lib');
81520var axisIds = _dereq_('../../plots/cartesian/axis_ids');
81521
81522var traceIs = _dereq_('../../registry').traceIs;
81523var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults;
81524
81525var nestedProperty = Lib.nestedProperty;
81526var getAxisGroup = axisIds.getAxisGroup;
81527
81528var BINATTRS = [
81529 {aStr: {x: 'xbins.start', y: 'ybins.start'}, name: 'start'},
81530 {aStr: {x: 'xbins.end', y: 'ybins.end'}, name: 'end'},
81531 {aStr: {x: 'xbins.size', y: 'ybins.size'}, name: 'size'},
81532 {aStr: {x: 'nbinsx', y: 'nbinsy'}, name: 'nbins'}
81533];
81534
81535var BINDIRECTIONS = ['x', 'y'];
81536
81537// handle bin attrs and relink auto-determined values so fullData is complete
81538module.exports = function crossTraceDefaults(fullData, fullLayout) {
81539 var allBinOpts = fullLayout._histogramBinOpts = {};
81540 var histTraces = [];
81541 var mustMatchTracesLookup = {};
81542 var otherTracesList = [];
81543
81544 var traceOut, traces, groupName, binDir;
81545 var i, j, k;
81546
81547 function coerce(attr, dflt) {
81548 return Lib.coerce(traceOut._input, traceOut, traceOut._module.attributes, attr, dflt);
81549 }
81550
81551 function orientation2binDir(traceOut) {
81552 return traceOut.orientation === 'v' ? 'x' : 'y';
81553 }
81554
81555 function getAxisType(traceOut, binDir) {
81556 var ax = axisIds.getFromTrace({_fullLayout: fullLayout}, traceOut, binDir);
81557 return ax.type;
81558 }
81559
81560 function fillBinOpts(traceOut, groupName, binDir) {
81561 // N.B. group traces that don't have a bingroup with themselves
81562 var fallbackGroupName = traceOut.uid + '__' + binDir;
81563 if(!groupName) groupName = fallbackGroupName;
81564
81565 var axType = getAxisType(traceOut, binDir);
81566 var calendar = traceOut[binDir + 'calendar'] || '';
81567 var binOpts = allBinOpts[groupName];
81568 var needsNewItem = true;
81569
81570 if(binOpts) {
81571 if(axType === binOpts.axType && calendar === binOpts.calendar) {
81572 needsNewItem = false;
81573 binOpts.traces.push(traceOut);
81574 binOpts.dirs.push(binDir);
81575 } else {
81576 groupName = fallbackGroupName;
81577
81578 if(axType !== binOpts.axType) {
81579 Lib.warn([
81580 'Attempted to group the bins of trace', traceOut.index,
81581 'set on a', 'type:' + axType, 'axis',
81582 'with bins on', 'type:' + binOpts.axType, 'axis.'
81583 ].join(' '));
81584 }
81585 if(calendar !== binOpts.calendar) {
81586 // prohibit bingroup for traces using different calendar,
81587 // there's probably a way to make this work, but skip for now
81588 Lib.warn([
81589 'Attempted to group the bins of trace', traceOut.index,
81590 'set with a', calendar, 'calendar',
81591 'with bins',
81592 (binOpts.calendar ? 'on a ' + binOpts.calendar + ' calendar' : 'w/o a set calendar')
81593 ].join(' '));
81594 }
81595 }
81596 }
81597
81598 if(needsNewItem) {
81599 allBinOpts[groupName] = {
81600 traces: [traceOut],
81601 dirs: [binDir],
81602 axType: axType,
81603 calendar: traceOut[binDir + 'calendar'] || ''
81604 };
81605 }
81606 traceOut['_' + binDir + 'bingroup'] = groupName;
81607 }
81608
81609 for(i = 0; i < fullData.length; i++) {
81610 traceOut = fullData[i];
81611
81612 if(traceIs(traceOut, 'histogram')) {
81613 histTraces.push(traceOut);
81614
81615 // TODO: this shouldn't be relinked as it's only used within calc
81616 // https://github.com/plotly/plotly.js/issues/749
81617 delete traceOut._xautoBinFinished;
81618 delete traceOut._yautoBinFinished;
81619
81620 // N.B. need to coerce *alignmentgroup* before *bingroup*, as traces
81621 // in same alignmentgroup "have to match"
81622 if(!traceIs(traceOut, '2dMap')) {
81623 handleGroupingDefaults(traceOut._input, traceOut, fullLayout, coerce);
81624 }
81625 }
81626 }
81627
81628 var alignmentOpts = fullLayout._alignmentOpts || {};
81629
81630 // Look for traces that "have to match", that is:
81631 // - 1d histogram traces on the same subplot with same orientation under barmode:stack,
81632 // - 1d histogram traces on the same subplot with same orientation under barmode:group
81633 // - 1d histogram traces on the same position axis with the same orientation
81634 // and the same *alignmentgroup* (coerced under barmode:group)
81635 // - Once `stackgroup` gets implemented (see https://github.com/plotly/plotly.js/issues/3614),
81636 // traces within the same stackgroup will also "have to match"
81637 for(i = 0; i < histTraces.length; i++) {
81638 traceOut = histTraces[i];
81639 groupName = '';
81640
81641 if(!traceIs(traceOut, '2dMap')) {
81642 binDir = orientation2binDir(traceOut);
81643
81644 if(fullLayout.barmode === 'group' && traceOut.alignmentgroup) {
81645 var pa = traceOut[binDir + 'axis'];
81646 var aGroupId = getAxisGroup(fullLayout, pa) + traceOut.orientation;
81647 if((alignmentOpts[aGroupId] || {})[traceOut.alignmentgroup]) {
81648 groupName = aGroupId;
81649 }
81650 }
81651
81652 if(!groupName && fullLayout.barmode !== 'overlay') {
81653 groupName = (
81654 getAxisGroup(fullLayout, traceOut.xaxis) +
81655 getAxisGroup(fullLayout, traceOut.yaxis) +
81656 orientation2binDir(traceOut)
81657 );
81658 }
81659 }
81660
81661 if(groupName) {
81662 if(!mustMatchTracesLookup[groupName]) {
81663 mustMatchTracesLookup[groupName] = [];
81664 }
81665 mustMatchTracesLookup[groupName].push(traceOut);
81666 } else {
81667 otherTracesList.push(traceOut);
81668 }
81669 }
81670
81671 // Setup binOpts for traces that have to match,
81672 // if the traces have a valid bingroup, use that
81673 // if not use axis+binDir groupName
81674 for(groupName in mustMatchTracesLookup) {
81675 traces = mustMatchTracesLookup[groupName];
81676
81677 // no need to 'force' anything when a single
81678 // trace is detected as "must match"
81679 if(traces.length === 1) {
81680 otherTracesList.push(traces[0]);
81681 continue;
81682 }
81683
81684 var binGroupFound = false;
81685 for(i = 0; i < traces.length; i++) {
81686 traceOut = traces[i];
81687 binGroupFound = coerce('bingroup');
81688 break;
81689 }
81690
81691 groupName = binGroupFound || groupName;
81692
81693 for(i = 0; i < traces.length; i++) {
81694 traceOut = traces[i];
81695 var bingroupIn = traceOut._input.bingroup;
81696 if(bingroupIn && bingroupIn !== groupName) {
81697 Lib.warn([
81698 'Trace', traceOut.index, 'must match',
81699 'within bingroup', groupName + '.',
81700 'Ignoring its bingroup:', bingroupIn, 'setting.'
81701 ].join(' '));
81702 }
81703 traceOut.bingroup = groupName;
81704
81705 // N.B. no need to worry about 2dMap case
81706 // (where both bin direction are set in each trace)
81707 // as 2dMap trace never "have to match"
81708 fillBinOpts(traceOut, groupName, orientation2binDir(traceOut));
81709 }
81710 }
81711
81712 // setup binOpts for traces that can but don't have to match,
81713 // notice that these traces can be matched with traces that have to match
81714 for(i = 0; i < otherTracesList.length; i++) {
81715 traceOut = otherTracesList[i];
81716
81717 var binGroup = coerce('bingroup');
81718
81719 if(traceIs(traceOut, '2dMap')) {
81720 for(k = 0; k < 2; k++) {
81721 binDir = BINDIRECTIONS[k];
81722 var binGroupInDir = coerce(binDir + 'bingroup',
81723 binGroup ? binGroup + '__' + binDir : null
81724 );
81725 fillBinOpts(traceOut, binGroupInDir, binDir);
81726 }
81727 } else {
81728 fillBinOpts(traceOut, binGroup, orientation2binDir(traceOut));
81729 }
81730 }
81731
81732 // coerce bin attrs!
81733 for(groupName in allBinOpts) {
81734 var binOpts = allBinOpts[groupName];
81735 traces = binOpts.traces;
81736
81737 for(j = 0; j < BINATTRS.length; j++) {
81738 var attrSpec = BINATTRS[j];
81739 var attr = attrSpec.name;
81740 var aStr;
81741 var autoVals;
81742
81743 // nbins(x|y) is moot if we have a size. This depends on
81744 // nbins coming after size in binAttrs.
81745 if(attr === 'nbins' && binOpts.sizeFound) continue;
81746
81747 for(i = 0; i < traces.length; i++) {
81748 traceOut = traces[i];
81749 binDir = binOpts.dirs[i];
81750 aStr = attrSpec.aStr[binDir];
81751
81752 if(nestedProperty(traceOut._input, aStr).get() !== undefined) {
81753 binOpts[attr] = coerce(aStr);
81754 binOpts[attr + 'Found'] = true;
81755 break;
81756 }
81757
81758 autoVals = (traceOut._autoBin || {})[binDir] || {};
81759 if(autoVals[attr]) {
81760 // if this is the *first* autoval
81761 nestedProperty(traceOut, aStr).set(autoVals[attr]);
81762 }
81763 }
81764
81765 // start and end we need to coerce anyway, after having collected the
81766 // first of each into binOpts, in case a trace wants to restrict its
81767 // data to a certain range
81768 if(attr === 'start' || attr === 'end') {
81769 for(; i < traces.length; i++) {
81770 traceOut = traces[i];
81771 if(traceOut['_' + binDir + 'bingroup']) {
81772 autoVals = (traceOut._autoBin || {})[binDir] || {};
81773 coerce(aStr, autoVals[attr]);
81774 }
81775 }
81776 }
81777
81778 if(attr === 'nbins' && !binOpts.sizeFound && !binOpts.nbinsFound) {
81779 traceOut = traces[0];
81780 binOpts[attr] = coerce(aStr);
81781 }
81782 }
81783 }
81784};
81785
81786},{"../../lib":178,"../../plots/cartesian/axis_ids":225,"../../registry":269,"../bar/defaults":283}],353:[function(_dereq_,module,exports){
81787/**
81788* Copyright 2012-2020, Plotly, Inc.
81789* All rights reserved.
81790*
81791* This source code is licensed under the MIT license found in the
81792* LICENSE file in the root directory of this source tree.
81793*/
81794
81795'use strict';
81796
81797var Registry = _dereq_('../../registry');
81798var Lib = _dereq_('../../lib');
81799var Color = _dereq_('../../components/color');
81800
81801var handleStyleDefaults = _dereq_('../bar/style_defaults');
81802var attributes = _dereq_('./attributes');
81803
81804module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
81805 function coerce(attr, dflt) {
81806 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
81807 }
81808
81809 var x = coerce('x');
81810 var y = coerce('y');
81811
81812 var cumulative = coerce('cumulative.enabled');
81813 if(cumulative) {
81814 coerce('cumulative.direction');
81815 coerce('cumulative.currentbin');
81816 }
81817
81818 coerce('text');
81819 coerce('hovertext');
81820 coerce('hovertemplate');
81821
81822 var orientation = coerce('orientation', (y && !x) ? 'h' : 'v');
81823 var sampleLetter = orientation === 'v' ? 'x' : 'y';
81824 var aggLetter = orientation === 'v' ? 'y' : 'x';
81825
81826 var len = (x && y) ?
81827 Math.min(Lib.minRowLength(x) && Lib.minRowLength(y)) :
81828 Lib.minRowLength(traceOut[sampleLetter] || []);
81829
81830 if(!len) {
81831 traceOut.visible = false;
81832 return;
81833 }
81834
81835 traceOut._length = len;
81836
81837 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
81838 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
81839
81840 var hasAggregationData = traceOut[aggLetter];
81841 if(hasAggregationData) coerce('histfunc');
81842 coerce('histnorm');
81843
81844 // Note: bin defaults are now handled in Histogram.crossTraceDefaults
81845 // autobin(x|y) are only included here to appease Plotly.validate
81846 coerce('autobin' + sampleLetter);
81847
81848 handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
81849
81850 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
81851
81852 var lineColor = (traceOut.marker.line || {}).color;
81853
81854 // override defaultColor for error bars with defaultLine
81855 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
81856 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
81857 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
81858};
81859
81860},{"../../components/color":52,"../../lib":178,"../../registry":269,"../bar/style_defaults":294,"./attributes":345}],354:[function(_dereq_,module,exports){
81861/**
81862* Copyright 2012-2020, Plotly, Inc.
81863* All rights reserved.
81864*
81865* This source code is licensed under the MIT license found in the
81866* LICENSE file in the root directory of this source tree.
81867*/
81868
81869'use strict';
81870
81871module.exports = function eventData(out, pt, trace, cd, pointNumber) {
81872 // standard cartesian event data
81873 out.x = 'xVal' in pt ? pt.xVal : pt.x;
81874 out.y = 'yVal' in pt ? pt.yVal : pt.y;
81875
81876 // for 2d histograms
81877 if('zLabelVal' in pt) out.z = pt.zLabelVal;
81878
81879 if(pt.xa) out.xaxis = pt.xa;
81880 if(pt.ya) out.yaxis = pt.ya;
81881
81882 // specific to histogram - CDFs do not have pts (yet?)
81883 if(!(trace.cumulative || {}).enabled) {
81884 var pts = Array.isArray(pointNumber) ?
81885 cd[0].pts[pointNumber[0]][pointNumber[1]] :
81886 cd[pointNumber].pts;
81887
81888 out.pointNumbers = pts;
81889 out.binNumber = out.pointNumber;
81890 delete out.pointNumber;
81891 delete out.pointIndex;
81892
81893 var pointIndices;
81894 if(trace._indexToPoints) {
81895 pointIndices = [];
81896 for(var i = 0; i < pts.length; i++) {
81897 pointIndices = pointIndices.concat(trace._indexToPoints[pts[i]]);
81898 }
81899 } else {
81900 pointIndices = pts;
81901 }
81902
81903 out.pointIndices = pointIndices;
81904 }
81905
81906 return out;
81907};
81908
81909},{}],355:[function(_dereq_,module,exports){
81910/**
81911* Copyright 2012-2020, Plotly, Inc.
81912* All rights reserved.
81913*
81914* This source code is licensed under the MIT license found in the
81915* LICENSE file in the root directory of this source tree.
81916*/
81917
81918
81919'use strict';
81920
81921var barHover = _dereq_('../bar/hover').hoverPoints;
81922var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
81923
81924module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
81925 var pts = barHover(pointData, xval, yval, hovermode);
81926
81927 if(!pts) return;
81928
81929 pointData = pts[0];
81930 var di = pointData.cd[pointData.index];
81931 var trace = pointData.cd[0].trace;
81932
81933 if(!trace.cumulative.enabled) {
81934 var posLetter = trace.orientation === 'h' ? 'y' : 'x';
81935
81936 pointData[posLetter + 'Label'] = hoverLabelText(pointData[posLetter + 'a'], di.ph0, di.ph1);
81937 }
81938
81939 return pts;
81940};
81941
81942},{"../../plots/cartesian/axes":222,"../bar/hover":286}],356:[function(_dereq_,module,exports){
81943/**
81944* Copyright 2012-2020, Plotly, Inc.
81945* All rights reserved.
81946*
81947* This source code is licensed under the MIT license found in the
81948* LICENSE file in the root directory of this source tree.
81949*/
81950
81951'use strict';
81952
81953/**
81954 * Histogram has its own attribute, defaults and calc steps,
81955 * but uses bar's plot to display
81956 * and bar's crossTraceCalc (formerly known as setPositions) for stacking and grouping
81957 */
81958
81959/**
81960 * histogram errorBarsOK is debatable, but it's put in for backward compat.
81961 * there are use cases for it - sqrt for a simple histogram works right now,
81962 * constant and % work but they're not so meaningful. I guess it could be cool
81963 * to allow quadrature combination of errors in summed histograms...
81964 */
81965
81966module.exports = {
81967 attributes: _dereq_('./attributes'),
81968 layoutAttributes: _dereq_('../bar/layout_attributes'),
81969 supplyDefaults: _dereq_('./defaults'),
81970 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
81971 supplyLayoutDefaults: _dereq_('../bar/layout_defaults'),
81972 calc: _dereq_('./calc').calc,
81973 crossTraceCalc: _dereq_('../bar/cross_trace_calc').crossTraceCalc,
81974 plot: _dereq_('../bar/plot').plot,
81975 layerName: 'barlayer',
81976 style: _dereq_('../bar/style').style,
81977 styleOnSelect: _dereq_('../bar/style').styleOnSelect,
81978 colorbar: _dereq_('../scatter/marker_colorbar'),
81979 hoverPoints: _dereq_('./hover'),
81980 selectPoints: _dereq_('../bar/select'),
81981 eventData: _dereq_('./event_data'),
81982
81983 moduleType: 'trace',
81984 name: 'histogram',
81985 basePlotModule: _dereq_('../../plots/cartesian'),
81986 categories: ['bar-like', 'cartesian', 'svg', 'bar', 'histogram', 'oriented', 'errorBarsOK', 'showLegend'],
81987 meta: {
81988
81989 }
81990};
81991
81992},{"../../plots/cartesian":235,"../bar/cross_trace_calc":282,"../bar/layout_attributes":288,"../bar/layout_defaults":289,"../bar/plot":290,"../bar/select":291,"../bar/style":293,"../scatter/marker_colorbar":407,"./attributes":345,"./calc":350,"./cross_trace_defaults":352,"./defaults":353,"./event_data":354,"./hover":355}],357:[function(_dereq_,module,exports){
81993/**
81994* Copyright 2012-2020, Plotly, Inc.
81995* All rights reserved.
81996*
81997* This source code is licensed under the MIT license found in the
81998* LICENSE file in the root directory of this source tree.
81999*/
82000
82001
82002'use strict';
82003
82004
82005module.exports = {
82006 percent: function(size, total) {
82007 var nMax = size.length;
82008 var norm = 100 / total;
82009 for(var n = 0; n < nMax; n++) size[n] *= norm;
82010 },
82011 probability: function(size, total) {
82012 var nMax = size.length;
82013 for(var n = 0; n < nMax; n++) size[n] /= total;
82014 },
82015 density: function(size, total, inc, yinc) {
82016 var nMax = size.length;
82017 yinc = yinc || 1;
82018 for(var n = 0; n < nMax; n++) size[n] *= inc[n] * yinc;
82019 },
82020 'probability density': function(size, total, inc, yinc) {
82021 var nMax = size.length;
82022 if(yinc) total /= yinc;
82023 for(var n = 0; n < nMax; n++) size[n] *= inc[n] / total;
82024 }
82025};
82026
82027},{}],358:[function(_dereq_,module,exports){
82028/**
82029* Copyright 2012-2020, Plotly, Inc.
82030* All rights reserved.
82031*
82032* This source code is licensed under the MIT license found in the
82033* LICENSE file in the root directory of this source tree.
82034*/
82035
82036'use strict';
82037
82038var histogramAttrs = _dereq_('../histogram/attributes');
82039var makeBinAttrs = _dereq_('../histogram/bin_attributes');
82040var heatmapAttrs = _dereq_('../heatmap/attributes');
82041var baseAttrs = _dereq_('../../plots/attributes');
82042var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
82043var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
82044
82045var extendFlat = _dereq_('../../lib/extend').extendFlat;
82046
82047module.exports = extendFlat(
82048 {
82049 x: histogramAttrs.x,
82050 y: histogramAttrs.y,
82051
82052 z: {
82053 valType: 'data_array',
82054 editType: 'calc',
82055
82056 },
82057 marker: {
82058 color: {
82059 valType: 'data_array',
82060 editType: 'calc',
82061
82062 },
82063 editType: 'calc'
82064 },
82065
82066 histnorm: histogramAttrs.histnorm,
82067 histfunc: histogramAttrs.histfunc,
82068 nbinsx: histogramAttrs.nbinsx,
82069 xbins: makeBinAttrs('x'),
82070 nbinsy: histogramAttrs.nbinsy,
82071 ybins: makeBinAttrs('y'),
82072 autobinx: histogramAttrs.autobinx,
82073 autobiny: histogramAttrs.autobiny,
82074
82075 bingroup: extendFlat({}, histogramAttrs.bingroup, {
82076
82077 }),
82078 xbingroup: extendFlat({}, histogramAttrs.bingroup, {
82079
82080 }),
82081 ybingroup: extendFlat({}, histogramAttrs.bingroup, {
82082
82083 }),
82084
82085 xgap: heatmapAttrs.xgap,
82086 ygap: heatmapAttrs.ygap,
82087 zsmooth: heatmapAttrs.zsmooth,
82088 zhoverformat: heatmapAttrs.zhoverformat,
82089 hovertemplate: hovertemplateAttrs({}, {keys: 'z'}),
82090 showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
82091 },
82092 colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false})
82093);
82094
82095},{"../../components/colorscale/attributes":59,"../../lib/extend":173,"../../plots/attributes":219,"../../plots/template_attributes":264,"../heatmap/attributes":330,"../histogram/attributes":345,"../histogram/bin_attributes":347}],359:[function(_dereq_,module,exports){
82096/**
82097* Copyright 2012-2020, Plotly, Inc.
82098* All rights reserved.
82099*
82100* This source code is licensed under the MIT license found in the
82101* LICENSE file in the root directory of this source tree.
82102*/
82103
82104'use strict';
82105
82106var Lib = _dereq_('../../lib');
82107var Axes = _dereq_('../../plots/cartesian/axes');
82108
82109var binFunctions = _dereq_('../histogram/bin_functions');
82110var normFunctions = _dereq_('../histogram/norm_functions');
82111var doAvg = _dereq_('../histogram/average');
82112var getBinSpanLabelRound = _dereq_('../histogram/bin_label_vals');
82113var calcAllAutoBins = _dereq_('../histogram/calc').calcAllAutoBins;
82114
82115module.exports = function calc(gd, trace) {
82116 var xa = Axes.getFromId(gd, trace.xaxis);
82117 var ya = Axes.getFromId(gd, trace.yaxis);
82118
82119 var xcalendar = trace.xcalendar;
82120 var ycalendar = trace.ycalendar;
82121 var xr2c = function(v) { return xa.r2c(v, 0, xcalendar); };
82122 var yr2c = function(v) { return ya.r2c(v, 0, ycalendar); };
82123 var xc2r = function(v) { return xa.c2r(v, 0, xcalendar); };
82124 var yc2r = function(v) { return ya.c2r(v, 0, ycalendar); };
82125
82126 var i, j, n, m;
82127
82128 // calculate the bins
82129 var xBinsAndPos = calcAllAutoBins(gd, trace, xa, 'x');
82130 var xBinSpec = xBinsAndPos[0];
82131 var xPos0 = xBinsAndPos[1];
82132 var yBinsAndPos = calcAllAutoBins(gd, trace, ya, 'y');
82133 var yBinSpec = yBinsAndPos[0];
82134 var yPos0 = yBinsAndPos[1];
82135
82136 var serieslen = trace._length;
82137 if(xPos0.length > serieslen) xPos0.splice(serieslen, xPos0.length - serieslen);
82138 if(yPos0.length > serieslen) yPos0.splice(serieslen, yPos0.length - serieslen);
82139
82140 // make the empty bin array & scale the map
82141 var z = [];
82142 var onecol = [];
82143 var zerocol = [];
82144 var nonuniformBinsX = typeof xBinSpec.size === 'string';
82145 var nonuniformBinsY = typeof yBinSpec.size === 'string';
82146 var xEdges = [];
82147 var yEdges = [];
82148 var xbins = nonuniformBinsX ? xEdges : xBinSpec;
82149 var ybins = nonuniformBinsY ? yEdges : yBinSpec;
82150 var total = 0;
82151 var counts = [];
82152 var inputPoints = [];
82153 var norm = trace.histnorm;
82154 var func = trace.histfunc;
82155 var densitynorm = norm.indexOf('density') !== -1;
82156 var extremefunc = func === 'max' || func === 'min';
82157 var sizeinit = extremefunc ? null : 0;
82158 var binfunc = binFunctions.count;
82159 var normfunc = normFunctions[norm];
82160 var doavg = false;
82161 var xinc = [];
82162 var yinc = [];
82163
82164 // set a binning function other than count?
82165 // for binning functions: check first for 'z',
82166 // then 'mc' in case we had a colored scatter plot
82167 // and want to transfer these colors to the 2D histo
82168 // TODO: axe this, make it the responsibility of the app changing type? or an impliedEdit?
82169 var rawCounterData = ('z' in trace) ?
82170 trace.z :
82171 (('marker' in trace && Array.isArray(trace.marker.color)) ?
82172 trace.marker.color : '');
82173 if(rawCounterData && func !== 'count') {
82174 doavg = func === 'avg';
82175 binfunc = binFunctions[func];
82176 }
82177
82178 // decrease end a little in case of rounding errors
82179 var xBinSize = xBinSpec.size;
82180 var xBinStart = xr2c(xBinSpec.start);
82181 var xBinEnd = xr2c(xBinSpec.end) +
82182 (xBinStart - Axes.tickIncrement(xBinStart, xBinSize, false, xcalendar)) / 1e6;
82183
82184 for(i = xBinStart; i < xBinEnd; i = Axes.tickIncrement(i, xBinSize, false, xcalendar)) {
82185 onecol.push(sizeinit);
82186 xEdges.push(i);
82187 if(doavg) zerocol.push(0);
82188 }
82189 xEdges.push(i);
82190
82191 var nx = onecol.length;
82192 var dx = (i - xBinStart) / nx;
82193 var x0 = xc2r(xBinStart + dx / 2);
82194
82195 var yBinSize = yBinSpec.size;
82196 var yBinStart = yr2c(yBinSpec.start);
82197 var yBinEnd = yr2c(yBinSpec.end) +
82198 (yBinStart - Axes.tickIncrement(yBinStart, yBinSize, false, ycalendar)) / 1e6;
82199
82200 for(i = yBinStart; i < yBinEnd; i = Axes.tickIncrement(i, yBinSize, false, ycalendar)) {
82201 z.push(onecol.slice());
82202 yEdges.push(i);
82203 var ipCol = new Array(nx);
82204 for(j = 0; j < nx; j++) ipCol[j] = [];
82205 inputPoints.push(ipCol);
82206 if(doavg) counts.push(zerocol.slice());
82207 }
82208 yEdges.push(i);
82209
82210 var ny = z.length;
82211 var dy = (i - yBinStart) / ny;
82212 var y0 = yc2r(yBinStart + dy / 2);
82213
82214 if(densitynorm) {
82215 xinc = makeIncrements(onecol.length, xbins, dx, nonuniformBinsX);
82216 yinc = makeIncrements(z.length, ybins, dy, nonuniformBinsY);
82217 }
82218
82219 // for date axes we need bin bounds to be calcdata. For nonuniform bins
82220 // we already have this, but uniform with start/end/size they're still strings.
82221 if(!nonuniformBinsX && xa.type === 'date') xbins = binsToCalc(xr2c, xbins);
82222 if(!nonuniformBinsY && ya.type === 'date') ybins = binsToCalc(yr2c, ybins);
82223
82224 // put data into bins
82225 var uniqueValsPerX = true;
82226 var uniqueValsPerY = true;
82227 var xVals = new Array(nx);
82228 var yVals = new Array(ny);
82229 var xGapLow = Infinity;
82230 var xGapHigh = Infinity;
82231 var yGapLow = Infinity;
82232 var yGapHigh = Infinity;
82233 for(i = 0; i < serieslen; i++) {
82234 var xi = xPos0[i];
82235 var yi = yPos0[i];
82236 n = Lib.findBin(xi, xbins);
82237 m = Lib.findBin(yi, ybins);
82238 if(n >= 0 && n < nx && m >= 0 && m < ny) {
82239 total += binfunc(n, i, z[m], rawCounterData, counts[m]);
82240 inputPoints[m][n].push(i);
82241
82242 if(uniqueValsPerX) {
82243 if(xVals[n] === undefined) xVals[n] = xi;
82244 else if(xVals[n] !== xi) uniqueValsPerX = false;
82245 }
82246 if(uniqueValsPerY) {
82247 if(yVals[m] === undefined) yVals[m] = yi;
82248 else if(yVals[m] !== yi) uniqueValsPerY = false;
82249 }
82250
82251 xGapLow = Math.min(xGapLow, xi - xEdges[n]);
82252 xGapHigh = Math.min(xGapHigh, xEdges[n + 1] - xi);
82253 yGapLow = Math.min(yGapLow, yi - yEdges[m]);
82254 yGapHigh = Math.min(yGapHigh, yEdges[m + 1] - yi);
82255 }
82256 }
82257 // normalize, if needed
82258 if(doavg) {
82259 for(m = 0; m < ny; m++) total += doAvg(z[m], counts[m]);
82260 }
82261 if(normfunc) {
82262 for(m = 0; m < ny; m++) normfunc(z[m], total, xinc, yinc[m]);
82263 }
82264
82265 return {
82266 x: xPos0,
82267 xRanges: getRanges(xEdges, uniqueValsPerX && xVals, xGapLow, xGapHigh, xa, xcalendar),
82268 x0: x0,
82269 dx: dx,
82270 y: yPos0,
82271 yRanges: getRanges(yEdges, uniqueValsPerY && yVals, yGapLow, yGapHigh, ya, ycalendar),
82272 y0: y0,
82273 dy: dy,
82274 z: z,
82275 pts: inputPoints
82276 };
82277};
82278
82279function makeIncrements(len, bins, dv, nonuniform) {
82280 var out = new Array(len);
82281 var i;
82282 if(nonuniform) {
82283 for(i = 0; i < len; i++) out[i] = 1 / (bins[i + 1] - bins[i]);
82284 } else {
82285 var inc = 1 / dv;
82286 for(i = 0; i < len; i++) out[i] = inc;
82287 }
82288 return out;
82289}
82290
82291function binsToCalc(r2c, bins) {
82292 return {
82293 start: r2c(bins.start),
82294 end: r2c(bins.end),
82295 size: bins.size
82296 };
82297}
82298
82299function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) {
82300 var i;
82301 var len = edges.length - 1;
82302 var out = new Array(len);
82303 var roundFn = getBinSpanLabelRound(gapLow, gapHigh, edges, ax, calendar);
82304
82305 for(i = 0; i < len; i++) {
82306 var v = (uniqueVals || [])[i];
82307 out[i] = v === undefined ?
82308 [roundFn(edges[i]), roundFn(edges[i + 1], true)] :
82309 [v, v];
82310 }
82311 return out;
82312}
82313
82314},{"../../lib":178,"../../plots/cartesian/axes":222,"../histogram/average":346,"../histogram/bin_functions":348,"../histogram/bin_label_vals":349,"../histogram/calc":350,"../histogram/norm_functions":357}],360:[function(_dereq_,module,exports){
82315/**
82316* Copyright 2012-2020, Plotly, Inc.
82317* All rights reserved.
82318*
82319* This source code is licensed under the MIT license found in the
82320* LICENSE file in the root directory of this source tree.
82321*/
82322
82323
82324'use strict';
82325
82326var Lib = _dereq_('../../lib');
82327
82328var handleSampleDefaults = _dereq_('./sample_defaults');
82329var handleStyleDefaults = _dereq_('../heatmap/style_defaults');
82330var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
82331var attributes = _dereq_('./attributes');
82332
82333
82334module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
82335 function coerce(attr, dflt) {
82336 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
82337 }
82338
82339 handleSampleDefaults(traceIn, traceOut, coerce, layout);
82340 if(traceOut.visible === false) return;
82341
82342 handleStyleDefaults(traceIn, traceOut, coerce, layout);
82343 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
82344 coerce('hovertemplate');
82345};
82346
82347},{"../../components/colorscale/defaults":62,"../../lib":178,"../heatmap/style_defaults":343,"./attributes":358,"./sample_defaults":363}],361:[function(_dereq_,module,exports){
82348/**
82349* Copyright 2012-2020, Plotly, Inc.
82350* All rights reserved.
82351*
82352* This source code is licensed under the MIT license found in the
82353* LICENSE file in the root directory of this source tree.
82354*/
82355
82356
82357'use strict';
82358
82359var heatmapHover = _dereq_('../heatmap/hover');
82360var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
82361
82362module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) {
82363 var pts = heatmapHover(pointData, xval, yval, hovermode, hoverLayer, contour);
82364
82365 if(!pts) return;
82366
82367 pointData = pts[0];
82368 var indices = pointData.index;
82369 var ny = indices[0];
82370 var nx = indices[1];
82371 var cd0 = pointData.cd[0];
82372 var xRange = cd0.xRanges[nx];
82373 var yRange = cd0.yRanges[ny];
82374
82375 pointData.xLabel = hoverLabelText(pointData.xa, xRange[0], xRange[1]);
82376 pointData.yLabel = hoverLabelText(pointData.ya, yRange[0], yRange[1]);
82377
82378 return pts;
82379};
82380
82381},{"../../plots/cartesian/axes":222,"../heatmap/hover":337}],362:[function(_dereq_,module,exports){
82382/**
82383* Copyright 2012-2020, Plotly, Inc.
82384* All rights reserved.
82385*
82386* This source code is licensed under the MIT license found in the
82387* LICENSE file in the root directory of this source tree.
82388*/
82389
82390'use strict';
82391
82392module.exports = {
82393 attributes: _dereq_('./attributes'),
82394 supplyDefaults: _dereq_('./defaults'),
82395 crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'),
82396 calc: _dereq_('../heatmap/calc'),
82397 plot: _dereq_('../heatmap/plot'),
82398 layerName: 'heatmaplayer',
82399 colorbar: _dereq_('../heatmap/colorbar'),
82400 style: _dereq_('../heatmap/style'),
82401 hoverPoints: _dereq_('./hover'),
82402 eventData: _dereq_('../histogram/event_data'),
82403
82404 moduleType: 'trace',
82405 name: 'histogram2d',
82406 basePlotModule: _dereq_('../../plots/cartesian'),
82407 categories: ['cartesian', 'svg', '2dMap', 'histogram', 'showLegend'],
82408 meta: {
82409
82410
82411 }
82412};
82413
82414},{"../../plots/cartesian":235,"../heatmap/calc":331,"../heatmap/colorbar":333,"../heatmap/plot":341,"../heatmap/style":342,"../histogram/cross_trace_defaults":352,"../histogram/event_data":354,"./attributes":358,"./defaults":360,"./hover":361}],363:[function(_dereq_,module,exports){
82415/**
82416* Copyright 2012-2020, Plotly, Inc.
82417* All rights reserved.
82418*
82419* This source code is licensed under the MIT license found in the
82420* LICENSE file in the root directory of this source tree.
82421*/
82422
82423'use strict';
82424
82425var Registry = _dereq_('../../registry');
82426var Lib = _dereq_('../../lib');
82427
82428module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
82429 var x = coerce('x');
82430 var y = coerce('y');
82431 var xlen = Lib.minRowLength(x);
82432 var ylen = Lib.minRowLength(y);
82433
82434 // we could try to accept x0 and dx, etc...
82435 // but that's a pretty weird use case.
82436 // for now require both x and y explicitly specified.
82437 if(!xlen || !ylen) {
82438 traceOut.visible = false;
82439 return;
82440 }
82441
82442 traceOut._length = Math.min(xlen, ylen);
82443
82444 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
82445 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
82446
82447 // if marker.color is an array, we can use it in aggregation instead of z
82448 var hasAggregationData = coerce('z') || coerce('marker.color');
82449
82450 if(hasAggregationData) coerce('histfunc');
82451 coerce('histnorm');
82452
82453 // Note: bin defaults are now handled in Histogram2D.crossTraceDefaults
82454 // autobin(x|y) are only included here to appease Plotly.validate
82455 coerce('autobinx');
82456 coerce('autobiny');
82457};
82458
82459},{"../../lib":178,"../../registry":269}],364:[function(_dereq_,module,exports){
82460/**
82461* Copyright 2012-2020, Plotly, Inc.
82462* All rights reserved.
82463*
82464* This source code is licensed under the MIT license found in the
82465* LICENSE file in the root directory of this source tree.
82466*/
82467
82468'use strict';
82469
82470var histogram2dAttrs = _dereq_('../histogram2d/attributes');
82471var contourAttrs = _dereq_('../contour/attributes');
82472var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
82473
82474var extendFlat = _dereq_('../../lib/extend').extendFlat;
82475
82476module.exports = extendFlat({
82477 x: histogram2dAttrs.x,
82478 y: histogram2dAttrs.y,
82479 z: histogram2dAttrs.z,
82480 marker: histogram2dAttrs.marker,
82481
82482 histnorm: histogram2dAttrs.histnorm,
82483 histfunc: histogram2dAttrs.histfunc,
82484 nbinsx: histogram2dAttrs.nbinsx,
82485 xbins: histogram2dAttrs.xbins,
82486 nbinsy: histogram2dAttrs.nbinsy,
82487 ybins: histogram2dAttrs.ybins,
82488 autobinx: histogram2dAttrs.autobinx,
82489 autobiny: histogram2dAttrs.autobiny,
82490
82491 bingroup: histogram2dAttrs.bingroup,
82492 xbingroup: histogram2dAttrs.xbingroup,
82493 ybingroup: histogram2dAttrs.ybingroup,
82494
82495 autocontour: contourAttrs.autocontour,
82496 ncontours: contourAttrs.ncontours,
82497 contours: contourAttrs.contours,
82498 line: {
82499 color: contourAttrs.line.color,
82500 width: extendFlat({}, contourAttrs.line.width, {
82501 dflt: 0.5,
82502
82503 }),
82504 dash: contourAttrs.line.dash,
82505 smoothing: contourAttrs.line.smoothing,
82506 editType: 'plot'
82507 },
82508 zhoverformat: histogram2dAttrs.zhoverformat,
82509 hovertemplate: histogram2dAttrs.hovertemplate
82510},
82511 colorScaleAttrs('', {
82512 cLetter: 'z',
82513 editTypeOverride: 'calc'
82514 })
82515);
82516
82517},{"../../components/colorscale/attributes":59,"../../lib/extend":173,"../contour/attributes":308,"../histogram2d/attributes":358}],365:[function(_dereq_,module,exports){
82518/**
82519* Copyright 2012-2020, Plotly, Inc.
82520* All rights reserved.
82521*
82522* This source code is licensed under the MIT license found in the
82523* LICENSE file in the root directory of this source tree.
82524*/
82525
82526
82527'use strict';
82528
82529var Lib = _dereq_('../../lib');
82530
82531var handleSampleDefaults = _dereq_('../histogram2d/sample_defaults');
82532var handleContoursDefaults = _dereq_('../contour/contours_defaults');
82533var handleStyleDefaults = _dereq_('../contour/style_defaults');
82534var attributes = _dereq_('./attributes');
82535
82536
82537module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
82538 function coerce(attr, dflt) {
82539 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
82540 }
82541
82542 function coerce2(attr) {
82543 return Lib.coerce2(traceIn, traceOut, attributes, attr);
82544 }
82545
82546 handleSampleDefaults(traceIn, traceOut, coerce, layout);
82547 if(traceOut.visible === false) return;
82548
82549 handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
82550 handleStyleDefaults(traceIn, traceOut, coerce, layout);
82551 coerce('hovertemplate');
82552};
82553
82554},{"../../lib":178,"../contour/contours_defaults":315,"../contour/style_defaults":329,"../histogram2d/sample_defaults":363,"./attributes":364}],366:[function(_dereq_,module,exports){
82555/**
82556* Copyright 2012-2020, Plotly, Inc.
82557* All rights reserved.
82558*
82559* This source code is licensed under the MIT license found in the
82560* LICENSE file in the root directory of this source tree.
82561*/
82562
82563'use strict';
82564
82565module.exports = {
82566 attributes: _dereq_('./attributes'),
82567 supplyDefaults: _dereq_('./defaults'),
82568 crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'),
82569 calc: _dereq_('../contour/calc'),
82570 plot: _dereq_('../contour/plot').plot,
82571 layerName: 'contourlayer',
82572 style: _dereq_('../contour/style'),
82573 colorbar: _dereq_('../contour/colorbar'),
82574 hoverPoints: _dereq_('../contour/hover'),
82575
82576 moduleType: 'trace',
82577 name: 'histogram2dcontour',
82578 basePlotModule: _dereq_('../../plots/cartesian'),
82579 categories: ['cartesian', 'svg', '2dMap', 'contour', 'histogram', 'showLegend'],
82580 meta: {
82581
82582
82583 }
82584};
82585
82586},{"../../plots/cartesian":235,"../contour/calc":309,"../contour/colorbar":311,"../contour/hover":321,"../contour/plot":326,"../contour/style":328,"../histogram/cross_trace_defaults":352,"./attributes":364,"./defaults":365}],367:[function(_dereq_,module,exports){
82587/**
82588* Copyright 2012-2020, Plotly, Inc.
82589* All rights reserved.
82590*
82591* This source code is licensed under the MIT license found in the
82592* LICENSE file in the root directory of this source tree.
82593*/
82594
82595'use strict';
82596
82597var baseAttrs = _dereq_('../../plots/attributes');
82598var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
82599var extendFlat = _dereq_('../../lib/extend').extendFlat;
82600var colormodel = _dereq_('./constants').colormodel;
82601
82602var cm = ['rgb', 'rgba', 'hsl', 'hsla'];
82603var zminDesc = [];
82604var zmaxDesc = [];
82605for(var i = 0; i < cm.length; i++) {
82606 zminDesc.push('For the `' + cm[i] + '` colormodel, it is [' + colormodel[cm[i]].min.join(', ') + '].');
82607 zmaxDesc.push('For the `' + cm[i] + '` colormodel, it is [' + colormodel[cm[i]].max.join(', ') + '].');
82608}
82609
82610module.exports = extendFlat({
82611 z: {
82612 valType: 'data_array',
82613
82614 editType: 'calc',
82615
82616 },
82617 colormodel: {
82618 valType: 'enumerated',
82619 values: cm,
82620 dflt: 'rgb',
82621
82622 editType: 'calc',
82623
82624 },
82625 zmin: {
82626 valType: 'info_array',
82627 items: [
82628 {valType: 'number', editType: 'calc'},
82629 {valType: 'number', editType: 'calc'},
82630 {valType: 'number', editType: 'calc'},
82631 {valType: 'number', editType: 'calc'}
82632 ],
82633
82634 editType: 'calc',
82635
82636 },
82637 zmax: {
82638 valType: 'info_array',
82639 items: [
82640 {valType: 'number', editType: 'calc'},
82641 {valType: 'number', editType: 'calc'},
82642 {valType: 'number', editType: 'calc'},
82643 {valType: 'number', editType: 'calc'}
82644 ],
82645
82646 editType: 'calc',
82647
82648 },
82649 x0: {
82650 valType: 'any',
82651 dflt: 0,
82652
82653 editType: 'calc+clearAxisTypes',
82654
82655 },
82656 y0: {
82657 valType: 'any',
82658 dflt: 0,
82659
82660 editType: 'calc+clearAxisTypes',
82661
82662 },
82663 dx: {
82664 valType: 'number',
82665 dflt: 1,
82666
82667 editType: 'calc',
82668
82669 },
82670 dy: {
82671 valType: 'number',
82672 dflt: 1,
82673
82674 editType: 'calc',
82675
82676 },
82677 text: {
82678 valType: 'data_array',
82679 editType: 'plot',
82680
82681 },
82682 hovertext: {
82683 valType: 'data_array',
82684 editType: 'plot',
82685
82686 },
82687 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
82688 flags: ['x', 'y', 'z', 'color', 'name', 'text'],
82689 dflt: 'x+y+z+text+name'
82690 }),
82691 hovertemplate: hovertemplateAttrs({}, {
82692 keys: ['z', 'color', 'colormodel']
82693 }),
82694
82695 transforms: undefined
82696});
82697
82698},{"../../lib/extend":173,"../../plots/attributes":219,"../../plots/template_attributes":264,"./constants":369}],368:[function(_dereq_,module,exports){
82699/**
82700* Copyright 2012-2020, Plotly, Inc.
82701* All rights reserved.
82702*
82703* This source code is licensed under the MIT license found in the
82704* LICENSE file in the root directory of this source tree.
82705*/
82706
82707'use strict';
82708
82709var Lib = _dereq_('../../lib');
82710var constants = _dereq_('./constants');
82711var isNumeric = _dereq_('fast-isnumeric');
82712var Axes = _dereq_('../../plots/cartesian/axes');
82713var maxRowLength = _dereq_('../../lib').maxRowLength;
82714
82715module.exports = function calc(gd, trace) {
82716 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
82717 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
82718
82719 var x0 = xa.d2c(trace.x0) - trace.dx / 2;
82720 var y0 = ya.d2c(trace.y0) - trace.dy / 2;
82721 var h = trace.z.length;
82722 var w = maxRowLength(trace.z);
82723
82724 // Set axis range
82725 var i;
82726 var xrange = [x0, x0 + w * trace.dx];
82727 var yrange = [y0, y0 + h * trace.dy];
82728 if(xa && xa.type === 'log') for(i = 0; i < w; i++) xrange.push(x0 + i * trace.dx);
82729 if(ya && ya.type === 'log') for(i = 0; i < h; i++) yrange.push(y0 + i * trace.dy);
82730 trace._extremes[xa._id] = Axes.findExtremes(xa, xrange);
82731 trace._extremes[ya._id] = Axes.findExtremes(ya, yrange);
82732 trace._scaler = makeScaler(trace);
82733
82734 var cd0 = {
82735 x0: x0,
82736 y0: y0,
82737 z: trace.z,
82738 w: w,
82739 h: h
82740 };
82741 return [cd0];
82742};
82743
82744function scale(zero, ratio, min, max) {
82745 return function(c) {
82746 return Lib.constrain((c - zero) * ratio, min, max);
82747 };
82748}
82749
82750function constrain(min, max) {
82751 return function(c) { return Lib.constrain(c, min, max);};
82752}
82753
82754// Generate a function to scale color components according to zmin/zmax and the colormodel
82755function makeScaler(trace) {
82756 var colormodel = trace.colormodel;
82757 var n = colormodel.length;
82758 var cr = constants.colormodel[colormodel];
82759
82760 trace._sArray = [];
82761 // Loop over all color components
82762 for(var k = 0; k < n; k++) {
82763 if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) {
82764 trace._sArray.push(scale(
82765 trace.zmin[k],
82766 (cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]),
82767 cr.min[k],
82768 cr.max[k]
82769 ));
82770 } else {
82771 trace._sArray.push(constrain(cr.min[k], cr.max[k]));
82772 }
82773 }
82774
82775 return function(pixel) {
82776 var c = pixel.slice(0, n);
82777 for(var k = 0; k < n; k++) {
82778 var ck = c[k];
82779 if(!isNumeric(ck)) return false;
82780 c[k] = trace._sArray[k](ck);
82781 }
82782 return c;
82783 };
82784}
82785
82786},{"../../lib":178,"../../plots/cartesian/axes":222,"./constants":369,"fast-isnumeric":18}],369:[function(_dereq_,module,exports){
82787/**
82788* Copyright 2012-2020, Plotly, Inc.
82789* All rights reserved.
82790*
82791* This source code is licensed under the MIT license found in the
82792* LICENSE file in the root directory of this source tree.
82793*/
82794
82795'use strict';
82796
82797module.exports = {
82798 colormodel: {
82799 rgb: {
82800 min: [0, 0, 0],
82801 max: [255, 255, 255],
82802 fmt: function(c) {return c.slice(0, 3);},
82803 suffix: ['', '', '']
82804 },
82805 rgba: {
82806 min: [0, 0, 0, 0],
82807 max: [255, 255, 255, 1],
82808 fmt: function(c) {return c.slice(0, 4);},
82809 suffix: ['', '', '', '']
82810 },
82811 hsl: {
82812 min: [0, 0, 0],
82813 max: [360, 100, 100],
82814 fmt: function(c) {
82815 var p = c.slice(0, 3);
82816 p[1] = p[1] + '%';
82817 p[2] = p[2] + '%';
82818 return p;
82819 },
82820 suffix: ['°', '%', '%']
82821 },
82822 hsla: {
82823 min: [0, 0, 0, 0],
82824 max: [360, 100, 100, 1],
82825 fmt: function(c) {
82826 var p = c.slice(0, 4);
82827 p[1] = p[1] + '%';
82828 p[2] = p[2] + '%';
82829 return p;
82830 },
82831 suffix: ['°', '%', '%', '']
82832 }
82833 }
82834};
82835
82836},{}],370:[function(_dereq_,module,exports){
82837/**
82838* Copyright 2012-2020, Plotly, Inc.
82839* All rights reserved.
82840*
82841* This source code is licensed under the MIT license found in the
82842* LICENSE file in the root directory of this source tree.
82843*/
82844
82845'use strict';
82846
82847var Lib = _dereq_('../../lib');
82848var attributes = _dereq_('./attributes');
82849var constants = _dereq_('./constants');
82850
82851module.exports = function supplyDefaults(traceIn, traceOut) {
82852 function coerce(attr, dflt) {
82853 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
82854 }
82855 var z = coerce('z');
82856 if(z === undefined || !z.length || !z[0] || !z[0].length) {
82857 traceOut.visible = false;
82858 return;
82859 }
82860
82861 coerce('x0');
82862 coerce('y0');
82863 coerce('dx');
82864 coerce('dy');
82865 var colormodel = coerce('colormodel');
82866
82867 coerce('zmin', constants.colormodel[colormodel].min);
82868 coerce('zmax', constants.colormodel[colormodel].max);
82869
82870 coerce('text');
82871 coerce('hovertext');
82872 coerce('hovertemplate');
82873
82874 traceOut._length = null;
82875};
82876
82877},{"../../lib":178,"./attributes":367,"./constants":369}],371:[function(_dereq_,module,exports){
82878/**
82879* Copyright 2012-2020, Plotly, Inc.
82880* All rights reserved.
82881*
82882* This source code is licensed under the MIT license found in the
82883* LICENSE file in the root directory of this source tree.
82884*/
82885
82886'use strict';
82887
82888module.exports = function eventData(out, pt) {
82889 if('xVal' in pt) out.x = pt.xVal;
82890 if('yVal' in pt) out.y = pt.yVal;
82891 if(pt.xa) out.xaxis = pt.xa;
82892 if(pt.ya) out.yaxis = pt.ya;
82893 out.color = pt.color;
82894 out.colormodel = pt.trace.colormodel;
82895 return out;
82896};
82897
82898},{}],372:[function(_dereq_,module,exports){
82899/**
82900* Copyright 2012-2020, Plotly, Inc.
82901* All rights reserved.
82902*
82903* This source code is licensed under the MIT license found in the
82904* LICENSE file in the root directory of this source tree.
82905*/
82906
82907'use strict';
82908
82909var Fx = _dereq_('../../components/fx');
82910var Lib = _dereq_('../../lib');
82911var constants = _dereq_('./constants');
82912
82913module.exports = function hoverPoints(pointData, xval, yval) {
82914 var cd0 = pointData.cd[0];
82915 var trace = cd0.trace;
82916 var xa = pointData.xa;
82917 var ya = pointData.ya;
82918
82919 // Return early if not on image
82920 if(Fx.inbox(xval - cd0.x0, xval - (cd0.x0 + cd0.w * trace.dx), 0) > 0 ||
82921 Fx.inbox(yval - cd0.y0, yval - (cd0.y0 + cd0.h * trace.dy), 0) > 0) {
82922 return;
82923 }
82924
82925 // Find nearest pixel's index
82926 var nx = Math.floor((xval - cd0.x0) / trace.dx);
82927 var ny = Math.floor(Math.abs(yval - cd0.y0) / trace.dy);
82928
82929 // return early if pixel is undefined
82930 if(!cd0.z[ny][nx]) return;
82931
82932 var hoverinfo = cd0.hi || trace.hoverinfo;
82933 var fmtColor;
82934 if(hoverinfo) {
82935 var parts = hoverinfo.split('+');
82936 if(parts.indexOf('all') !== -1) parts = ['color'];
82937 if(parts.indexOf('color') !== -1) fmtColor = true;
82938 }
82939
82940 var colormodel = trace.colormodel;
82941 var dims = colormodel.length;
82942 var c = trace._scaler(cd0.z[ny][nx]);
82943 var s = constants.colormodel[colormodel].suffix;
82944
82945 var colorstring = [];
82946 if(trace.hovertemplate || fmtColor) {
82947 colorstring.push('[' + [c[0] + s[0], c[1] + s[1], c[2] + s[2]].join(', '));
82948 if(dims === 4) colorstring.push(', ' + c[3] + s[3]);
82949 colorstring.push(']');
82950 colorstring = colorstring.join('');
82951 pointData.extraText = colormodel.toUpperCase() + ': ' + colorstring;
82952 }
82953
82954 var text;
82955 if(Array.isArray(trace.hovertext) && Array.isArray(trace.hovertext[ny])) {
82956 text = trace.hovertext[ny][nx];
82957 } else if(Array.isArray(trace.text) && Array.isArray(trace.text[ny])) {
82958 text = trace.text[ny][nx];
82959 }
82960
82961 // TODO: for color model with 3 dims, display something useful for hovertemplate `%{color[3]}`
82962 var py = ya.c2p(cd0.y0 + (ny + 0.5) * trace.dy);
82963 var xVal = cd0.x0 + (nx + 0.5) * trace.dx;
82964 var yVal = cd0.y0 + (ny + 0.5) * trace.dy;
82965 var zLabel = '[' + cd0.z[ny][nx].slice(0, trace.colormodel.length).join(', ') + ']';
82966 return [Lib.extendFlat(pointData, {
82967 index: [ny, nx],
82968 x0: xa.c2p(cd0.x0 + nx * trace.dx),
82969 x1: xa.c2p(cd0.x0 + (nx + 1) * trace.dx),
82970 y0: py,
82971 y1: py,
82972 color: c,
82973 xVal: xVal,
82974 xLabelVal: xVal,
82975 yVal: yVal,
82976 yLabelVal: yVal,
82977 zLabelVal: zLabel,
82978 text: text,
82979 hovertemplateLabels: {
82980 'zLabel': zLabel,
82981 'colorLabel': colorstring,
82982 'color[0]Label': c[0] + s[0],
82983 'color[1]Label': c[1] + s[1],
82984 'color[2]Label': c[2] + s[2],
82985 'color[3]Label': c[3] + s[3]
82986 }
82987 })];
82988};
82989
82990},{"../../components/fx":92,"../../lib":178,"./constants":369}],373:[function(_dereq_,module,exports){
82991/**
82992* Copyright 2012-2020, Plotly, Inc.
82993* All rights reserved.
82994*
82995* This source code is licensed under the MIT license found in the
82996* LICENSE file in the root directory of this source tree.
82997*/
82998
82999'use strict';
83000
83001module.exports = {
83002 attributes: _dereq_('./attributes'),
83003 supplyDefaults: _dereq_('./defaults'),
83004 calc: _dereq_('./calc'),
83005 plot: _dereq_('./plot'),
83006 style: _dereq_('./style'),
83007 hoverPoints: _dereq_('./hover'),
83008 eventData: _dereq_('./event_data'),
83009
83010 moduleType: 'trace',
83011 name: 'image',
83012 basePlotModule: _dereq_('../../plots/cartesian'),
83013 categories: ['cartesian', 'svg', '2dMap', 'noSortingByValue'],
83014 animatable: false,
83015 meta: {
83016
83017 }
83018};
83019
83020},{"../../plots/cartesian":235,"./attributes":367,"./calc":368,"./defaults":370,"./event_data":371,"./hover":372,"./plot":374,"./style":375}],374:[function(_dereq_,module,exports){
83021/**
83022* Copyright 2012-2020, Plotly, Inc.
83023* All rights reserved.
83024*
83025* This source code is licensed under the MIT license found in the
83026* LICENSE file in the root directory of this source tree.
83027*/
83028
83029'use strict';
83030
83031var d3 = _dereq_('d3');
83032var Lib = _dereq_('../../lib');
83033var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
83034var constants = _dereq_('./constants');
83035
83036module.exports = function plot(gd, plotinfo, cdimage, imageLayer) {
83037 var xa = plotinfo.xaxis;
83038 var ya = plotinfo.yaxis;
83039
83040 Lib.makeTraceGroups(imageLayer, cdimage, 'im').each(function(cd) {
83041 var plotGroup = d3.select(this);
83042 var cd0 = cd[0];
83043 var trace = cd0.trace;
83044
83045 var z = cd0.z;
83046 var x0 = cd0.x0;
83047 var y0 = cd0.y0;
83048 var w = cd0.w;
83049 var h = cd0.h;
83050 var dx = trace.dx;
83051 var dy = trace.dy;
83052
83053 var left, right, temp, top, bottom, i;
83054 // in case of log of a negative
83055 i = 0;
83056 while(left === undefined && i < w) {
83057 left = xa.c2p(x0 + i * dx);
83058 i++;
83059 }
83060 i = w;
83061 while(right === undefined && i > 0) {
83062 right = xa.c2p(x0 + i * dx);
83063 i--;
83064 }
83065 i = 0;
83066 while(top === undefined && i < h) {
83067 top = ya.c2p(y0 + i * dy);
83068 i++;
83069 }
83070 i = h;
83071 while(bottom === undefined && i > 0) {
83072 bottom = ya.c2p(y0 + i * dy);
83073 i--;
83074 }
83075
83076 if(right < left) {
83077 temp = right;
83078 right = left;
83079 left = temp;
83080 }
83081
83082 if(bottom < top) {
83083 temp = top;
83084 top = bottom;
83085 bottom = temp;
83086 }
83087
83088 // Reduce image size when zoomed in to save memory
83089 var extra = 0.5; // half the axis size
83090 left = Math.max(-extra * xa._length, left);
83091 right = Math.min((1 + extra) * xa._length, right);
83092 top = Math.max(-extra * ya._length, top);
83093 bottom = Math.min((1 + extra) * ya._length, bottom);
83094 var imageWidth = Math.round(right - left);
83095 var imageHeight = Math.round(bottom - top);
83096
83097 // if image is entirely off-screen, don't even draw it
83098 var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
83099 if(isOffScreen) {
83100 var noImage = plotGroup.selectAll('image').data([]);
83101 noImage.exit().remove();
83102 return;
83103 }
83104
83105 // Draw each pixel
83106 var canvas = document.createElement('canvas');
83107 canvas.width = imageWidth;
83108 canvas.height = imageHeight;
83109 var context = canvas.getContext('2d');
83110
83111 var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);};
83112 var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);};
83113
83114 var fmt = constants.colormodel[trace.colormodel].fmt;
83115 var c;
83116 for(i = 0; i < cd0.w; i++) {
83117 var ipx0 = ipx(i); var ipx1 = ipx(i + 1);
83118 if(ipx1 === ipx0 || isNaN(ipx1) || isNaN(ipx0)) continue;
83119 for(var j = 0; j < cd0.h; j++) {
83120 var jpx0 = jpx(j); var jpx1 = jpx(j + 1);
83121 if(jpx1 === jpx0 || isNaN(jpx1) || isNaN(jpx0) || !z[j][i]) continue;
83122 c = trace._scaler(z[j][i]);
83123 if(c) {
83124 context.fillStyle = trace.colormodel + '(' + fmt(c).join(',') + ')';
83125 } else {
83126 // Return a transparent pixel
83127 context.fillStyle = 'rgba(0,0,0,0)';
83128 }
83129 context.fillRect(ipx0, jpx0, ipx1 - ipx0, jpx1 - jpx0);
83130 }
83131 }
83132
83133 var image3 = plotGroup.selectAll('image')
83134 .data(cd);
83135
83136 image3.enter().append('svg:image').attr({
83137 xmlns: xmlnsNamespaces.svg,
83138 preserveAspectRatio: 'none'
83139 });
83140
83141 image3.attr({
83142 height: imageHeight,
83143 width: imageWidth,
83144 x: left,
83145 y: top,
83146 'xlink:href': canvas.toDataURL('image/png')
83147 });
83148 });
83149};
83150
83151},{"../../constants/xmlns_namespaces":159,"../../lib":178,"./constants":369,"d3":16}],375:[function(_dereq_,module,exports){
83152/**
83153* Copyright 2012-2020, Plotly, Inc.
83154* All rights reserved.
83155*
83156* This source code is licensed under the MIT license found in the
83157* LICENSE file in the root directory of this source tree.
83158*/
83159
83160'use strict';
83161
83162var d3 = _dereq_('d3');
83163
83164module.exports = function style(gd) {
83165 d3.select(gd).selectAll('.im image')
83166 .style('opacity', function(d) {
83167 return d.trace.opacity;
83168 });
83169};
83170
83171},{"d3":16}],376:[function(_dereq_,module,exports){
83172/**
83173* Copyright 2012-2020, Plotly, Inc.
83174* All rights reserved.
83175*
83176* This source code is licensed under the MIT license found in the
83177* LICENSE file in the root directory of this source tree.
83178*/
83179
83180'use strict';
83181
83182var baseAttrs = _dereq_('../../plots/attributes');
83183var domainAttrs = _dereq_('../../plots/domain').attributes;
83184var fontAttrs = _dereq_('../../plots/font_attributes');
83185var colorAttrs = _dereq_('../../components/color/attributes');
83186var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
83187var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
83188
83189var extendFlat = _dereq_('../../lib/extend').extendFlat;
83190
83191var textFontAttrs = fontAttrs({
83192 editType: 'plot',
83193 arrayOk: true,
83194 colorEditType: 'plot',
83195
83196});
83197
83198module.exports = {
83199 labels: {
83200 valType: 'data_array',
83201 editType: 'calc',
83202
83203 },
83204 // equivalent of x0 and dx, if label is missing
83205 label0: {
83206 valType: 'number',
83207
83208 dflt: 0,
83209 editType: 'calc',
83210
83211 },
83212 dlabel: {
83213 valType: 'number',
83214
83215 dflt: 1,
83216 editType: 'calc',
83217
83218 },
83219
83220 values: {
83221 valType: 'data_array',
83222 editType: 'calc',
83223
83224 },
83225
83226 marker: {
83227 colors: {
83228 valType: 'data_array', // TODO 'color_array' ?
83229 editType: 'calc',
83230
83231 },
83232
83233 line: {
83234 color: {
83235 valType: 'color',
83236
83237 dflt: colorAttrs.defaultLine,
83238 arrayOk: true,
83239 editType: 'style',
83240
83241 },
83242 width: {
83243 valType: 'number',
83244
83245 min: 0,
83246 dflt: 0,
83247 arrayOk: true,
83248 editType: 'style',
83249
83250 },
83251 editType: 'calc'
83252 },
83253 editType: 'calc'
83254 },
83255
83256 text: {
83257 valType: 'data_array',
83258 editType: 'plot',
83259
83260 },
83261 hovertext: {
83262 valType: 'string',
83263
83264 dflt: '',
83265 arrayOk: true,
83266 editType: 'style',
83267
83268 },
83269
83270// 'see eg:'
83271// 'https://www.e-education.psu.edu/natureofgeoinfo/sites/www.e-education.psu.edu.natureofgeoinfo/files/image/hisp_pies.gif',
83272// '(this example involves a map too - may someday be a whole trace type',
83273// 'of its own. but the point is the size of the whole pie is important.)'
83274 scalegroup: {
83275 valType: 'string',
83276
83277 dflt: '',
83278 editType: 'calc',
83279
83280 },
83281
83282 // labels (legend is handled by plots.attributes.showlegend and layout.hiddenlabels)
83283 textinfo: {
83284 valType: 'flaglist',
83285
83286 flags: ['label', 'text', 'value', 'percent'],
83287 extras: ['none'],
83288 editType: 'calc',
83289
83290 },
83291 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
83292 flags: ['label', 'text', 'value', 'percent', 'name']
83293 }),
83294 hovertemplate: hovertemplateAttrs({}, {
83295 keys: ['label', 'color', 'value', 'percent', 'text']
83296 }),
83297 texttemplate: texttemplateAttrs({editType: 'plot'}, {
83298 keys: ['label', 'color', 'value', 'percent', 'text']
83299 }),
83300 textposition: {
83301 valType: 'enumerated',
83302
83303 values: ['inside', 'outside', 'auto', 'none'],
83304 dflt: 'auto',
83305 arrayOk: true,
83306 editType: 'plot',
83307
83308 },
83309 textfont: extendFlat({}, textFontAttrs, {
83310
83311 }),
83312 insidetextorientation: {
83313 valType: 'enumerated',
83314
83315 values: ['horizontal', 'radial', 'tangential', 'auto'],
83316 dflt: 'auto',
83317 editType: 'plot',
83318
83319 },
83320 insidetextfont: extendFlat({}, textFontAttrs, {
83321
83322 }),
83323 outsidetextfont: extendFlat({}, textFontAttrs, {
83324
83325 }),
83326 automargin: {
83327 valType: 'boolean',
83328 dflt: false,
83329
83330 editType: 'plot',
83331
83332 },
83333
83334 title: {
83335 text: {
83336 valType: 'string',
83337 dflt: '',
83338
83339 editType: 'plot',
83340
83341 },
83342 font: extendFlat({}, textFontAttrs, {
83343
83344 }),
83345 position: {
83346 valType: 'enumerated',
83347 values: [
83348 'top left', 'top center', 'top right',
83349 'middle center',
83350 'bottom left', 'bottom center', 'bottom right'
83351 ],
83352
83353 editType: 'plot',
83354
83355 },
83356
83357 editType: 'plot'
83358 },
83359
83360 // position and shape
83361 domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}),
83362
83363 hole: {
83364 valType: 'number',
83365
83366 min: 0,
83367 max: 1,
83368 dflt: 0,
83369 editType: 'calc',
83370
83371 },
83372
83373 // ordering and direction
83374 sort: {
83375 valType: 'boolean',
83376
83377 dflt: true,
83378 editType: 'calc',
83379
83380 },
83381 direction: {
83382 /**
83383 * there are two common conventions, both of which place the first
83384 * (largest, if sorted) slice with its left edge at 12 o'clock but
83385 * succeeding slices follow either cw or ccw from there.
83386 *
83387 * see http://visage.co/data-visualization-101-pie-charts/
83388 */
83389 valType: 'enumerated',
83390 values: ['clockwise', 'counterclockwise'],
83391
83392 dflt: 'counterclockwise',
83393 editType: 'calc',
83394
83395 },
83396 rotation: {
83397 valType: 'number',
83398
83399 min: -360,
83400 max: 360,
83401 dflt: 0,
83402 editType: 'calc',
83403
83404 },
83405
83406 pull: {
83407 valType: 'number',
83408
83409 min: 0,
83410 max: 1,
83411 dflt: 0,
83412 arrayOk: true,
83413 editType: 'calc',
83414
83415 },
83416
83417 _deprecated: {
83418 title: {
83419 valType: 'string',
83420 dflt: '',
83421
83422 editType: 'calc',
83423
83424 },
83425 titlefont: extendFlat({}, textFontAttrs, {
83426
83427 }),
83428 titleposition: {
83429 valType: 'enumerated',
83430 values: [
83431 'top left', 'top center', 'top right',
83432 'middle center',
83433 'bottom left', 'bottom center', 'bottom right'
83434 ],
83435
83436 editType: 'calc',
83437
83438 }
83439 }
83440};
83441
83442},{"../../components/color/attributes":51,"../../lib/extend":173,"../../plots/attributes":219,"../../plots/domain":249,"../../plots/font_attributes":250,"../../plots/template_attributes":264}],377:[function(_dereq_,module,exports){
83443/**
83444* Copyright 2012-2020, Plotly, Inc.
83445* All rights reserved.
83446*
83447* This source code is licensed under the MIT license found in the
83448* LICENSE file in the root directory of this source tree.
83449*/
83450
83451'use strict';
83452
83453var plots = _dereq_('../../plots/plots');
83454
83455exports.name = 'pie';
83456
83457exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
83458 plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
83459};
83460
83461exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
83462 plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
83463};
83464
83465},{"../../plots/plots":256}],378:[function(_dereq_,module,exports){
83466/**
83467* Copyright 2012-2020, Plotly, Inc.
83468* All rights reserved.
83469*
83470* This source code is licensed under the MIT license found in the
83471* LICENSE file in the root directory of this source tree.
83472*/
83473
83474'use strict';
83475
83476var isNumeric = _dereq_('fast-isnumeric');
83477var tinycolor = _dereq_('tinycolor2');
83478
83479var Color = _dereq_('../../components/color');
83480
83481var extendedColorWayList = {};
83482
83483function calc(gd, trace) {
83484 var cd = [];
83485
83486 var fullLayout = gd._fullLayout;
83487 var hiddenLabels = fullLayout.hiddenlabels || [];
83488
83489 var labels = trace.labels;
83490 var colors = trace.marker.colors || [];
83491 var vals = trace.values;
83492 var len = trace._length;
83493 var hasValues = trace._hasValues && len;
83494
83495 var i, pt;
83496
83497 if(trace.dlabel) {
83498 labels = new Array(len);
83499 for(i = 0; i < len; i++) {
83500 labels[i] = String(trace.label0 + i * trace.dlabel);
83501 }
83502 }
83503
83504 var allThisTraceLabels = {};
83505 var pullColor = makePullColorFn(fullLayout['_' + trace.type + 'colormap']);
83506 var vTotal = 0;
83507 var isAggregated = false;
83508
83509 for(i = 0; i < len; i++) {
83510 var v, label, hidden;
83511 if(hasValues) {
83512 v = vals[i];
83513 if(!isNumeric(v)) continue;
83514 v = +v;
83515 if(v < 0) continue;
83516 } else v = 1;
83517
83518 label = labels[i];
83519 if(label === undefined || label === '') label = i;
83520 label = String(label);
83521
83522 var thisLabelIndex = allThisTraceLabels[label];
83523 if(thisLabelIndex === undefined) {
83524 allThisTraceLabels[label] = cd.length;
83525
83526 hidden = hiddenLabels.indexOf(label) !== -1;
83527
83528 if(!hidden) vTotal += v;
83529
83530 cd.push({
83531 v: v,
83532 label: label,
83533 color: pullColor(colors[i], label),
83534 i: i,
83535 pts: [i],
83536 hidden: hidden
83537 });
83538 } else {
83539 isAggregated = true;
83540
83541 pt = cd[thisLabelIndex];
83542 pt.v += v;
83543 pt.pts.push(i);
83544 if(!pt.hidden) vTotal += v;
83545
83546 if(pt.color === false && colors[i]) {
83547 pt.color = pullColor(colors[i], label);
83548 }
83549 }
83550 }
83551
83552 var shouldSort = (trace.type === 'funnelarea') ? isAggregated : trace.sort;
83553 if(shouldSort) cd.sort(function(a, b) { return b.v - a.v; });
83554
83555 // include the sum of all values in the first point
83556 if(cd[0]) cd[0].vTotal = vTotal;
83557
83558 return cd;
83559}
83560
83561function makePullColorFn(colorMap) {
83562 return function pullColor(color, id) {
83563 if(!color) return false;
83564
83565 color = tinycolor(color);
83566 if(!color.isValid()) return false;
83567
83568 color = Color.addOpacity(color, color.getAlpha());
83569 if(!colorMap[id]) colorMap[id] = color;
83570
83571 return color;
83572 };
83573}
83574
83575/*
83576 * `calc` filled in (and collated) explicit colors.
83577 * Now we need to propagate these explicit colors to other traces,
83578 * and fill in default colors.
83579 * This is done after sorting, so we pick defaults
83580 * in the order slices will be displayed
83581 */
83582function crossTraceCalc(gd, plotinfo) { // TODO: should we name the second argument opts?
83583 var desiredType = (plotinfo || {}).type;
83584 if(!desiredType) desiredType = 'pie';
83585
83586 var fullLayout = gd._fullLayout;
83587 var calcdata = gd.calcdata;
83588 var colorWay = fullLayout[desiredType + 'colorway'];
83589 var colorMap = fullLayout['_' + desiredType + 'colormap'];
83590
83591 if(fullLayout['extend' + desiredType + 'colors']) {
83592 colorWay = generateExtendedColors(colorWay, extendedColorWayList);
83593 }
83594 var dfltColorCount = 0;
83595
83596 for(var i = 0; i < calcdata.length; i++) {
83597 var cd = calcdata[i];
83598 var traceType = cd[0].trace.type;
83599 if(traceType !== desiredType) continue;
83600
83601 for(var j = 0; j < cd.length; j++) {
83602 var pt = cd[j];
83603 if(pt.color === false) {
83604 // have we seen this label and assigned a color to it in a previous trace?
83605 if(colorMap[pt.label]) {
83606 pt.color = colorMap[pt.label];
83607 } else {
83608 colorMap[pt.label] = pt.color = colorWay[dfltColorCount % colorWay.length];
83609 dfltColorCount++;
83610 }
83611 }
83612 }
83613 }
83614}
83615
83616/**
83617 * pick a default color from the main default set, augmented by
83618 * itself lighter then darker before repeating
83619 */
83620function generateExtendedColors(colorList, extendedColorWays) {
83621 var i;
83622 var colorString = JSON.stringify(colorList);
83623 var colors = extendedColorWays[colorString];
83624 if(!colors) {
83625 colors = colorList.slice();
83626
83627 for(i = 0; i < colorList.length; i++) {
83628 colors.push(tinycolor(colorList[i]).lighten(20).toHexString());
83629 }
83630
83631 for(i = 0; i < colorList.length; i++) {
83632 colors.push(tinycolor(colorList[i]).darken(20).toHexString());
83633 }
83634 extendedColorWays[colorString] = colors;
83635 }
83636
83637 return colors;
83638}
83639
83640module.exports = {
83641 calc: calc,
83642 crossTraceCalc: crossTraceCalc,
83643
83644 makePullColorFn: makePullColorFn,
83645 generateExtendedColors: generateExtendedColors
83646};
83647
83648},{"../../components/color":52,"fast-isnumeric":18,"tinycolor2":35}],379:[function(_dereq_,module,exports){
83649/**
83650* Copyright 2012-2020, Plotly, Inc.
83651* All rights reserved.
83652*
83653* This source code is licensed under the MIT license found in the
83654* LICENSE file in the root directory of this source tree.
83655*/
83656
83657'use strict';
83658
83659var isNumeric = _dereq_('fast-isnumeric');
83660var Lib = _dereq_('../../lib');
83661var attributes = _dereq_('./attributes');
83662var handleDomainDefaults = _dereq_('../../plots/domain').defaults;
83663var handleText = _dereq_('../bar/defaults').handleText;
83664
83665function handleLabelsAndValues(labels, values) {
83666 var hasLabels = Array.isArray(labels);
83667 var hasValues = Lib.isArrayOrTypedArray(values);
83668 var len = Math.min(
83669 hasLabels ? labels.length : Infinity,
83670 hasValues ? values.length : Infinity
83671 );
83672
83673 if(!isFinite(len)) len = 0;
83674
83675 if(len && hasValues) {
83676 var hasPositive;
83677 for(var i = 0; i < len; i++) {
83678 var v = values[i];
83679 if(isNumeric(v) && v > 0) {
83680 hasPositive = true;
83681 break;
83682 }
83683 }
83684 if(!hasPositive) len = 0;
83685 }
83686
83687 return {
83688 hasLabels: hasLabels,
83689 hasValues: hasValues,
83690 len: len
83691 };
83692}
83693
83694function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
83695 function coerce(attr, dflt) {
83696 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
83697 }
83698
83699 var labels = coerce('labels');
83700 var values = coerce('values');
83701
83702 var res = handleLabelsAndValues(labels, values);
83703 var len = res.len;
83704 traceOut._hasLabels = res.hasLabels;
83705 traceOut._hasValues = res.hasValues;
83706
83707 if(!traceOut._hasLabels &&
83708 traceOut._hasValues
83709 ) {
83710 coerce('label0');
83711 coerce('dlabel');
83712 }
83713
83714 if(!len) {
83715 traceOut.visible = false;
83716 return;
83717 }
83718 traceOut._length = len;
83719
83720 var lineWidth = coerce('marker.line.width');
83721 if(lineWidth) coerce('marker.line.color');
83722
83723 coerce('marker.colors');
83724
83725 coerce('scalegroup');
83726 // TODO: hole needs to be coerced to the same value within a scaleegroup
83727
83728 var textData = coerce('text');
83729 var textTemplate = coerce('texttemplate');
83730 var textInfo;
83731 if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');
83732
83733 coerce('hovertext');
83734 coerce('hovertemplate');
83735
83736 if(textTemplate || (textInfo && textInfo !== 'none')) {
83737 var textposition = coerce('textposition');
83738 handleText(traceIn, traceOut, layout, coerce, textposition, {
83739 moduleHasSelected: false,
83740 moduleHasUnselected: false,
83741 moduleHasConstrain: false,
83742 moduleHasCliponaxis: false,
83743 moduleHasTextangle: false,
83744 moduleHasInsideanchor: false
83745 });
83746
83747 var hasBoth = Array.isArray(textposition) || textposition === 'auto';
83748 var hasOutside = hasBoth || textposition === 'outside';
83749 if(hasOutside) {
83750 coerce('automargin');
83751 }
83752
83753 if(textposition === 'inside' || textposition === 'auto' || Array.isArray(textposition)) {
83754 coerce('insidetextorientation');
83755 }
83756 }
83757
83758 handleDomainDefaults(traceOut, layout, coerce);
83759
83760 var hole = coerce('hole');
83761 var title = coerce('title.text');
83762 if(title) {
83763 var titlePosition = coerce('title.position', hole ? 'middle center' : 'top center');
83764 if(!hole && titlePosition === 'middle center') traceOut.title.position = 'top center';
83765 Lib.coerceFont(coerce, 'title.font', layout.font);
83766 }
83767
83768 coerce('sort');
83769 coerce('direction');
83770 coerce('rotation');
83771 coerce('pull');
83772}
83773
83774module.exports = {
83775 handleLabelsAndValues: handleLabelsAndValues,
83776 supplyDefaults: supplyDefaults
83777};
83778
83779},{"../../lib":178,"../../plots/domain":249,"../bar/defaults":283,"./attributes":376,"fast-isnumeric":18}],380:[function(_dereq_,module,exports){
83780/**
83781* Copyright 2012-2020, Plotly, Inc.
83782* All rights reserved.
83783*
83784* This source code is licensed under the MIT license found in the
83785* LICENSE file in the root directory of this source tree.
83786*/
83787
83788'use strict';
83789
83790var appendArrayMultiPointValues = _dereq_('../../components/fx/helpers').appendArrayMultiPointValues;
83791
83792// Note: like other eventData routines, this creates the data for hover/unhover/click events
83793// but it has a different API and goes through a totally different pathway.
83794// So to ensure it doesn't get misused, it's not attached to the Pie module.
83795module.exports = function eventData(pt, trace) {
83796 var out = {
83797 curveNumber: trace.index,
83798 pointNumbers: pt.pts,
83799 data: trace._input,
83800 fullData: trace,
83801 label: pt.label,
83802 color: pt.color,
83803 value: pt.v,
83804 percent: pt.percent,
83805 text: pt.text,
83806
83807 // pt.v (and pt.i below) for backward compatibility
83808 v: pt.v
83809 };
83810
83811 // Only include pointNumber if it's unambiguous
83812 if(pt.pts.length === 1) out.pointNumber = out.i = pt.pts[0];
83813
83814 // Add extra data arrays to the output
83815 // notice that this is the multi-point version ('s' on the end!)
83816 // so added data will be arrays matching the pointNumbers array.
83817 appendArrayMultiPointValues(out, trace, pt.pts);
83818
83819 // don't include obsolete fields in new funnelarea traces
83820 if(trace.type === 'funnelarea') {
83821 delete out.v;
83822 delete out.i;
83823 }
83824
83825 return out;
83826};
83827
83828},{"../../components/fx/helpers":88}],381:[function(_dereq_,module,exports){
83829/**
83830* Copyright 2012-2020, Plotly, Inc.
83831* All rights reserved.
83832*
83833* This source code is licensed under the MIT license found in the
83834* LICENSE file in the root directory of this source tree.
83835*/
83836
83837'use strict';
83838
83839var Lib = _dereq_('../../lib');
83840
83841exports.formatPiePercent = function formatPiePercent(v, separators) {
83842 var vRounded = (v * 100).toPrecision(3);
83843 if(vRounded.lastIndexOf('.') !== -1) {
83844 vRounded = vRounded.replace(/[.]?0+$/, '');
83845 }
83846 return Lib.numSeparate(vRounded, separators) + '%';
83847};
83848
83849exports.formatPieValue = function formatPieValue(v, separators) {
83850 var vRounded = v.toPrecision(10);
83851 if(vRounded.lastIndexOf('.') !== -1) {
83852 vRounded = vRounded.replace(/[.]?0+$/, '');
83853 }
83854 return Lib.numSeparate(vRounded, separators);
83855};
83856
83857exports.getFirstFilled = function getFirstFilled(array, indices) {
83858 if(!Array.isArray(array)) return;
83859 for(var i = 0; i < indices.length; i++) {
83860 var v = array[indices[i]];
83861 if(v || v === 0 || v === '') return v;
83862 }
83863};
83864
83865exports.castOption = function castOption(item, indices) {
83866 if(Array.isArray(item)) return exports.getFirstFilled(item, indices);
83867 else if(item) return item;
83868};
83869
83870},{"../../lib":178}],382:[function(_dereq_,module,exports){
83871/**
83872* Copyright 2012-2020, Plotly, Inc.
83873* All rights reserved.
83874*
83875* This source code is licensed under the MIT license found in the
83876* LICENSE file in the root directory of this source tree.
83877*/
83878
83879'use strict';
83880
83881module.exports = {
83882 attributes: _dereq_('./attributes'),
83883 supplyDefaults: _dereq_('./defaults').supplyDefaults,
83884 supplyLayoutDefaults: _dereq_('./layout_defaults'),
83885 layoutAttributes: _dereq_('./layout_attributes'),
83886
83887 calc: _dereq_('./calc').calc,
83888 crossTraceCalc: _dereq_('./calc').crossTraceCalc,
83889
83890 plot: _dereq_('./plot').plot,
83891 style: _dereq_('./style'),
83892 styleOne: _dereq_('./style_one'),
83893
83894 moduleType: 'trace',
83895 name: 'pie',
83896 basePlotModule: _dereq_('./base_plot'),
83897 categories: ['pie-like', 'pie', 'showLegend'],
83898 meta: {
83899
83900 }
83901};
83902
83903},{"./attributes":376,"./base_plot":377,"./calc":378,"./defaults":379,"./layout_attributes":383,"./layout_defaults":384,"./plot":385,"./style":386,"./style_one":387}],383:[function(_dereq_,module,exports){
83904/**
83905* Copyright 2012-2020, Plotly, Inc.
83906* All rights reserved.
83907*
83908* This source code is licensed under the MIT license found in the
83909* LICENSE file in the root directory of this source tree.
83910*/
83911
83912'use strict';
83913
83914module.exports = {
83915 hiddenlabels: {
83916 valType: 'data_array',
83917
83918 editType: 'calc',
83919
83920 },
83921 piecolorway: {
83922 valType: 'colorlist',
83923
83924 editType: 'calc',
83925
83926 },
83927 extendpiecolors: {
83928 valType: 'boolean',
83929 dflt: true,
83930
83931 editType: 'calc',
83932
83933 }
83934};
83935
83936},{}],384:[function(_dereq_,module,exports){
83937/**
83938* Copyright 2012-2020, Plotly, Inc.
83939* All rights reserved.
83940*
83941* This source code is licensed under the MIT license found in the
83942* LICENSE file in the root directory of this source tree.
83943*/
83944
83945'use strict';
83946
83947var Lib = _dereq_('../../lib');
83948
83949var layoutAttributes = _dereq_('./layout_attributes');
83950
83951module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
83952 function coerce(attr, dflt) {
83953 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
83954 }
83955
83956 coerce('hiddenlabels');
83957 coerce('piecolorway', layoutOut.colorway);
83958 coerce('extendpiecolors');
83959};
83960
83961},{"../../lib":178,"./layout_attributes":383}],385:[function(_dereq_,module,exports){
83962/**
83963* Copyright 2012-2020, Plotly, Inc.
83964* All rights reserved.
83965*
83966* This source code is licensed under the MIT license found in the
83967* LICENSE file in the root directory of this source tree.
83968*/
83969
83970'use strict';
83971
83972var d3 = _dereq_('d3');
83973
83974var Plots = _dereq_('../../plots/plots');
83975var Fx = _dereq_('../../components/fx');
83976var Color = _dereq_('../../components/color');
83977var Drawing = _dereq_('../../components/drawing');
83978var Lib = _dereq_('../../lib');
83979var svgTextUtils = _dereq_('../../lib/svg_text_utils');
83980var uniformText = _dereq_('../bar/uniform_text');
83981var recordMinTextSize = uniformText.recordMinTextSize;
83982var clearMinTextSize = uniformText.clearMinTextSize;
83983var TEXTPAD = _dereq_('../bar/constants').TEXTPAD;
83984
83985var helpers = _dereq_('./helpers');
83986var eventData = _dereq_('./event_data');
83987var isValidTextValue = _dereq_('../../lib').isValidTextValue;
83988
83989function plot(gd, cdModule) {
83990 var fullLayout = gd._fullLayout;
83991 var gs = fullLayout._size;
83992
83993 clearMinTextSize('pie', fullLayout);
83994
83995 prerenderTitles(cdModule, gd);
83996 layoutAreas(cdModule, gs);
83997
83998 var plotGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdModule, 'trace').each(function(cd) {
83999 var plotGroup = d3.select(this);
84000 var cd0 = cd[0];
84001 var trace = cd0.trace;
84002
84003 setCoords(cd);
84004
84005 // TODO: miter might look better but can sometimes cause problems
84006 // maybe miter with a small-ish stroke-miterlimit?
84007 plotGroup.attr('stroke-linejoin', 'round');
84008
84009 plotGroup.each(function() {
84010 var slices = d3.select(this).selectAll('g.slice').data(cd);
84011
84012 slices.enter().append('g')
84013 .classed('slice', true);
84014 slices.exit().remove();
84015
84016 var quadrants = [
84017 [[], []], // y<0: x<0, x>=0
84018 [[], []] // y>=0: x<0, x>=0
84019 ];
84020 var hasOutsideText = false;
84021
84022 slices.each(function(pt, i) {
84023 if(pt.hidden) {
84024 d3.select(this).selectAll('path,g').remove();
84025 return;
84026 }
84027
84028 // to have consistent event data compared to other traces
84029 pt.pointNumber = pt.i;
84030 pt.curveNumber = trace.index;
84031
84032 quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt);
84033
84034 var cx = cd0.cx;
84035 var cy = cd0.cy;
84036 var sliceTop = d3.select(this);
84037 var slicePath = sliceTop.selectAll('path.surface').data([pt]);
84038
84039 slicePath.enter().append('path')
84040 .classed('surface', true)
84041 .style({'pointer-events': 'all'});
84042
84043 sliceTop.call(attachFxHandlers, gd, cd);
84044
84045 if(trace.pull) {
84046 var pull = +helpers.castOption(trace.pull, pt.pts) || 0;
84047 if(pull > 0) {
84048 cx += pull * pt.pxmid[0];
84049 cy += pull * pt.pxmid[1];
84050 }
84051 }
84052
84053 pt.cxFinal = cx;
84054 pt.cyFinal = cy;
84055
84056 function arc(start, finish, cw, scale) {
84057 var dx = scale * (finish[0] - start[0]);
84058 var dy = scale * (finish[1] - start[1]);
84059
84060 return 'a' +
84061 (scale * cd0.r) + ',' + (scale * cd0.r) + ' 0 ' +
84062 pt.largeArc + (cw ? ' 1 ' : ' 0 ') + dx + ',' + dy;
84063 }
84064
84065 var hole = trace.hole;
84066 if(pt.v === cd0.vTotal) { // 100% fails bcs arc start and end are identical
84067 var outerCircle = 'M' + (cx + pt.px0[0]) + ',' + (cy + pt.px0[1]) +
84068 arc(pt.px0, pt.pxmid, true, 1) +
84069 arc(pt.pxmid, pt.px0, true, 1) + 'Z';
84070 if(hole) {
84071 slicePath.attr('d',
84072 'M' + (cx + hole * pt.px0[0]) + ',' + (cy + hole * pt.px0[1]) +
84073 arc(pt.px0, pt.pxmid, false, hole) +
84074 arc(pt.pxmid, pt.px0, false, hole) +
84075 'Z' + outerCircle);
84076 } else slicePath.attr('d', outerCircle);
84077 } else {
84078 var outerArc = arc(pt.px0, pt.px1, true, 1);
84079
84080 if(hole) {
84081 var rim = 1 - hole;
84082 slicePath.attr('d',
84083 'M' + (cx + hole * pt.px1[0]) + ',' + (cy + hole * pt.px1[1]) +
84084 arc(pt.px1, pt.px0, false, hole) +
84085 'l' + (rim * pt.px0[0]) + ',' + (rim * pt.px0[1]) +
84086 outerArc +
84087 'Z');
84088 } else {
84089 slicePath.attr('d',
84090 'M' + cx + ',' + cy +
84091 'l' + pt.px0[0] + ',' + pt.px0[1] +
84092 outerArc +
84093 'Z');
84094 }
84095 }
84096
84097 // add text
84098 formatSliceLabel(gd, pt, cd0);
84099 var textPosition = helpers.castOption(trace.textposition, pt.pts);
84100 var sliceTextGroup = sliceTop.selectAll('g.slicetext')
84101 .data(pt.text && (textPosition !== 'none') ? [0] : []);
84102
84103 sliceTextGroup.enter().append('g')
84104 .classed('slicetext', true);
84105 sliceTextGroup.exit().remove();
84106
84107 sliceTextGroup.each(function() {
84108 var sliceText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
84109 // prohibit tex interpretation until we can handle
84110 // tex and regular text together
84111 s.attr('data-notex', 1);
84112 });
84113
84114 var font = Lib.ensureUniformFontSize(gd, textPosition === 'outside' ?
84115 determineOutsideTextFont(trace, pt, fullLayout.font) :
84116 determineInsideTextFont(trace, pt, fullLayout.font)
84117 );
84118
84119 sliceText.text(pt.text)
84120 .attr({
84121 'class': 'slicetext',
84122 transform: '',
84123 'text-anchor': 'middle'
84124 })
84125 .call(Drawing.font, font)
84126 .call(svgTextUtils.convertToTspans, gd);
84127
84128 // position the text relative to the slice
84129 var textBB = Drawing.bBox(sliceText.node());
84130 var transform;
84131
84132 if(textPosition === 'outside') {
84133 transform = transformOutsideText(textBB, pt);
84134 } else {
84135 transform = transformInsideText(textBB, pt, cd0);
84136 if(textPosition === 'auto' && transform.scale < 1) {
84137 var newFont = Lib.ensureUniformFontSize(gd, trace.outsidetextfont);
84138
84139 sliceText.call(Drawing.font, newFont);
84140 textBB = Drawing.bBox(sliceText.node());
84141
84142 transform = transformOutsideText(textBB, pt);
84143 }
84144 }
84145
84146 var textPosAngle = transform.textPosAngle;
84147 var textXY = textPosAngle === undefined ? pt.pxmid : getCoords(cd0.r, textPosAngle);
84148 transform.targetX = cx + textXY[0] * transform.rCenter + (transform.x || 0);
84149 transform.targetY = cy + textXY[1] * transform.rCenter + (transform.y || 0);
84150 computeTransform(transform, textBB);
84151
84152 // save some stuff to use later ensure no labels overlap
84153 if(transform.outside) {
84154 var targetY = transform.targetY;
84155 pt.yLabelMin = targetY - textBB.height / 2;
84156 pt.yLabelMid = targetY;
84157 pt.yLabelMax = targetY + textBB.height / 2;
84158 pt.labelExtraX = 0;
84159 pt.labelExtraY = 0;
84160 hasOutsideText = true;
84161 }
84162
84163 transform.fontSize = font.size;
84164 recordMinTextSize(trace.type, transform, fullLayout);
84165 cd[i].transform = transform;
84166
84167 sliceText.attr('transform', Lib.getTextTransform(transform));
84168 });
84169 });
84170
84171 // add the title
84172 var titleTextGroup = d3.select(this).selectAll('g.titletext')
84173 .data(trace.title.text ? [0] : []);
84174
84175 titleTextGroup.enter().append('g')
84176 .classed('titletext', true);
84177 titleTextGroup.exit().remove();
84178
84179 titleTextGroup.each(function() {
84180 var titleText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
84181 // prohibit tex interpretation as above
84182 s.attr('data-notex', 1);
84183 });
84184
84185 var txt = trace.title.text;
84186 if(trace._meta) {
84187 txt = Lib.templateString(txt, trace._meta);
84188 }
84189
84190 titleText.text(txt)
84191 .attr({
84192 'class': 'titletext',
84193 transform: '',
84194 'text-anchor': 'middle',
84195 })
84196 .call(Drawing.font, trace.title.font)
84197 .call(svgTextUtils.convertToTspans, gd);
84198
84199 var transform;
84200
84201 if(trace.title.position === 'middle center') {
84202 transform = positionTitleInside(cd0);
84203 } else {
84204 transform = positionTitleOutside(cd0, gs);
84205 }
84206
84207 titleText.attr('transform',
84208 'translate(' + transform.x + ',' + transform.y + ')' +
84209 (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') +
84210 'translate(' + transform.tx + ',' + transform.ty + ')');
84211 });
84212
84213 // now make sure no labels overlap (at least within one pie)
84214 if(hasOutsideText) scootLabels(quadrants, trace);
84215
84216 plotTextLines(slices, trace);
84217
84218 if(hasOutsideText && trace.automargin) {
84219 // TODO if we ever want to improve perf,
84220 // we could reuse the textBB computed above together
84221 // with the sliceText transform info
84222 var traceBbox = Drawing.bBox(plotGroup.node());
84223
84224 var domain = trace.domain;
84225 var vpw = gs.w * (domain.x[1] - domain.x[0]);
84226 var vph = gs.h * (domain.y[1] - domain.y[0]);
84227 var xgap = (0.5 * vpw - cd0.r) / gs.w;
84228 var ygap = (0.5 * vph - cd0.r) / gs.h;
84229
84230 Plots.autoMargin(gd, 'pie.' + trace.uid + '.automargin', {
84231 xl: domain.x[0] - xgap,
84232 xr: domain.x[1] + xgap,
84233 yb: domain.y[0] - ygap,
84234 yt: domain.y[1] + ygap,
84235 l: Math.max(cd0.cx - cd0.r - traceBbox.left, 0),
84236 r: Math.max(traceBbox.right - (cd0.cx + cd0.r), 0),
84237 b: Math.max(traceBbox.bottom - (cd0.cy + cd0.r), 0),
84238 t: Math.max(cd0.cy - cd0.r - traceBbox.top, 0),
84239 pad: 5
84240 });
84241 }
84242 });
84243 });
84244
84245 // This is for a bug in Chrome (as of 2015-07-22, and does not affect FF)
84246 // if insidetextfont and outsidetextfont are different sizes, sometimes the size
84247 // of an "em" gets taken from the wrong element at first so lines are
84248 // spaced wrong. You just have to tell it to try again later and it gets fixed.
84249 // I have no idea why we haven't seen this in other contexts. Also, sometimes
84250 // it gets the initial draw correct but on redraw it gets confused.
84251 setTimeout(function() {
84252 plotGroups.selectAll('tspan').each(function() {
84253 var s = d3.select(this);
84254 if(s.attr('dy')) s.attr('dy', s.attr('dy'));
84255 });
84256 }, 0);
84257}
84258
84259// TODO add support for transition
84260function plotTextLines(slices, trace) {
84261 slices.each(function(pt) {
84262 var sliceTop = d3.select(this);
84263
84264 if(!pt.labelExtraX && !pt.labelExtraY) {
84265 sliceTop.select('path.textline').remove();
84266 return;
84267 }
84268
84269 // first move the text to its new location
84270 var sliceText = sliceTop.select('g.slicetext text');
84271
84272 pt.transform.targetX += pt.labelExtraX;
84273 pt.transform.targetY += pt.labelExtraY;
84274
84275 sliceText.attr('transform', Lib.getTextTransform(pt.transform));
84276
84277 // then add a line to the new location
84278 var lineStartX = pt.cxFinal + pt.pxmid[0];
84279 var lineStartY = pt.cyFinal + pt.pxmid[1];
84280 var textLinePath = 'M' + lineStartX + ',' + lineStartY;
84281 var finalX = (pt.yLabelMax - pt.yLabelMin) * (pt.pxmid[0] < 0 ? -1 : 1) / 4;
84282
84283 if(pt.labelExtraX) {
84284 var yFromX = pt.labelExtraX * pt.pxmid[1] / pt.pxmid[0];
84285 var yNet = pt.yLabelMid + pt.labelExtraY - (pt.cyFinal + pt.pxmid[1]);
84286
84287 if(Math.abs(yFromX) > Math.abs(yNet)) {
84288 textLinePath +=
84289 'l' + (yNet * pt.pxmid[0] / pt.pxmid[1]) + ',' + yNet +
84290 'H' + (lineStartX + pt.labelExtraX + finalX);
84291 } else {
84292 textLinePath += 'l' + pt.labelExtraX + ',' + yFromX +
84293 'v' + (yNet - yFromX) +
84294 'h' + finalX;
84295 }
84296 } else {
84297 textLinePath +=
84298 'V' + (pt.yLabelMid + pt.labelExtraY) +
84299 'h' + finalX;
84300 }
84301
84302 Lib.ensureSingle(sliceTop, 'path', 'textline')
84303 .call(Color.stroke, trace.outsidetextfont.color)
84304 .attr({
84305 'stroke-width': Math.min(2, trace.outsidetextfont.size / 8),
84306 d: textLinePath,
84307 fill: 'none'
84308 });
84309 });
84310}
84311
84312function attachFxHandlers(sliceTop, gd, cd) {
84313 var cd0 = cd[0];
84314 var trace = cd0.trace;
84315 var cx = cd0.cx;
84316 var cy = cd0.cy;
84317
84318 // hover state vars
84319 // have we drawn a hover label, so it should be cleared later
84320 if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false;
84321 // have we emitted a hover event, so later an unhover event should be emitted
84322 // note that click events do not depend on this - you can still get them
84323 // with hovermode: false or if you were earlier dragging, then clicked
84324 // in the same slice that you moused up in
84325 if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false;
84326
84327 sliceTop.on('mouseover', function(pt) {
84328 // in case fullLayout or fullData has changed without a replot
84329 var fullLayout2 = gd._fullLayout;
84330 var trace2 = gd._fullData[trace.index];
84331
84332 if(gd._dragging || fullLayout2.hovermode === false) return;
84333
84334 var hoverinfo = trace2.hoverinfo;
84335 if(Array.isArray(hoverinfo)) {
84336 // super hacky: we need to pull out the *first* hoverinfo from
84337 // pt.pts, then put it back into an array in a dummy trace
84338 // and call castHoverinfo on that.
84339 // TODO: do we want to have Fx.castHoverinfo somehow handle this?
84340 // it already takes an array for index, for 2D, so this seems tricky.
84341 hoverinfo = Fx.castHoverinfo({
84342 hoverinfo: [helpers.castOption(hoverinfo, pt.pts)],
84343 _module: trace._module
84344 }, fullLayout2, 0);
84345 }
84346
84347 if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name';
84348
84349 // in case we dragged over the pie from another subplot,
84350 // or if hover is turned off
84351 if(trace2.hovertemplate || (hoverinfo !== 'none' && hoverinfo !== 'skip' && hoverinfo)) {
84352 var rInscribed = pt.rInscribed || 0;
84353 var hoverCenterX = cx + pt.pxmid[0] * (1 - rInscribed);
84354 var hoverCenterY = cy + pt.pxmid[1] * (1 - rInscribed);
84355 var separators = fullLayout2.separators;
84356 var text = [];
84357
84358 if(hoverinfo && hoverinfo.indexOf('label') !== -1) text.push(pt.label);
84359 pt.text = helpers.castOption(trace2.hovertext || trace2.text, pt.pts);
84360 if(hoverinfo && hoverinfo.indexOf('text') !== -1) {
84361 var tx = pt.text;
84362 if(Lib.isValidTextValue(tx)) text.push(tx);
84363 }
84364 pt.value = pt.v;
84365 pt.valueLabel = helpers.formatPieValue(pt.v, separators);
84366 if(hoverinfo && hoverinfo.indexOf('value') !== -1) text.push(pt.valueLabel);
84367 pt.percent = pt.v / cd0.vTotal;
84368 pt.percentLabel = helpers.formatPiePercent(pt.percent, separators);
84369 if(hoverinfo && hoverinfo.indexOf('percent') !== -1) text.push(pt.percentLabel);
84370
84371 var hoverLabel = trace2.hoverlabel;
84372 var hoverFont = hoverLabel.font;
84373
84374 Fx.loneHover({
84375 trace: trace,
84376 x0: hoverCenterX - rInscribed * cd0.r,
84377 x1: hoverCenterX + rInscribed * cd0.r,
84378 y: hoverCenterY,
84379 text: text.join('<br>'),
84380 name: (trace2.hovertemplate || hoverinfo.indexOf('name') !== -1) ? trace2.name : undefined,
84381 idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right',
84382 color: helpers.castOption(hoverLabel.bgcolor, pt.pts) || pt.color,
84383 borderColor: helpers.castOption(hoverLabel.bordercolor, pt.pts),
84384 fontFamily: helpers.castOption(hoverFont.family, pt.pts),
84385 fontSize: helpers.castOption(hoverFont.size, pt.pts),
84386 fontColor: helpers.castOption(hoverFont.color, pt.pts),
84387 nameLength: helpers.castOption(hoverLabel.namelength, pt.pts),
84388 textAlign: helpers.castOption(hoverLabel.align, pt.pts),
84389 hovertemplate: helpers.castOption(trace2.hovertemplate, pt.pts),
84390 hovertemplateLabels: pt,
84391 eventData: [eventData(pt, trace2)]
84392 }, {
84393 container: fullLayout2._hoverlayer.node(),
84394 outerContainer: fullLayout2._paper.node(),
84395 gd: gd
84396 });
84397
84398 trace._hasHoverLabel = true;
84399 }
84400
84401 trace._hasHoverEvent = true;
84402 gd.emit('plotly_hover', {
84403 points: [eventData(pt, trace2)],
84404 event: d3.event
84405 });
84406 });
84407
84408 sliceTop.on('mouseout', function(evt) {
84409 var fullLayout2 = gd._fullLayout;
84410 var trace2 = gd._fullData[trace.index];
84411 var pt = d3.select(this).datum();
84412
84413 if(trace._hasHoverEvent) {
84414 evt.originalEvent = d3.event;
84415 gd.emit('plotly_unhover', {
84416 points: [eventData(pt, trace2)],
84417 event: d3.event
84418 });
84419 trace._hasHoverEvent = false;
84420 }
84421
84422 if(trace._hasHoverLabel) {
84423 Fx.loneUnhover(fullLayout2._hoverlayer.node());
84424 trace._hasHoverLabel = false;
84425 }
84426 });
84427
84428 sliceTop.on('click', function(pt) {
84429 // TODO: this does not support right-click. If we want to support it, we
84430 // would likely need to change pie to use dragElement instead of straight
84431 // mapbox event binding. Or perhaps better, make a simple wrapper with the
84432 // right mousedown, mousemove, and mouseup handlers just for a left/right click
84433 // mapbox would use this too.
84434 var fullLayout2 = gd._fullLayout;
84435 var trace2 = gd._fullData[trace.index];
84436
84437 if(gd._dragging || fullLayout2.hovermode === false) return;
84438
84439 gd._hoverdata = [eventData(pt, trace2)];
84440 Fx.click(gd, d3.event);
84441 });
84442}
84443
84444function determineOutsideTextFont(trace, pt, layoutFont) {
84445 var color =
84446 helpers.castOption(trace.outsidetextfont.color, pt.pts) ||
84447 helpers.castOption(trace.textfont.color, pt.pts) ||
84448 layoutFont.color;
84449
84450 var family =
84451 helpers.castOption(trace.outsidetextfont.family, pt.pts) ||
84452 helpers.castOption(trace.textfont.family, pt.pts) ||
84453 layoutFont.family;
84454
84455 var size =
84456 helpers.castOption(trace.outsidetextfont.size, pt.pts) ||
84457 helpers.castOption(trace.textfont.size, pt.pts) ||
84458 layoutFont.size;
84459
84460 return {
84461 color: color,
84462 family: family,
84463 size: size
84464 };
84465}
84466
84467function determineInsideTextFont(trace, pt, layoutFont) {
84468 var customColor = helpers.castOption(trace.insidetextfont.color, pt.pts);
84469 if(!customColor && trace._input.textfont) {
84470 // Why not simply using trace.textfont? Because if not set, it
84471 // defaults to layout.font which has a default color. But if
84472 // textfont.color and insidetextfont.color don't supply a value,
84473 // a contrasting color shall be used.
84474 customColor = helpers.castOption(trace._input.textfont.color, pt.pts);
84475 }
84476
84477 var family =
84478 helpers.castOption(trace.insidetextfont.family, pt.pts) ||
84479 helpers.castOption(trace.textfont.family, pt.pts) ||
84480 layoutFont.family;
84481
84482 var size =
84483 helpers.castOption(trace.insidetextfont.size, pt.pts) ||
84484 helpers.castOption(trace.textfont.size, pt.pts) ||
84485 layoutFont.size;
84486
84487 return {
84488 color: customColor || Color.contrast(pt.color),
84489 family: family,
84490 size: size
84491 };
84492}
84493
84494function prerenderTitles(cdModule, gd) {
84495 var cd0, trace;
84496
84497 // Determine the width and height of the title for each pie.
84498 for(var i = 0; i < cdModule.length; i++) {
84499 cd0 = cdModule[i][0];
84500 trace = cd0.trace;
84501
84502 if(trace.title.text) {
84503 var txt = trace.title.text;
84504 if(trace._meta) {
84505 txt = Lib.templateString(txt, trace._meta);
84506 }
84507
84508 var dummyTitle = Drawing.tester.append('text')
84509 .attr('data-notex', 1)
84510 .text(txt)
84511 .call(Drawing.font, trace.title.font)
84512 .call(svgTextUtils.convertToTspans, gd);
84513 var bBox = Drawing.bBox(dummyTitle.node(), true);
84514 cd0.titleBox = {
84515 width: bBox.width,
84516 height: bBox.height,
84517 };
84518 dummyTitle.remove();
84519 }
84520 }
84521}
84522
84523function transformInsideText(textBB, pt, cd0) {
84524 var r = cd0.r || pt.rpx1;
84525 var rInscribed = pt.rInscribed;
84526
84527 var isEmpty = pt.startangle === pt.stopangle;
84528 if(isEmpty) {
84529 return {
84530 rCenter: 1 - rInscribed,
84531 scale: 0,
84532 rotate: 0,
84533 textPosAngle: 0
84534 };
84535 }
84536
84537 var ring = pt.ring;
84538 var isCircle = (ring === 1) && (Math.abs(pt.startangle - pt.stopangle) === Math.PI * 2);
84539
84540 var halfAngle = pt.halfangle;
84541 var midAngle = pt.midangle;
84542
84543 var orientation = cd0.trace.insidetextorientation;
84544 var isHorizontal = orientation === 'horizontal';
84545 var isTangential = orientation === 'tangential';
84546 var isRadial = orientation === 'radial';
84547 var isAuto = orientation === 'auto';
84548
84549 var allTransforms = [];
84550 var newT;
84551
84552 if(!isAuto) {
84553 // max size if text is placed (horizontally) at the top or bottom of the arc
84554
84555 var considerCrossing = function(angle, key) {
84556 if(isCrossing(pt, angle)) {
84557 var dStart = Math.abs(angle - pt.startangle);
84558 var dStop = Math.abs(angle - pt.stopangle);
84559
84560 var closestEdge = dStart < dStop ? dStart : dStop;
84561
84562 if(key === 'tan') {
84563 newT = calcTanTransform(textBB, r, ring, closestEdge, 0);
84564 } else { // case of 'rad'
84565 newT = calcRadTransform(textBB, r, ring, closestEdge, Math.PI / 2);
84566 }
84567 newT.textPosAngle = angle;
84568
84569 allTransforms.push(newT);
84570 }
84571 };
84572
84573 // to cover all cases with trace.rotation added
84574 var i;
84575 if(isHorizontal || isTangential) {
84576 // top
84577 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * i, 'tan');
84578 // bottom
84579 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1), 'tan');
84580 }
84581 if(isHorizontal || isRadial) {
84582 // left
84583 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1.5), 'rad');
84584 // right
84585 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 0.5), 'rad');
84586 }
84587 }
84588
84589 if(isCircle || isAuto || isHorizontal) {
84590 // max size text can be inserted inside without rotating it
84591 // this inscribes the text rectangle in a circle, which is then inscribed
84592 // in the slice, so it will be an underestimate, which some day we may want
84593 // to improve so this case can get more use
84594 var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height);
84595
84596 newT = {
84597 scale: rInscribed * r * 2 / textDiameter,
84598
84599 // and the center position and rotation in this case
84600 rCenter: 1 - rInscribed,
84601 rotate: 0
84602 };
84603
84604 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
84605 if(newT.scale >= 1) return newT;
84606
84607 allTransforms.push(newT);
84608 }
84609
84610 if(isAuto || isRadial) {
84611 newT = calcRadTransform(textBB, r, ring, halfAngle, midAngle);
84612 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
84613 allTransforms.push(newT);
84614 }
84615
84616 if(isAuto || isTangential) {
84617 newT = calcTanTransform(textBB, r, ring, halfAngle, midAngle);
84618 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
84619 allTransforms.push(newT);
84620 }
84621
84622 var id = 0;
84623 var maxScale = 0;
84624 for(var k = 0; k < allTransforms.length; k++) {
84625 var s = allTransforms[k].scale;
84626 if(maxScale < s) {
84627 maxScale = s;
84628 id = k;
84629 }
84630
84631 if(!isAuto && maxScale >= 1) {
84632 // respect test order for non-auto options
84633 break;
84634 }
84635 }
84636 return allTransforms[id];
84637}
84638
84639function isCrossing(pt, angle) {
84640 var start = pt.startangle;
84641 var stop = pt.stopangle;
84642 return (
84643 (start > angle && angle > stop) ||
84644 (start < angle && angle < stop)
84645 );
84646}
84647
84648function calcRadTransform(textBB, r, ring, halfAngle, midAngle) {
84649 r = Math.max(0, r - 2 * TEXTPAD);
84650
84651 // max size if text is rotated radially
84652 var a = textBB.width / textBB.height;
84653 var s = calcMaxHalfSize(a, halfAngle, r, ring);
84654 return {
84655 scale: s * 2 / textBB.height,
84656 rCenter: calcRCenter(a, s / r),
84657 rotate: calcRotate(midAngle)
84658 };
84659}
84660
84661function calcTanTransform(textBB, r, ring, halfAngle, midAngle) {
84662 r = Math.max(0, r - 2 * TEXTPAD);
84663
84664 // max size if text is rotated tangentially
84665 var a = textBB.height / textBB.width;
84666 var s = calcMaxHalfSize(a, halfAngle, r, ring);
84667 return {
84668 scale: s * 2 / textBB.width,
84669 rCenter: calcRCenter(a, s / r),
84670 rotate: calcRotate(midAngle + Math.PI / 2)
84671 };
84672}
84673
84674function calcRCenter(a, b) {
84675 return Math.cos(b) - a * b;
84676}
84677
84678function calcRotate(t) {
84679 return (180 / Math.PI * t + 720) % 180 - 90;
84680}
84681
84682function calcMaxHalfSize(a, halfAngle, r, ring) {
84683 var q = a + 1 / (2 * Math.tan(halfAngle));
84684 return r * Math.min(
84685 1 / (Math.sqrt(q * q + 0.5) + q),
84686 ring / (Math.sqrt(a * a + ring / 2) + a)
84687 );
84688}
84689
84690function getInscribedRadiusFraction(pt, cd0) {
84691 if(pt.v === cd0.vTotal && !cd0.trace.hole) return 1;// special case of 100% with no hole
84692
84693 return Math.min(1 / (1 + 1 / Math.sin(pt.halfangle)), pt.ring / 2);
84694}
84695
84696function transformOutsideText(textBB, pt) {
84697 var x = pt.pxmid[0];
84698 var y = pt.pxmid[1];
84699 var dx = textBB.width / 2;
84700 var dy = textBB.height / 2;
84701
84702 if(x < 0) dx *= -1;
84703 if(y < 0) dy *= -1;
84704
84705 return {
84706 scale: 1,
84707 rCenter: 1,
84708 rotate: 0,
84709 x: dx + Math.abs(dy) * (dx > 0 ? 1 : -1) / 2,
84710 y: dy / (1 + x * x / (y * y)),
84711 outside: true
84712 };
84713}
84714
84715function positionTitleInside(cd0) {
84716 var textDiameter =
84717 Math.sqrt(cd0.titleBox.width * cd0.titleBox.width + cd0.titleBox.height * cd0.titleBox.height);
84718 return {
84719 x: cd0.cx,
84720 y: cd0.cy,
84721 scale: cd0.trace.hole * cd0.r * 2 / textDiameter,
84722 tx: 0,
84723 ty: - cd0.titleBox.height / 2 + cd0.trace.title.font.size
84724 };
84725}
84726
84727function positionTitleOutside(cd0, plotSize) {
84728 var scaleX = 1;
84729 var scaleY = 1;
84730 var maxPull;
84731
84732 var trace = cd0.trace;
84733 // position of the baseline point of the text box in the plot, before scaling.
84734 // we anchored the text in the middle, so the baseline is on the bottom middle
84735 // of the first line of text.
84736 var topMiddle = {
84737 x: cd0.cx,
84738 y: cd0.cy
84739 };
84740 // relative translation of the text box after scaling
84741 var translate = {
84742 tx: 0,
84743 ty: 0
84744 };
84745
84746 // we reason below as if the baseline is the top middle point of the text box.
84747 // so we must add the font size to approximate the y-coord. of the top.
84748 // note that this correction must happen after scaling.
84749 translate.ty += trace.title.font.size;
84750 maxPull = getMaxPull(trace);
84751
84752 if(trace.title.position.indexOf('top') !== -1) {
84753 topMiddle.y -= (1 + maxPull) * cd0.r;
84754 translate.ty -= cd0.titleBox.height;
84755 } else if(trace.title.position.indexOf('bottom') !== -1) {
84756 topMiddle.y += (1 + maxPull) * cd0.r;
84757 }
84758
84759 var rx = applyAspectRatio(cd0.r, cd0.trace.aspectratio);
84760
84761 var maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]) / 2;
84762 if(trace.title.position.indexOf('left') !== -1) {
84763 // we start the text at the left edge of the pie
84764 maxWidth = maxWidth + rx;
84765 topMiddle.x -= (1 + maxPull) * rx;
84766 translate.tx += cd0.titleBox.width / 2;
84767 } else if(trace.title.position.indexOf('center') !== -1) {
84768 maxWidth *= 2;
84769 } else if(trace.title.position.indexOf('right') !== -1) {
84770 maxWidth = maxWidth + rx;
84771 topMiddle.x += (1 + maxPull) * rx;
84772 translate.tx -= cd0.titleBox.width / 2;
84773 }
84774 scaleX = maxWidth / cd0.titleBox.width;
84775 scaleY = getTitleSpace(cd0, plotSize) / cd0.titleBox.height;
84776 return {
84777 x: topMiddle.x,
84778 y: topMiddle.y,
84779 scale: Math.min(scaleX, scaleY),
84780 tx: translate.tx,
84781 ty: translate.ty
84782 };
84783}
84784
84785function applyAspectRatio(x, aspectratio) {
84786 return x / ((aspectratio === undefined) ? 1 : aspectratio);
84787}
84788
84789function getTitleSpace(cd0, plotSize) {
84790 var trace = cd0.trace;
84791 var pieBoxHeight = plotSize.h * (trace.domain.y[1] - trace.domain.y[0]);
84792 // use at most half of the plot for the title
84793 return Math.min(cd0.titleBox.height, pieBoxHeight / 2);
84794}
84795
84796function getMaxPull(trace) {
84797 var maxPull = trace.pull;
84798 if(!maxPull) return 0;
84799
84800 var j;
84801 if(Array.isArray(maxPull)) {
84802 maxPull = 0;
84803 for(j = 0; j < trace.pull.length; j++) {
84804 if(trace.pull[j] > maxPull) maxPull = trace.pull[j];
84805 }
84806 }
84807 return maxPull;
84808}
84809
84810function scootLabels(quadrants, trace) {
84811 var xHalf, yHalf, equatorFirst, farthestX, farthestY,
84812 xDiffSign, yDiffSign, thisQuad, oppositeQuad,
84813 wholeSide, i, thisQuadOutside, firstOppositeOutsidePt;
84814
84815 function topFirst(a, b) { return a.pxmid[1] - b.pxmid[1]; }
84816 function bottomFirst(a, b) { return b.pxmid[1] - a.pxmid[1]; }
84817
84818 function scootOneLabel(thisPt, prevPt) {
84819 if(!prevPt) prevPt = {};
84820
84821 var prevOuterY = prevPt.labelExtraY + (yHalf ? prevPt.yLabelMax : prevPt.yLabelMin);
84822 var thisInnerY = yHalf ? thisPt.yLabelMin : thisPt.yLabelMax;
84823 var thisOuterY = yHalf ? thisPt.yLabelMax : thisPt.yLabelMin;
84824 var thisSliceOuterY = thisPt.cyFinal + farthestY(thisPt.px0[1], thisPt.px1[1]);
84825 var newExtraY = prevOuterY - thisInnerY;
84826
84827 var xBuffer, i, otherPt, otherOuterY, otherOuterX, newExtraX;
84828
84829 // make sure this label doesn't overlap other labels
84830 // this *only* has us move these labels vertically
84831 if(newExtraY * yDiffSign > 0) thisPt.labelExtraY = newExtraY;
84832
84833 // make sure this label doesn't overlap any slices
84834 if(!Array.isArray(trace.pull)) return; // this can only happen with array pulls
84835
84836 for(i = 0; i < wholeSide.length; i++) {
84837 otherPt = wholeSide[i];
84838
84839 // overlap can only happen if the other point is pulled more than this one
84840 if(otherPt === thisPt || (
84841 (helpers.castOption(trace.pull, thisPt.pts) || 0) >=
84842 (helpers.castOption(trace.pull, otherPt.pts) || 0))
84843 ) {
84844 continue;
84845 }
84846
84847 if((thisPt.pxmid[1] - otherPt.pxmid[1]) * yDiffSign > 0) {
84848 // closer to the equator - by construction all of these happen first
84849 // move the text vertically to get away from these slices
84850 otherOuterY = otherPt.cyFinal + farthestY(otherPt.px0[1], otherPt.px1[1]);
84851 newExtraY = otherOuterY - thisInnerY - thisPt.labelExtraY;
84852
84853 if(newExtraY * yDiffSign > 0) thisPt.labelExtraY += newExtraY;
84854 } else if((thisOuterY + thisPt.labelExtraY - thisSliceOuterY) * yDiffSign > 0) {
84855 // farther from the equator - happens after we've done all the
84856 // vertical moving we're going to do
84857 // move horizontally to get away from these more polar slices
84858
84859 // if we're moving horz. based on a slice that's several slices away from this one
84860 // then we need some extra space for the lines to labels between them
84861 xBuffer = 3 * xDiffSign * Math.abs(i - wholeSide.indexOf(thisPt));
84862
84863 otherOuterX = otherPt.cxFinal + farthestX(otherPt.px0[0], otherPt.px1[0]);
84864 newExtraX = otherOuterX + xBuffer - (thisPt.cxFinal + thisPt.pxmid[0]) - thisPt.labelExtraX;
84865
84866 if(newExtraX * xDiffSign > 0) thisPt.labelExtraX += newExtraX;
84867 }
84868 }
84869 }
84870
84871 for(yHalf = 0; yHalf < 2; yHalf++) {
84872 equatorFirst = yHalf ? topFirst : bottomFirst;
84873 farthestY = yHalf ? Math.max : Math.min;
84874 yDiffSign = yHalf ? 1 : -1;
84875
84876 for(xHalf = 0; xHalf < 2; xHalf++) {
84877 farthestX = xHalf ? Math.max : Math.min;
84878 xDiffSign = xHalf ? 1 : -1;
84879
84880 // first sort the array
84881 // note this is a copy of cd, so cd itself doesn't get sorted
84882 // but we can still modify points in place.
84883 thisQuad = quadrants[yHalf][xHalf];
84884 thisQuad.sort(equatorFirst);
84885
84886 oppositeQuad = quadrants[1 - yHalf][xHalf];
84887 wholeSide = oppositeQuad.concat(thisQuad);
84888
84889 thisQuadOutside = [];
84890 for(i = 0; i < thisQuad.length; i++) {
84891 if(thisQuad[i].yLabelMid !== undefined) thisQuadOutside.push(thisQuad[i]);
84892 }
84893
84894 firstOppositeOutsidePt = false;
84895 for(i = 0; yHalf && i < oppositeQuad.length; i++) {
84896 if(oppositeQuad[i].yLabelMid !== undefined) {
84897 firstOppositeOutsidePt = oppositeQuad[i];
84898 break;
84899 }
84900 }
84901
84902 // each needs to avoid the previous
84903 for(i = 0; i < thisQuadOutside.length; i++) {
84904 var prevPt = i && thisQuadOutside[i - 1];
84905 // bottom half needs to avoid the first label of the top half
84906 // top half we still need to call scootOneLabel on the first slice
84907 // so we can avoid other slices, but we don't pass a prevPt
84908 if(firstOppositeOutsidePt && !i) prevPt = firstOppositeOutsidePt;
84909 scootOneLabel(thisQuadOutside[i], prevPt);
84910 }
84911 }
84912 }
84913}
84914
84915function layoutAreas(cdModule, plotSize) {
84916 var scaleGroups = [];
84917
84918 // figure out the center and maximum radius
84919 for(var i = 0; i < cdModule.length; i++) {
84920 var cd0 = cdModule[i][0];
84921 var trace = cd0.trace;
84922
84923 var domain = trace.domain;
84924 var width = plotSize.w * (domain.x[1] - domain.x[0]);
84925 var height = plotSize.h * (domain.y[1] - domain.y[0]);
84926 // leave some space for the title, if it will be displayed outside
84927 if(trace.title.text && trace.title.position !== 'middle center') {
84928 height -= getTitleSpace(cd0, plotSize);
84929 }
84930
84931 var rx = width / 2;
84932 var ry = height / 2;
84933 if(trace.type === 'funnelarea' && !trace.scalegroup) {
84934 ry /= trace.aspectratio;
84935 }
84936
84937 cd0.r = Math.min(rx, ry) / (1 + getMaxPull(trace));
84938
84939 cd0.cx = plotSize.l + plotSize.w * (trace.domain.x[1] + trace.domain.x[0]) / 2;
84940 cd0.cy = plotSize.t + plotSize.h * (1 - trace.domain.y[0]) - height / 2;
84941 if(trace.title.text && trace.title.position.indexOf('bottom') !== -1) {
84942 cd0.cy -= getTitleSpace(cd0, plotSize);
84943 }
84944
84945 if(trace.scalegroup && scaleGroups.indexOf(trace.scalegroup) === -1) {
84946 scaleGroups.push(trace.scalegroup);
84947 }
84948 }
84949
84950 groupScale(cdModule, scaleGroups);
84951}
84952
84953function groupScale(cdModule, scaleGroups) {
84954 var cd0, i, trace;
84955
84956 // scale those that are grouped
84957 for(var k = 0; k < scaleGroups.length; k++) {
84958 var min = Infinity;
84959 var g = scaleGroups[k];
84960
84961 for(i = 0; i < cdModule.length; i++) {
84962 cd0 = cdModule[i][0];
84963 trace = cd0.trace;
84964
84965 if(trace.scalegroup === g) {
84966 var area;
84967 if(trace.type === 'pie') {
84968 area = cd0.r * cd0.r;
84969 } else if(trace.type === 'funnelarea') {
84970 var rx, ry;
84971
84972 if(trace.aspectratio > 1) {
84973 rx = cd0.r;
84974 ry = rx / trace.aspectratio;
84975 } else {
84976 ry = cd0.r;
84977 rx = ry * trace.aspectratio;
84978 }
84979
84980 rx *= (1 + trace.baseratio) / 2;
84981
84982 area = rx * ry;
84983 }
84984
84985 min = Math.min(min, area / cd0.vTotal);
84986 }
84987 }
84988
84989 for(i = 0; i < cdModule.length; i++) {
84990 cd0 = cdModule[i][0];
84991 trace = cd0.trace;
84992 if(trace.scalegroup === g) {
84993 var v = min * cd0.vTotal;
84994 if(trace.type === 'funnelarea') {
84995 v /= (1 + trace.baseratio) / 2;
84996 v /= trace.aspectratio;
84997 }
84998
84999 cd0.r = Math.sqrt(v);
85000 }
85001 }
85002 }
85003}
85004
85005function setCoords(cd) {
85006 var cd0 = cd[0];
85007 var r = cd0.r;
85008 var trace = cd0.trace;
85009 var currentAngle = trace.rotation * Math.PI / 180;
85010 var angleFactor = 2 * Math.PI / cd0.vTotal;
85011 var firstPt = 'px0';
85012 var lastPt = 'px1';
85013
85014 var i, cdi, currentCoords;
85015
85016 if(trace.direction === 'counterclockwise') {
85017 for(i = 0; i < cd.length; i++) {
85018 if(!cd[i].hidden) break; // find the first non-hidden slice
85019 }
85020 if(i === cd.length) return; // all slices hidden
85021
85022 currentAngle += angleFactor * cd[i].v;
85023 angleFactor *= -1;
85024 firstPt = 'px1';
85025 lastPt = 'px0';
85026 }
85027
85028 currentCoords = getCoords(r, currentAngle);
85029
85030 for(i = 0; i < cd.length; i++) {
85031 cdi = cd[i];
85032 if(cdi.hidden) continue;
85033
85034 cdi[firstPt] = currentCoords;
85035
85036 cdi.startangle = currentAngle;
85037 currentAngle += angleFactor * cdi.v / 2;
85038 cdi.pxmid = getCoords(r, currentAngle);
85039 cdi.midangle = currentAngle;
85040 currentAngle += angleFactor * cdi.v / 2;
85041 currentCoords = getCoords(r, currentAngle);
85042 cdi.stopangle = currentAngle;
85043
85044 cdi[lastPt] = currentCoords;
85045
85046 cdi.largeArc = (cdi.v > cd0.vTotal / 2) ? 1 : 0;
85047
85048 cdi.halfangle = Math.PI * Math.min(cdi.v / cd0.vTotal, 0.5);
85049 cdi.ring = 1 - trace.hole;
85050 cdi.rInscribed = getInscribedRadiusFraction(cdi, cd0);
85051 }
85052}
85053
85054function getCoords(r, angle) {
85055 return [r * Math.sin(angle), -r * Math.cos(angle)];
85056}
85057
85058function formatSliceLabel(gd, pt, cd0) {
85059 var fullLayout = gd._fullLayout;
85060 var trace = cd0.trace;
85061 // look for textemplate
85062 var texttemplate = trace.texttemplate;
85063
85064 // now insert text
85065 var textinfo = trace.textinfo;
85066 if(!texttemplate && textinfo && textinfo !== 'none') {
85067 var parts = textinfo.split('+');
85068 var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
85069 var hasLabel = hasFlag('label');
85070 var hasText = hasFlag('text');
85071 var hasValue = hasFlag('value');
85072 var hasPercent = hasFlag('percent');
85073
85074 var separators = fullLayout.separators;
85075 var text;
85076
85077 text = hasLabel ? [pt.label] : [];
85078 if(hasText) {
85079 var tx = helpers.getFirstFilled(trace.text, pt.pts);
85080 if(isValidTextValue(tx)) text.push(tx);
85081 }
85082 if(hasValue) text.push(helpers.formatPieValue(pt.v, separators));
85083 if(hasPercent) text.push(helpers.formatPiePercent(pt.v / cd0.vTotal, separators));
85084 pt.text = text.join('<br>');
85085 }
85086
85087 function makeTemplateVariables(pt) {
85088 return {
85089 label: pt.label,
85090 value: pt.v,
85091 valueLabel: helpers.formatPieValue(pt.v, fullLayout.separators),
85092 percent: pt.v / cd0.vTotal,
85093 percentLabel: helpers.formatPiePercent(pt.v / cd0.vTotal, fullLayout.separators),
85094 color: pt.color,
85095 text: pt.text,
85096 customdata: Lib.castOption(trace, pt.i, 'customdata')
85097 };
85098 }
85099
85100 if(texttemplate) {
85101 var txt = Lib.castOption(trace, pt.i, 'texttemplate');
85102 if(!txt) {
85103 pt.text = '';
85104 } else {
85105 var obj = makeTemplateVariables(pt);
85106 var ptTx = helpers.getFirstFilled(trace.text, pt.pts);
85107 if(isValidTextValue(ptTx) || ptTx === '') obj.text = ptTx;
85108 pt.text = Lib.texttemplateString(txt, obj, gd._fullLayout._d3locale, obj, trace._meta || {});
85109 }
85110 }
85111}
85112
85113function computeTransform(
85114 transform, // inout
85115 textBB // in
85116) {
85117 var a = transform.rotate * Math.PI / 180;
85118 var cosA = Math.cos(a);
85119 var sinA = Math.sin(a);
85120 var midX = (textBB.left + textBB.right) / 2;
85121 var midY = (textBB.top + textBB.bottom) / 2;
85122 transform.textX = midX * cosA - midY * sinA;
85123 transform.textY = midX * sinA + midY * cosA;
85124 transform.noCenter = true;
85125}
85126
85127module.exports = {
85128 plot: plot,
85129 formatSliceLabel: formatSliceLabel,
85130 transformInsideText: transformInsideText,
85131 determineInsideTextFont: determineInsideTextFont,
85132 positionTitleOutside: positionTitleOutside,
85133 prerenderTitles: prerenderTitles,
85134 layoutAreas: layoutAreas,
85135 attachFxHandlers: attachFxHandlers,
85136 computeTransform: computeTransform
85137};
85138
85139},{"../../components/color":52,"../../components/drawing":74,"../../components/fx":92,"../../lib":178,"../../lib/svg_text_utils":199,"../../plots/plots":256,"../bar/constants":281,"../bar/uniform_text":295,"./event_data":380,"./helpers":381,"d3":16}],386:[function(_dereq_,module,exports){
85140/**
85141* Copyright 2012-2020, Plotly, Inc.
85142* All rights reserved.
85143*
85144* This source code is licensed under the MIT license found in the
85145* LICENSE file in the root directory of this source tree.
85146*/
85147
85148'use strict';
85149
85150var d3 = _dereq_('d3');
85151
85152var styleOne = _dereq_('./style_one');
85153var resizeText = _dereq_('../bar/uniform_text').resizeText;
85154
85155module.exports = function style(gd) {
85156 var s = gd._fullLayout._pielayer.selectAll('.trace');
85157 resizeText(gd, s, 'pie');
85158
85159 s.each(function(cd) {
85160 var cd0 = cd[0];
85161 var trace = cd0.trace;
85162 var traceSelection = d3.select(this);
85163
85164 traceSelection.style({opacity: trace.opacity});
85165
85166 traceSelection.selectAll('path.surface').each(function(pt) {
85167 d3.select(this).call(styleOne, pt, trace);
85168 });
85169 });
85170};
85171
85172},{"../bar/uniform_text":295,"./style_one":387,"d3":16}],387:[function(_dereq_,module,exports){
85173/**
85174* Copyright 2012-2020, Plotly, Inc.
85175* All rights reserved.
85176*
85177* This source code is licensed under the MIT license found in the
85178* LICENSE file in the root directory of this source tree.
85179*/
85180
85181'use strict';
85182
85183var Color = _dereq_('../../components/color');
85184var castOption = _dereq_('./helpers').castOption;
85185
85186module.exports = function styleOne(s, pt, trace) {
85187 var line = trace.marker.line;
85188 var lineColor = castOption(line.color, pt.pts) || Color.defaultLine;
85189 var lineWidth = castOption(line.width, pt.pts) || 0;
85190
85191 s.style('stroke-width', lineWidth)
85192 .call(Color.fill, pt.color)
85193 .call(Color.stroke, lineColor);
85194};
85195
85196},{"../../components/color":52,"./helpers":381}],388:[function(_dereq_,module,exports){
85197/**
85198* Copyright 2012-2020, Plotly, Inc.
85199* All rights reserved.
85200*
85201* This source code is licensed under the MIT license found in the
85202* LICENSE file in the root directory of this source tree.
85203*/
85204
85205
85206'use strict';
85207
85208var Lib = _dereq_('../../lib');
85209
85210
85211// arrayOk attributes, merge them into calcdata array
85212module.exports = function arraysToCalcdata(cd, trace) {
85213 // so each point knows which index it originally came from
85214 for(var i = 0; i < cd.length; i++) cd[i].i = i;
85215
85216 Lib.mergeArray(trace.text, cd, 'tx');
85217 Lib.mergeArray(trace.texttemplate, cd, 'txt');
85218 Lib.mergeArray(trace.hovertext, cd, 'htx');
85219 Lib.mergeArray(trace.customdata, cd, 'data');
85220 Lib.mergeArray(trace.textposition, cd, 'tp');
85221 if(trace.textfont) {
85222 Lib.mergeArrayCastPositive(trace.textfont.size, cd, 'ts');
85223 Lib.mergeArray(trace.textfont.color, cd, 'tc');
85224 Lib.mergeArray(trace.textfont.family, cd, 'tf');
85225 }
85226
85227 var marker = trace.marker;
85228 if(marker) {
85229 Lib.mergeArrayCastPositive(marker.size, cd, 'ms');
85230 Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo');
85231 Lib.mergeArray(marker.symbol, cd, 'mx');
85232 Lib.mergeArray(marker.color, cd, 'mc');
85233
85234 var markerLine = marker.line;
85235 if(marker.line) {
85236 Lib.mergeArray(markerLine.color, cd, 'mlc');
85237 Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw');
85238 }
85239
85240 var markerGradient = marker.gradient;
85241 if(markerGradient && markerGradient.type !== 'none') {
85242 Lib.mergeArray(markerGradient.type, cd, 'mgt');
85243 Lib.mergeArray(markerGradient.color, cd, 'mgc');
85244 }
85245 }
85246};
85247
85248},{"../../lib":178}],389:[function(_dereq_,module,exports){
85249/**
85250* Copyright 2012-2020, Plotly, Inc.
85251* All rights reserved.
85252*
85253* This source code is licensed under the MIT license found in the
85254* LICENSE file in the root directory of this source tree.
85255*/
85256
85257'use strict';
85258
85259var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
85260var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
85261var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
85262var fontAttrs = _dereq_('../../plots/font_attributes');
85263var dash = _dereq_('../../components/drawing/attributes').dash;
85264
85265var Drawing = _dereq_('../../components/drawing');
85266var constants = _dereq_('./constants');
85267var extendFlat = _dereq_('../../lib/extend').extendFlat;
85268
85269module.exports = {
85270 x: {
85271 valType: 'data_array',
85272 editType: 'calc+clearAxisTypes',
85273 anim: true,
85274
85275 },
85276 x0: {
85277 valType: 'any',
85278 dflt: 0,
85279
85280 editType: 'calc+clearAxisTypes',
85281 anim: true,
85282
85283 },
85284 dx: {
85285 valType: 'number',
85286 dflt: 1,
85287
85288 editType: 'calc',
85289 anim: true,
85290
85291 },
85292 y: {
85293 valType: 'data_array',
85294 editType: 'calc+clearAxisTypes',
85295 anim: true,
85296
85297 },
85298 y0: {
85299 valType: 'any',
85300 dflt: 0,
85301
85302 editType: 'calc+clearAxisTypes',
85303 anim: true,
85304
85305 },
85306 dy: {
85307 valType: 'number',
85308 dflt: 1,
85309
85310 editType: 'calc',
85311 anim: true,
85312
85313 },
85314
85315 stackgroup: {
85316 valType: 'string',
85317
85318 dflt: '',
85319 editType: 'calc',
85320
85321 },
85322 orientation: {
85323 valType: 'enumerated',
85324
85325 values: ['v', 'h'],
85326 editType: 'calc',
85327
85328 },
85329 groupnorm: {
85330 valType: 'enumerated',
85331 values: ['', 'fraction', 'percent'],
85332 dflt: '',
85333
85334 editType: 'calc',
85335
85336 },
85337 stackgaps: {
85338 valType: 'enumerated',
85339 values: ['infer zero', 'interpolate'],
85340 dflt: 'infer zero',
85341
85342 editType: 'calc',
85343
85344 },
85345
85346 text: {
85347 valType: 'string',
85348
85349 dflt: '',
85350 arrayOk: true,
85351 editType: 'calc',
85352
85353 },
85354
85355 texttemplate: texttemplateAttrs({}, {
85356
85357 }),
85358 hovertext: {
85359 valType: 'string',
85360
85361 dflt: '',
85362 arrayOk: true,
85363 editType: 'style',
85364
85365 },
85366 mode: {
85367 valType: 'flaglist',
85368 flags: ['lines', 'markers', 'text'],
85369 extras: ['none'],
85370
85371 editType: 'calc',
85372
85373 },
85374 hoveron: {
85375 valType: 'flaglist',
85376 flags: ['points', 'fills'],
85377
85378 editType: 'style',
85379
85380 },
85381 hovertemplate: hovertemplateAttrs({}, {
85382 keys: constants.eventDataKeys
85383 }),
85384 line: {
85385 color: {
85386 valType: 'color',
85387
85388 editType: 'style',
85389 anim: true,
85390
85391 },
85392 width: {
85393 valType: 'number',
85394 min: 0,
85395 dflt: 2,
85396
85397 editType: 'style',
85398 anim: true,
85399
85400 },
85401 shape: {
85402 valType: 'enumerated',
85403 values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'],
85404 dflt: 'linear',
85405
85406 editType: 'plot',
85407
85408 },
85409 smoothing: {
85410 valType: 'number',
85411 min: 0,
85412 max: 1.3,
85413 dflt: 1,
85414
85415 editType: 'plot',
85416
85417 },
85418 dash: extendFlat({}, dash, {editType: 'style'}),
85419 simplify: {
85420 valType: 'boolean',
85421 dflt: true,
85422
85423 editType: 'plot',
85424
85425 },
85426 editType: 'plot'
85427 },
85428
85429 connectgaps: {
85430 valType: 'boolean',
85431 dflt: false,
85432
85433 editType: 'calc',
85434
85435 },
85436 cliponaxis: {
85437 valType: 'boolean',
85438 dflt: true,
85439
85440 editType: 'plot',
85441
85442 },
85443
85444 fill: {
85445 valType: 'enumerated',
85446 values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
85447
85448 editType: 'calc',
85449
85450 },
85451 fillcolor: {
85452 valType: 'color',
85453
85454 editType: 'style',
85455 anim: true,
85456
85457 },
85458 marker: extendFlat({
85459 symbol: {
85460 valType: 'enumerated',
85461 values: Drawing.symbolList,
85462 dflt: 'circle',
85463 arrayOk: true,
85464
85465 editType: 'style',
85466
85467 },
85468 opacity: {
85469 valType: 'number',
85470 min: 0,
85471 max: 1,
85472 arrayOk: true,
85473
85474 editType: 'style',
85475 anim: true,
85476
85477 },
85478 size: {
85479 valType: 'number',
85480 min: 0,
85481 dflt: 6,
85482 arrayOk: true,
85483
85484 editType: 'calc',
85485 anim: true,
85486
85487 },
85488 maxdisplayed: {
85489 valType: 'number',
85490 min: 0,
85491 dflt: 0,
85492
85493 editType: 'plot',
85494
85495 },
85496 sizeref: {
85497 valType: 'number',
85498 dflt: 1,
85499
85500 editType: 'calc',
85501
85502 },
85503 sizemin: {
85504 valType: 'number',
85505 min: 0,
85506 dflt: 0,
85507
85508 editType: 'calc',
85509
85510 },
85511 sizemode: {
85512 valType: 'enumerated',
85513 values: ['diameter', 'area'],
85514 dflt: 'diameter',
85515
85516 editType: 'calc',
85517
85518 },
85519
85520 line: extendFlat({
85521 width: {
85522 valType: 'number',
85523 min: 0,
85524 arrayOk: true,
85525
85526 editType: 'style',
85527 anim: true,
85528
85529 },
85530 editType: 'calc'
85531 },
85532 colorScaleAttrs('marker.line', {anim: true})
85533 ),
85534 gradient: {
85535 type: {
85536 valType: 'enumerated',
85537 values: ['radial', 'horizontal', 'vertical', 'none'],
85538 arrayOk: true,
85539 dflt: 'none',
85540
85541 editType: 'calc',
85542
85543 },
85544 color: {
85545 valType: 'color',
85546 arrayOk: true,
85547
85548 editType: 'calc',
85549
85550 },
85551 editType: 'calc'
85552 },
85553 editType: 'calc'
85554 },
85555 colorScaleAttrs('marker', {anim: true})
85556 ),
85557 selected: {
85558 marker: {
85559 opacity: {
85560 valType: 'number',
85561 min: 0,
85562 max: 1,
85563
85564 editType: 'style',
85565
85566 },
85567 color: {
85568 valType: 'color',
85569
85570 editType: 'style',
85571
85572 },
85573 size: {
85574 valType: 'number',
85575 min: 0,
85576
85577 editType: 'style',
85578
85579 },
85580 editType: 'style'
85581 },
85582 textfont: {
85583 color: {
85584 valType: 'color',
85585
85586 editType: 'style',
85587
85588 },
85589 editType: 'style'
85590 },
85591 editType: 'style'
85592 },
85593 unselected: {
85594 marker: {
85595 opacity: {
85596 valType: 'number',
85597 min: 0,
85598 max: 1,
85599
85600 editType: 'style',
85601
85602 },
85603 color: {
85604 valType: 'color',
85605
85606 editType: 'style',
85607
85608 },
85609 size: {
85610 valType: 'number',
85611 min: 0,
85612
85613 editType: 'style',
85614
85615 },
85616 editType: 'style'
85617 },
85618 textfont: {
85619 color: {
85620 valType: 'color',
85621
85622 editType: 'style',
85623
85624 },
85625 editType: 'style'
85626 },
85627 editType: 'style'
85628 },
85629
85630 textposition: {
85631 valType: 'enumerated',
85632 values: [
85633 'top left', 'top center', 'top right',
85634 'middle left', 'middle center', 'middle right',
85635 'bottom left', 'bottom center', 'bottom right'
85636 ],
85637 dflt: 'middle center',
85638 arrayOk: true,
85639
85640 editType: 'calc',
85641
85642 },
85643 textfont: fontAttrs({
85644 editType: 'calc',
85645 colorEditType: 'style',
85646 arrayOk: true,
85647
85648 }),
85649
85650 r: {
85651 valType: 'data_array',
85652 editType: 'calc',
85653
85654 },
85655 t: {
85656 valType: 'data_array',
85657 editType: 'calc',
85658
85659 }
85660};
85661
85662},{"../../components/colorscale/attributes":59,"../../components/drawing":74,"../../components/drawing/attributes":73,"../../lib/extend":173,"../../plots/font_attributes":250,"../../plots/template_attributes":264,"./constants":393}],390:[function(_dereq_,module,exports){
85663/**
85664* Copyright 2012-2020, Plotly, Inc.
85665* All rights reserved.
85666*
85667* This source code is licensed under the MIT license found in the
85668* LICENSE file in the root directory of this source tree.
85669*/
85670
85671'use strict';
85672
85673var isNumeric = _dereq_('fast-isnumeric');
85674var Lib = _dereq_('../../lib');
85675
85676var Axes = _dereq_('../../plots/cartesian/axes');
85677var BADNUM = _dereq_('../../constants/numerical').BADNUM;
85678
85679var subTypes = _dereq_('./subtypes');
85680var calcColorscale = _dereq_('./colorscale_calc');
85681var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
85682var calcSelection = _dereq_('./calc_selection');
85683
85684function calc(gd, trace) {
85685 var fullLayout = gd._fullLayout;
85686 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
85687 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
85688 var x = xa.makeCalcdata(trace, 'x');
85689 var y = ya.makeCalcdata(trace, 'y');
85690 var serieslen = trace._length;
85691 var cd = new Array(serieslen);
85692 var ids = trace.ids;
85693 var stackGroupOpts = getStackOpts(trace, fullLayout, xa, ya);
85694 var interpolateGaps = false;
85695 var isV, i, j, k, interpolate, vali;
85696
85697 setFirstScatter(fullLayout, trace);
85698
85699 var xAttr = 'x';
85700 var yAttr = 'y';
85701 var posAttr;
85702 if(stackGroupOpts) {
85703 Lib.pushUnique(stackGroupOpts.traceIndices, trace._expandedIndex);
85704 isV = stackGroupOpts.orientation === 'v';
85705
85706 // size, like we use for bar
85707 if(isV) {
85708 yAttr = 's';
85709 posAttr = 'x';
85710 } else {
85711 xAttr = 's';
85712 posAttr = 'y';
85713 }
85714 interpolate = stackGroupOpts.stackgaps === 'interpolate';
85715 } else {
85716 var ppad = calcMarkerSize(trace, serieslen);
85717 calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
85718 }
85719
85720 for(i = 0; i < serieslen; i++) {
85721 var cdi = cd[i] = {};
85722 var xValid = isNumeric(x[i]);
85723 var yValid = isNumeric(y[i]);
85724 if(xValid && yValid) {
85725 cdi[xAttr] = x[i];
85726 cdi[yAttr] = y[i];
85727 } else if(stackGroupOpts && (isV ? xValid : yValid)) {
85728 // if we're stacking we need to hold on to all valid positions
85729 // even with invalid sizes
85730
85731 cdi[posAttr] = isV ? x[i] : y[i];
85732 cdi.gap = true;
85733 if(interpolate) {
85734 cdi.s = BADNUM;
85735 interpolateGaps = true;
85736 } else {
85737 cdi.s = 0;
85738 }
85739 } else {
85740 cdi[xAttr] = cdi[yAttr] = BADNUM;
85741 }
85742
85743 if(ids) {
85744 cdi.id = String(ids[i]);
85745 }
85746 }
85747
85748 arraysToCalcdata(cd, trace);
85749 calcColorscale(gd, trace);
85750 calcSelection(cd, trace);
85751
85752 if(stackGroupOpts) {
85753 // remove bad positions and sort
85754 // note that original indices get added to cd in arraysToCalcdata
85755 i = 0;
85756 while(i < cd.length) {
85757 if(cd[i][posAttr] === BADNUM) {
85758 cd.splice(i, 1);
85759 } else i++;
85760 }
85761
85762 Lib.sort(cd, function(a, b) {
85763 return (a[posAttr] - b[posAttr]) || (a.i - b.i);
85764 });
85765
85766 if(interpolateGaps) {
85767 // first fill the beginning with constant from the first point
85768 i = 0;
85769 while(i < cd.length - 1 && cd[i].gap) {
85770 i++;
85771 }
85772 vali = cd[i].s;
85773 if(!vali) vali = cd[i].s = 0; // in case of no data AT ALL in this trace - use 0
85774 for(j = 0; j < i; j++) {
85775 cd[j].s = vali;
85776 }
85777 // then fill the end with constant from the last point
85778 k = cd.length - 1;
85779 while(k > i && cd[k].gap) {
85780 k--;
85781 }
85782 vali = cd[k].s;
85783 for(j = cd.length - 1; j > k; j--) {
85784 cd[j].s = vali;
85785 }
85786 // now interpolate internal gaps linearly
85787 while(i < k) {
85788 i++;
85789 if(cd[i].gap) {
85790 j = i + 1;
85791 while(cd[j].gap) {
85792 j++;
85793 }
85794 var pos0 = cd[i - 1][posAttr];
85795 var size0 = cd[i - 1].s;
85796 var m = (cd[j].s - size0) / (cd[j][posAttr] - pos0);
85797 while(i < j) {
85798 cd[i].s = size0 + (cd[i][posAttr] - pos0) * m;
85799 i++;
85800 }
85801 }
85802 }
85803 }
85804 }
85805
85806 return cd;
85807}
85808
85809function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
85810 var serieslen = trace._length;
85811 var fullLayout = gd._fullLayout;
85812 var xId = xa._id;
85813 var yId = ya._id;
85814 var firstScatter = fullLayout._firstScatter[firstScatterGroup(trace)] === trace.uid;
85815 var stackOrientation = (getStackOpts(trace, fullLayout, xa, ya) || {}).orientation;
85816 var fill = trace.fill;
85817
85818 // cancel minimum tick spacings (only applies to bars and boxes)
85819 xa._minDtick = 0;
85820 ya._minDtick = 0;
85821
85822 // check whether bounds should be tight, padded, extended to zero...
85823 // most cases both should be padded on both ends, so start with that.
85824 var xOptions = {padded: true};
85825 var yOptions = {padded: true};
85826
85827 if(ppad) {
85828 xOptions.ppad = yOptions.ppad = ppad;
85829 }
85830
85831 // TODO: text size
85832
85833 var openEnded = serieslen < 2 || (x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]);
85834
85835 if(openEnded && (
85836 (fill === 'tozerox') ||
85837 ((fill === 'tonextx') && (firstScatter || stackOrientation === 'h'))
85838 )) {
85839 // include zero (tight) and extremes (padded) if fill to zero
85840 // (unless the shape is closed, then it's just filling the shape regardless)
85841
85842 xOptions.tozero = true;
85843 } else if(!(trace.error_y || {}).visible && (
85844 // if no error bars, markers or text, or fill to y=0 remove x padding
85845
85846 (fill === 'tonexty' || fill === 'tozeroy') ||
85847 (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace))
85848 )) {
85849 xOptions.padded = false;
85850 xOptions.ppad = 0;
85851 }
85852
85853 if(openEnded && (
85854 (fill === 'tozeroy') ||
85855 ((fill === 'tonexty') && (firstScatter || stackOrientation === 'v'))
85856 )) {
85857 // now check for y - rather different logic, though still mostly padded both ends
85858 // include zero (tight) and extremes (padded) if fill to zero
85859 // (unless the shape is closed, then it's just filling the shape regardless)
85860
85861 yOptions.tozero = true;
85862 } else if(fill === 'tonextx' || fill === 'tozerox') {
85863 // tight y: any x fill
85864
85865 yOptions.padded = false;
85866 }
85867
85868 // N.B. asymmetric splom traces call this with blank {} xa or ya
85869 if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions);
85870 if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions);
85871}
85872
85873function calcMarkerSize(trace, serieslen) {
85874 if(!subTypes.hasMarkers(trace)) return;
85875
85876 // Treat size like x or y arrays --- Run d2c
85877 // this needs to go before ppad computation
85878 var marker = trace.marker;
85879 var sizeref = 1.6 * (trace.marker.sizeref || 1);
85880 var markerTrans;
85881
85882 if(trace.marker.sizemode === 'area') {
85883 markerTrans = function(v) {
85884 return Math.max(Math.sqrt((v || 0) / sizeref), 3);
85885 };
85886 } else {
85887 markerTrans = function(v) {
85888 return Math.max((v || 0) / sizeref, 3);
85889 };
85890 }
85891
85892 if(Lib.isArrayOrTypedArray(marker.size)) {
85893 // I tried auto-type but category and dates dont make much sense.
85894 var ax = {type: 'linear'};
85895 Axes.setConvert(ax);
85896
85897 var s = ax.makeCalcdata(trace.marker, 'size');
85898
85899 var sizeOut = new Array(serieslen);
85900 for(var i = 0; i < serieslen; i++) {
85901 sizeOut[i] = markerTrans(s[i]);
85902 }
85903 return sizeOut;
85904 } else {
85905 return markerTrans(marker.size);
85906 }
85907}
85908
85909/**
85910 * mark the first scatter trace for each subplot
85911 * note that scatter and scattergl each get their own first trace
85912 * note also that I'm doing this during calc rather than supplyDefaults
85913 * so I don't need to worry about transforms, but if we ever do
85914 * per-trace calc this will get confused.
85915 */
85916function setFirstScatter(fullLayout, trace) {
85917 var group = firstScatterGroup(trace);
85918 var firstScatter = fullLayout._firstScatter;
85919 if(!firstScatter[group]) firstScatter[group] = trace.uid;
85920}
85921
85922function firstScatterGroup(trace) {
85923 var stackGroup = trace.stackgroup;
85924 return trace.xaxis + trace.yaxis + trace.type +
85925 (stackGroup ? '-' + stackGroup : '');
85926}
85927
85928function getStackOpts(trace, fullLayout, xa, ya) {
85929 var stackGroup = trace.stackgroup;
85930 if(!stackGroup) return;
85931 var stackOpts = fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup];
85932 var stackAx = stackOpts.orientation === 'v' ? ya : xa;
85933 // Allow stacking only on numeric axes
85934 // calc is a little late to be figuring this out, but during supplyDefaults
85935 // we don't know the axis type yet
85936 if(stackAx.type === 'linear' || stackAx.type === 'log') return stackOpts;
85937}
85938
85939module.exports = {
85940 calc: calc,
85941 calcMarkerSize: calcMarkerSize,
85942 calcAxisExpansion: calcAxisExpansion,
85943 setFirstScatter: setFirstScatter,
85944 getStackOpts: getStackOpts
85945};
85946
85947},{"../../constants/numerical":158,"../../lib":178,"../../plots/cartesian/axes":222,"./arrays_to_calcdata":388,"./calc_selection":391,"./colorscale_calc":392,"./subtypes":413,"fast-isnumeric":18}],391:[function(_dereq_,module,exports){
85948/**
85949* Copyright 2012-2020, Plotly, Inc.
85950* All rights reserved.
85951*
85952* This source code is licensed under the MIT license found in the
85953* LICENSE file in the root directory of this source tree.
85954*/
85955
85956'use strict';
85957
85958var Lib = _dereq_('../../lib');
85959
85960module.exports = function calcSelection(cd, trace) {
85961 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
85962 Lib.tagSelected(cd, trace);
85963 }
85964};
85965
85966},{"../../lib":178}],392:[function(_dereq_,module,exports){
85967/**
85968* Copyright 2012-2020, Plotly, Inc.
85969* All rights reserved.
85970*
85971* This source code is licensed under the MIT license found in the
85972* LICENSE file in the root directory of this source tree.
85973*/
85974
85975'use strict';
85976
85977var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
85978var calcColorscale = _dereq_('../../components/colorscale/calc');
85979
85980var subTypes = _dereq_('./subtypes');
85981
85982module.exports = function calcMarkerColorscale(gd, trace) {
85983 if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) {
85984 calcColorscale(gd, trace, {
85985 vals: trace.line.color,
85986 containerStr: 'line',
85987 cLetter: 'c'
85988 });
85989 }
85990
85991 if(subTypes.hasMarkers(trace)) {
85992 if(hasColorscale(trace, 'marker')) {
85993 calcColorscale(gd, trace, {
85994 vals: trace.marker.color,
85995 containerStr: 'marker',
85996 cLetter: 'c'
85997 });
85998 }
85999 if(hasColorscale(trace, 'marker.line')) {
86000 calcColorscale(gd, trace, {
86001 vals: trace.marker.line.color,
86002 containerStr: 'marker.line',
86003 cLetter: 'c'
86004 });
86005 }
86006 }
86007};
86008
86009},{"../../components/colorscale/calc":60,"../../components/colorscale/helpers":63,"./subtypes":413}],393:[function(_dereq_,module,exports){
86010/**
86011* Copyright 2012-2020, Plotly, Inc.
86012* All rights reserved.
86013*
86014* This source code is licensed under the MIT license found in the
86015* LICENSE file in the root directory of this source tree.
86016*/
86017
86018
86019'use strict';
86020
86021module.exports = {
86022 PTS_LINESONLY: 20,
86023
86024 // fixed parameters of clustering and clipping algorithms
86025
86026 // fraction of clustering tolerance "so close we don't even consider it a new point"
86027 minTolerance: 0.2,
86028 // how fast does clustering tolerance increase as you get away from the visible region
86029 toleranceGrowth: 10,
86030
86031 // number of viewport sizes away from the visible region
86032 // at which we clip all lines to the perimeter
86033 maxScreensAway: 20,
86034
86035 eventDataKeys: []
86036};
86037
86038},{}],394:[function(_dereq_,module,exports){
86039/**
86040* Copyright 2012-2020, Plotly, Inc.
86041* All rights reserved.
86042*
86043* This source code is licensed under the MIT license found in the
86044* LICENSE file in the root directory of this source tree.
86045*/
86046
86047
86048'use strict';
86049
86050var calc = _dereq_('./calc');
86051
86052/*
86053 * Scatter stacking & normalization calculations
86054 * runs per subplot, and can handle multiple stacking groups
86055 */
86056
86057module.exports = function crossTraceCalc(gd, plotinfo) {
86058 var xa = plotinfo.xaxis;
86059 var ya = plotinfo.yaxis;
86060 var subplot = xa._id + ya._id;
86061
86062 var subplotStackOpts = gd._fullLayout._scatterStackOpts[subplot];
86063 if(!subplotStackOpts) return;
86064
86065 var calcTraces = gd.calcdata;
86066
86067 var i, j, k, i2, cd, cd0, posj, sumj, norm;
86068 var groupOpts, interpolate, groupnorm, posAttr, valAttr;
86069 var hasAnyBlanks;
86070
86071 for(var stackGroup in subplotStackOpts) {
86072 groupOpts = subplotStackOpts[stackGroup];
86073 var indices = groupOpts.traceIndices;
86074
86075 // can get here with no indices if the stack axis is non-numeric
86076 if(!indices.length) continue;
86077
86078 interpolate = groupOpts.stackgaps === 'interpolate';
86079 groupnorm = groupOpts.groupnorm;
86080 if(groupOpts.orientation === 'v') {
86081 posAttr = 'x';
86082 valAttr = 'y';
86083 } else {
86084 posAttr = 'y';
86085 valAttr = 'x';
86086 }
86087 hasAnyBlanks = new Array(indices.length);
86088 for(i = 0; i < hasAnyBlanks.length; i++) {
86089 hasAnyBlanks[i] = false;
86090 }
86091
86092 // Collect the complete set of all positions across ALL traces.
86093 // Start with the first trace, then interleave items from later traces
86094 // as needed.
86095 // Fill in mising items as we go.
86096 cd0 = calcTraces[indices[0]];
86097 var allPositions = new Array(cd0.length);
86098 for(i = 0; i < cd0.length; i++) {
86099 allPositions[i] = cd0[i][posAttr];
86100 }
86101
86102 for(i = 1; i < indices.length; i++) {
86103 cd = calcTraces[indices[i]];
86104
86105 for(j = k = 0; j < cd.length; j++) {
86106 posj = cd[j][posAttr];
86107 for(; posj > allPositions[k] && k < allPositions.length; k++) {
86108 // the current trace is missing a position from some previous trace(s)
86109 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
86110 j++;
86111 }
86112 if(posj !== allPositions[k]) {
86113 // previous trace(s) are missing a position from the current trace
86114 for(i2 = 0; i2 < i; i2++) {
86115 insertBlank(calcTraces[indices[i2]], k, posj, i2, hasAnyBlanks, interpolate, posAttr);
86116 }
86117 allPositions.splice(k, 0, posj);
86118 }
86119 k++;
86120 }
86121 for(; k < allPositions.length; k++) {
86122 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
86123 j++;
86124 }
86125 }
86126
86127 var serieslen = allPositions.length;
86128
86129 // stack (and normalize)!
86130 for(j = 0; j < cd0.length; j++) {
86131 sumj = cd0[j][valAttr] = cd0[j].s;
86132 for(i = 1; i < indices.length; i++) {
86133 cd = calcTraces[indices[i]];
86134 cd[0].trace._rawLength = cd[0].trace._length;
86135 cd[0].trace._length = serieslen;
86136 sumj += cd[j].s;
86137 cd[j][valAttr] = sumj;
86138 }
86139
86140 if(groupnorm) {
86141 norm = ((groupnorm === 'fraction') ? sumj : (sumj / 100)) || 1;
86142 for(i = 0; i < indices.length; i++) {
86143 var cdj = calcTraces[indices[i]][j];
86144 cdj[valAttr] /= norm;
86145 cdj.sNorm = cdj.s / norm;
86146 }
86147 }
86148 }
86149
86150 // autorange
86151 for(i = 0; i < indices.length; i++) {
86152 cd = calcTraces[indices[i]];
86153 var trace = cd[0].trace;
86154 var ppad = calc.calcMarkerSize(trace, trace._rawLength);
86155 var arrayPad = Array.isArray(ppad);
86156 if((ppad && hasAnyBlanks[i]) || arrayPad) {
86157 var ppadRaw = ppad;
86158 ppad = new Array(serieslen);
86159 for(j = 0; j < serieslen; j++) {
86160 ppad[j] = cd[j].gap ? 0 : (arrayPad ? ppadRaw[cd[j].i] : ppadRaw);
86161 }
86162 }
86163 var x = new Array(serieslen);
86164 var y = new Array(serieslen);
86165 for(j = 0; j < serieslen; j++) {
86166 x[j] = cd[j].x;
86167 y[j] = cd[j].y;
86168 }
86169 calc.calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
86170
86171 // while we're here (in a loop over all traces in the stack)
86172 // record the orientation, so hover can find it easily
86173 cd[0].t.orientation = groupOpts.orientation;
86174 }
86175 }
86176};
86177
86178function insertBlank(calcTrace, index, position, traceIndex, hasAnyBlanks, interpolate, posAttr) {
86179 hasAnyBlanks[traceIndex] = true;
86180 var newEntry = {
86181 i: null,
86182 gap: true,
86183 s: 0
86184 };
86185 newEntry[posAttr] = position;
86186 calcTrace.splice(index, 0, newEntry);
86187 // Even if we're not interpolating, if one trace has multiple
86188 // values at the same position and this trace only has one value there,
86189 // we just duplicate that one value rather than insert a zero.
86190 // We also make it look like a real point - because it's ambiguous which
86191 // one really is the real one!
86192 if(index && position === calcTrace[index - 1][posAttr]) {
86193 var prevEntry = calcTrace[index - 1];
86194 newEntry.s = prevEntry.s;
86195 // TODO is it going to cause any problems to have multiple
86196 // calcdata points with the same index?
86197 newEntry.i = prevEntry.i;
86198 newEntry.gap = prevEntry.gap;
86199 } else if(interpolate) {
86200 newEntry.s = getInterp(calcTrace, index, position, posAttr);
86201 }
86202 if(!index) {
86203 // t and trace need to stay on the first cd entry
86204 calcTrace[0].t = calcTrace[1].t;
86205 calcTrace[0].trace = calcTrace[1].trace;
86206 delete calcTrace[1].t;
86207 delete calcTrace[1].trace;
86208 }
86209}
86210
86211function getInterp(calcTrace, index, position, posAttr) {
86212 var pt0 = calcTrace[index - 1];
86213 var pt1 = calcTrace[index + 1];
86214 if(!pt1) return pt0.s;
86215 if(!pt0) return pt1.s;
86216 return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]);
86217}
86218
86219},{"./calc":390}],395:[function(_dereq_,module,exports){
86220/**
86221* Copyright 2012-2020, Plotly, Inc.
86222* All rights reserved.
86223*
86224* This source code is licensed under the MIT license found in the
86225* LICENSE file in the root directory of this source tree.
86226*/
86227
86228
86229'use strict';
86230
86231
86232// remove opacity for any trace that has a fill or is filled to
86233module.exports = function crossTraceDefaults(fullData) {
86234 for(var i = 0; i < fullData.length; i++) {
86235 var tracei = fullData[i];
86236 if(tracei.type !== 'scatter') continue;
86237
86238 var filli = tracei.fill;
86239 if(filli === 'none' || filli === 'toself') continue;
86240
86241 tracei.opacity = undefined;
86242
86243 if(filli === 'tonexty' || filli === 'tonextx') {
86244 for(var j = i - 1; j >= 0; j--) {
86245 var tracej = fullData[j];
86246
86247 if((tracej.type === 'scatter') &&
86248 (tracej.xaxis === tracei.xaxis) &&
86249 (tracej.yaxis === tracei.yaxis)) {
86250 tracej.opacity = undefined;
86251 break;
86252 }
86253 }
86254 }
86255 }
86256};
86257
86258},{}],396:[function(_dereq_,module,exports){
86259/**
86260* Copyright 2012-2020, Plotly, Inc.
86261* All rights reserved.
86262*
86263* This source code is licensed under the MIT license found in the
86264* LICENSE file in the root directory of this source tree.
86265*/
86266
86267'use strict';
86268
86269var Lib = _dereq_('../../lib');
86270var Registry = _dereq_('../../registry');
86271
86272var attributes = _dereq_('./attributes');
86273var constants = _dereq_('./constants');
86274var subTypes = _dereq_('./subtypes');
86275var handleXYDefaults = _dereq_('./xy_defaults');
86276var handleStackDefaults = _dereq_('./stack_defaults');
86277var handleMarkerDefaults = _dereq_('./marker_defaults');
86278var handleLineDefaults = _dereq_('./line_defaults');
86279var handleLineShapeDefaults = _dereq_('./line_shape_defaults');
86280var handleTextDefaults = _dereq_('./text_defaults');
86281var handleFillColorDefaults = _dereq_('./fillcolor_defaults');
86282
86283module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
86284 function coerce(attr, dflt) {
86285 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
86286 }
86287
86288 var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
86289 if(!len) traceOut.visible = false;
86290
86291 if(!traceOut.visible) return;
86292
86293 var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
86294
86295 var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
86296 'lines+markers' : 'lines';
86297 coerce('text');
86298 coerce('hovertext');
86299 coerce('mode', defaultMode);
86300
86301 if(subTypes.hasLines(traceOut)) {
86302 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
86303 handleLineShapeDefaults(traceIn, traceOut, coerce);
86304 coerce('connectgaps');
86305 coerce('line.simplify');
86306 }
86307
86308 if(subTypes.hasMarkers(traceOut)) {
86309 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
86310 }
86311
86312 if(subTypes.hasText(traceOut)) {
86313 coerce('texttemplate');
86314 handleTextDefaults(traceIn, traceOut, layout, coerce);
86315 }
86316
86317 var dfltHoverOn = [];
86318
86319 if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
86320 coerce('cliponaxis');
86321 coerce('marker.maxdisplayed');
86322 dfltHoverOn.push('points');
86323 }
86324
86325 // It's possible for this default to be changed by a later trace.
86326 // We handle that case in some hacky code inside handleStackDefaults.
86327 coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none');
86328 if(traceOut.fill !== 'none') {
86329 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
86330 if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
86331 }
86332
86333 var lineColor = (traceOut.line || {}).color;
86334 var markerColor = (traceOut.marker || {}).color;
86335
86336 if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
86337 dfltHoverOn.push('fills');
86338 }
86339 coerce('hoveron', dfltHoverOn.join('+') || 'points');
86340 if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
86341 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
86342 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'});
86343 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'});
86344
86345 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
86346};
86347
86348},{"../../lib":178,"../../registry":269,"./attributes":389,"./constants":393,"./fillcolor_defaults":397,"./line_defaults":402,"./line_shape_defaults":404,"./marker_defaults":408,"./stack_defaults":411,"./subtypes":413,"./text_defaults":414,"./xy_defaults":415}],397:[function(_dereq_,module,exports){
86349/**
86350* Copyright 2012-2020, Plotly, Inc.
86351* All rights reserved.
86352*
86353* This source code is licensed under the MIT license found in the
86354* LICENSE file in the root directory of this source tree.
86355*/
86356
86357
86358'use strict';
86359
86360var Color = _dereq_('../../components/color');
86361var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
86362
86363module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) {
86364 var inheritColorFromMarker = false;
86365
86366 if(traceOut.marker) {
86367 // don't try to inherit a color array
86368 var markerColor = traceOut.marker.color;
86369 var markerLineColor = (traceOut.marker.line || {}).color;
86370
86371 if(markerColor && !isArrayOrTypedArray(markerColor)) {
86372 inheritColorFromMarker = markerColor;
86373 } else if(markerLineColor && !isArrayOrTypedArray(markerLineColor)) {
86374 inheritColorFromMarker = markerLineColor;
86375 }
86376 }
86377
86378 coerce('fillcolor', Color.addOpacity(
86379 (traceOut.line || {}).color ||
86380 inheritColorFromMarker ||
86381 defaultColor, 0.5
86382 ));
86383};
86384
86385},{"../../components/color":52,"../../lib":178}],398:[function(_dereq_,module,exports){
86386/**
86387* Copyright 2012-2020, Plotly, Inc.
86388* All rights reserved.
86389*
86390* This source code is licensed under the MIT license found in the
86391* LICENSE file in the root directory of this source tree.
86392*/
86393
86394'use strict';
86395
86396var Axes = _dereq_('../../plots/cartesian/axes');
86397
86398module.exports = function formatLabels(cdi, trace, fullLayout) {
86399 var labels = {};
86400
86401 var mockGd = {_fullLayout: fullLayout};
86402 var xa = Axes.getFromTrace(mockGd, trace, 'x');
86403 var ya = Axes.getFromTrace(mockGd, trace, 'y');
86404
86405 labels.xLabel = Axes.tickText(xa, cdi.x, true).text;
86406 labels.yLabel = Axes.tickText(ya, cdi.y, true).text;
86407
86408 return labels;
86409};
86410
86411},{"../../plots/cartesian/axes":222}],399:[function(_dereq_,module,exports){
86412/**
86413* Copyright 2012-2020, Plotly, Inc.
86414* All rights reserved.
86415*
86416* This source code is licensed under the MIT license found in the
86417* LICENSE file in the root directory of this source tree.
86418*/
86419
86420
86421'use strict';
86422
86423var Color = _dereq_('../../components/color');
86424var subtypes = _dereq_('./subtypes');
86425
86426
86427module.exports = function getTraceColor(trace, di) {
86428 var lc, tc;
86429
86430 // TODO: text modes
86431
86432 if(trace.mode === 'lines') {
86433 lc = trace.line.color;
86434 return (lc && Color.opacity(lc)) ?
86435 lc : trace.fillcolor;
86436 } else if(trace.mode === 'none') {
86437 return trace.fill ? trace.fillcolor : '';
86438 } else {
86439 var mc = di.mcc || (trace.marker || {}).color;
86440 var mlc = di.mlcc || ((trace.marker || {}).line || {}).color;
86441
86442 tc = (mc && Color.opacity(mc)) ? mc :
86443 (mlc && Color.opacity(mlc) &&
86444 (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : '';
86445
86446 if(tc) {
86447 // make sure the points aren't TOO transparent
86448 if(Color.opacity(tc) < 0.3) {
86449 return Color.addOpacity(tc, 0.3);
86450 } else return tc;
86451 } else {
86452 lc = (trace.line || {}).color;
86453 return (lc && Color.opacity(lc) &&
86454 subtypes.hasLines(trace) && trace.line.width) ?
86455 lc : trace.fillcolor;
86456 }
86457 }
86458};
86459
86460},{"../../components/color":52,"./subtypes":413}],400:[function(_dereq_,module,exports){
86461/**
86462* Copyright 2012-2020, Plotly, Inc.
86463* All rights reserved.
86464*
86465* This source code is licensed under the MIT license found in the
86466* LICENSE file in the root directory of this source tree.
86467*/
86468
86469'use strict';
86470
86471var Lib = _dereq_('../../lib');
86472var Fx = _dereq_('../../components/fx');
86473var Registry = _dereq_('../../registry');
86474var getTraceColor = _dereq_('./get_trace_color');
86475var Color = _dereq_('../../components/color');
86476var fillText = Lib.fillText;
86477
86478module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
86479 var cd = pointData.cd;
86480 var trace = cd[0].trace;
86481 var xa = pointData.xa;
86482 var ya = pointData.ya;
86483 var xpx = xa.c2p(xval);
86484 var ypx = ya.c2p(yval);
86485 var pt = [xpx, ypx];
86486 var hoveron = trace.hoveron || '';
86487 var minRad = (trace.mode.indexOf('markers') !== -1) ? 3 : 0.5;
86488
86489 // look for points to hover on first, then take fills only if we
86490 // didn't find a point
86491 if(hoveron.indexOf('points') !== -1) {
86492 var dx = function(di) {
86493 // dx and dy are used in compare modes - here we want to always
86494 // prioritize the closest data point, at least as long as markers are
86495 // the same size or nonexistent, but still try to prioritize small markers too.
86496 var rad = Math.max(3, di.mrc || 0);
86497 var kink = 1 - 1 / rad;
86498 var dxRaw = Math.abs(xa.c2p(di.x) - xpx);
86499 var d = (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
86500 return d;
86501 };
86502 var dy = function(di) {
86503 var rad = Math.max(3, di.mrc || 0);
86504 var kink = 1 - 1 / rad;
86505 var dyRaw = Math.abs(ya.c2p(di.y) - ypx);
86506 return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink);
86507 };
86508 var dxy = function(di) {
86509 // scatter points: d.mrc is the calculated marker radius
86510 // adjust the distance so if you're inside the marker it
86511 // always will show up regardless of point size, but
86512 // prioritize smaller points
86513 var rad = Math.max(minRad, di.mrc || 0);
86514 var dx = xa.c2p(di.x) - xpx;
86515 var dy = ya.c2p(di.y) - ypx;
86516 return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad);
86517 };
86518 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
86519
86520 Fx.getClosest(cd, distfn, pointData);
86521
86522 // skip the rest (for this trace) if we didn't find a close point
86523 if(pointData.index !== false) {
86524 // the closest data point
86525 var di = cd[pointData.index];
86526 var xc = xa.c2p(di.x, true);
86527 var yc = ya.c2p(di.y, true);
86528 var rad = di.mrc || 1;
86529
86530 // now we're done using the whole `calcdata` array, replace the
86531 // index with the original index (in case of inserted point from
86532 // stacked area)
86533 pointData.index = di.i;
86534
86535 var orientation = cd[0].t.orientation;
86536 // TODO: for scatter and bar, option to show (sub)totals and
86537 // raw data? Currently stacked and/or normalized bars just show
86538 // the normalized individual sizes, so that's what I'm doing here
86539 // for now.
86540 var sizeVal = orientation && (di.sNorm || di.s);
86541 var xLabelVal = (orientation === 'h') ? sizeVal : di.x;
86542 var yLabelVal = (orientation === 'v') ? sizeVal : di.y;
86543
86544 Lib.extendFlat(pointData, {
86545 color: getTraceColor(trace, di),
86546
86547 x0: xc - rad,
86548 x1: xc + rad,
86549 xLabelVal: xLabelVal,
86550
86551 y0: yc - rad,
86552 y1: yc + rad,
86553 yLabelVal: yLabelVal,
86554
86555 spikeDistance: dxy(di),
86556 hovertemplate: trace.hovertemplate
86557 });
86558
86559 fillText(di, trace, pointData);
86560 Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
86561
86562 return [pointData];
86563 }
86564 }
86565
86566 // even if hoveron is 'fills', only use it if we have polygons too
86567 if(hoveron.indexOf('fills') !== -1 && trace._polygons) {
86568 var polygons = trace._polygons;
86569 var polygonsIn = [];
86570 var inside = false;
86571 var xmin = Infinity;
86572 var xmax = -Infinity;
86573 var ymin = Infinity;
86574 var ymax = -Infinity;
86575
86576 var i, j, polygon, pts, xCross, x0, x1, y0, y1;
86577
86578 for(i = 0; i < polygons.length; i++) {
86579 polygon = polygons[i];
86580 // TODO: this is not going to work right for curved edges, it will
86581 // act as though they're straight. That's probably going to need
86582 // the elements themselves to capture the events. Worth it?
86583 if(polygon.contains(pt)) {
86584 inside = !inside;
86585 // TODO: need better than just the overall bounding box
86586 polygonsIn.push(polygon);
86587 ymin = Math.min(ymin, polygon.ymin);
86588 ymax = Math.max(ymax, polygon.ymax);
86589 }
86590 }
86591
86592 if(inside) {
86593 // constrain ymin/max to the visible plot, so the label goes
86594 // at the middle of the piece you can see
86595 ymin = Math.max(ymin, 0);
86596 ymax = Math.min(ymax, ya._length);
86597
86598 // find the overall left-most and right-most points of the
86599 // polygon(s) we're inside at their combined vertical midpoint.
86600 // This is where we will draw the hover label.
86601 // Note that this might not be the vertical midpoint of the
86602 // whole trace, if it's disjoint.
86603 var yAvg = (ymin + ymax) / 2;
86604 for(i = 0; i < polygonsIn.length; i++) {
86605 pts = polygonsIn[i].pts;
86606 for(j = 1; j < pts.length; j++) {
86607 y0 = pts[j - 1][1];
86608 y1 = pts[j][1];
86609 if((y0 > yAvg) !== (y1 >= yAvg)) {
86610 x0 = pts[j - 1][0];
86611 x1 = pts[j][0];
86612 if(y1 - y0) {
86613 xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0);
86614 xmin = Math.min(xmin, xCross);
86615 xmax = Math.max(xmax, xCross);
86616 }
86617 }
86618 }
86619 }
86620
86621 // constrain xmin/max to the visible plot now too
86622 xmin = Math.max(xmin, 0);
86623 xmax = Math.min(xmax, xa._length);
86624
86625 // get only fill or line color for the hover color
86626 var color = Color.defaultLine;
86627 if(Color.opacity(trace.fillcolor)) color = trace.fillcolor;
86628 else if(Color.opacity((trace.line || {}).color)) {
86629 color = trace.line.color;
86630 }
86631
86632 Lib.extendFlat(pointData, {
86633 // never let a 2D override 1D type as closest point
86634 // also: no spikeDistance, it's not allowed for fills
86635 distance: pointData.maxHoverDistance,
86636 x0: xmin,
86637 x1: xmax,
86638 y0: yAvg,
86639 y1: yAvg,
86640 color: color,
86641 hovertemplate: false
86642 });
86643
86644 delete pointData.index;
86645
86646 if(trace.text && !Array.isArray(trace.text)) {
86647 pointData.text = String(trace.text);
86648 } else pointData.text = trace.name;
86649
86650 return [pointData];
86651 }
86652 }
86653};
86654
86655},{"../../components/color":52,"../../components/fx":92,"../../lib":178,"../../registry":269,"./get_trace_color":399}],401:[function(_dereq_,module,exports){
86656/**
86657* Copyright 2012-2020, Plotly, Inc.
86658* All rights reserved.
86659*
86660* This source code is licensed under the MIT license found in the
86661* LICENSE file in the root directory of this source tree.
86662*/
86663
86664'use strict';
86665
86666var subtypes = _dereq_('./subtypes');
86667
86668module.exports = {
86669 hasLines: subtypes.hasLines,
86670 hasMarkers: subtypes.hasMarkers,
86671 hasText: subtypes.hasText,
86672 isBubble: subtypes.isBubble,
86673
86674 attributes: _dereq_('./attributes'),
86675 supplyDefaults: _dereq_('./defaults'),
86676 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
86677 calc: _dereq_('./calc').calc,
86678 crossTraceCalc: _dereq_('./cross_trace_calc'),
86679 arraysToCalcdata: _dereq_('./arrays_to_calcdata'),
86680 plot: _dereq_('./plot'),
86681 colorbar: _dereq_('./marker_colorbar'),
86682 formatLabels: _dereq_('./format_labels'),
86683 style: _dereq_('./style').style,
86684 styleOnSelect: _dereq_('./style').styleOnSelect,
86685 hoverPoints: _dereq_('./hover'),
86686 selectPoints: _dereq_('./select'),
86687 animatable: true,
86688
86689 moduleType: 'trace',
86690 name: 'scatter',
86691 basePlotModule: _dereq_('../../plots/cartesian'),
86692 categories: [
86693 'cartesian', 'svg', 'symbols', 'errorBarsOK', 'showLegend', 'scatter-like',
86694 'zoomScale'
86695 ],
86696 meta: {
86697
86698 }
86699};
86700
86701},{"../../plots/cartesian":235,"./arrays_to_calcdata":388,"./attributes":389,"./calc":390,"./cross_trace_calc":394,"./cross_trace_defaults":395,"./defaults":396,"./format_labels":398,"./hover":400,"./marker_colorbar":407,"./plot":409,"./select":410,"./style":412,"./subtypes":413}],402:[function(_dereq_,module,exports){
86702/**
86703* Copyright 2012-2020, Plotly, Inc.
86704* All rights reserved.
86705*
86706* This source code is licensed under the MIT license found in the
86707* LICENSE file in the root directory of this source tree.
86708*/
86709
86710'use strict';
86711
86712var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
86713var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
86714var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
86715
86716module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
86717 var markerColor = (traceIn.marker || {}).color;
86718
86719 coerce('line.color', defaultColor);
86720
86721 if(hasColorscale(traceIn, 'line')) {
86722 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'line.', cLetter: 'c'});
86723 } else {
86724 var lineColorDflt = (isArrayOrTypedArray(markerColor) ? false : markerColor) || defaultColor;
86725 coerce('line.color', lineColorDflt);
86726 }
86727
86728 coerce('line.width');
86729 if(!(opts || {}).noDash) coerce('line.dash');
86730};
86731
86732},{"../../components/colorscale/defaults":62,"../../components/colorscale/helpers":63,"../../lib":178}],403:[function(_dereq_,module,exports){
86733/**
86734* Copyright 2012-2020, Plotly, Inc.
86735* All rights reserved.
86736*
86737* This source code is licensed under the MIT license found in the
86738* LICENSE file in the root directory of this source tree.
86739*/
86740
86741
86742'use strict';
86743
86744var numConstants = _dereq_('../../constants/numerical');
86745var BADNUM = numConstants.BADNUM;
86746var LOG_CLIP = numConstants.LOG_CLIP;
86747var LOG_CLIP_PLUS = LOG_CLIP + 0.5;
86748var LOG_CLIP_MINUS = LOG_CLIP - 0.5;
86749var Lib = _dereq_('../../lib');
86750var segmentsIntersect = Lib.segmentsIntersect;
86751var constrain = Lib.constrain;
86752var constants = _dereq_('./constants');
86753
86754
86755module.exports = function linePoints(d, opts) {
86756 var xa = opts.xaxis;
86757 var ya = opts.yaxis;
86758 var xLog = xa.type === 'log';
86759 var yLog = ya.type === 'log';
86760 var xLen = xa._length;
86761 var yLen = ya._length;
86762 var connectGaps = opts.connectGaps;
86763 var baseTolerance = opts.baseTolerance;
86764 var shape = opts.shape;
86765 var linear = shape === 'linear';
86766 var fill = opts.fill && opts.fill !== 'none';
86767 var segments = [];
86768 var minTolerance = constants.minTolerance;
86769 var len = d.length;
86770 var pts = new Array(len);
86771 var pti = 0;
86772
86773 var i;
86774
86775 // pt variables are pixel coordinates [x,y] of one point
86776 // these four are the outputs of clustering on a line
86777 var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt;
86778
86779 // "this" is the next point we're considering adding to the cluster
86780 var thisPt;
86781
86782 // did we encounter the high point first, then a low point, or vice versa?
86783 var clusterHighFirst;
86784
86785 // the first two points in the cluster determine its unit vector
86786 // so the second is always in the "High" direction
86787 var clusterUnitVector;
86788
86789 // the pixel delta from clusterStartPt
86790 var thisVector;
86791
86792 // val variables are (signed) pixel distances along the cluster vector
86793 var clusterRefDist, clusterHighVal, clusterLowVal, thisVal;
86794
86795 // deviation variables are (signed) pixel distances normal to the cluster vector
86796 var clusterMinDeviation, clusterMaxDeviation, thisDeviation;
86797
86798 // turn one calcdata point into pixel coordinates
86799 function getPt(index) {
86800 var di = d[index];
86801 if(!di) return false;
86802 var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x);
86803 var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y);
86804
86805 // if non-positive log values, set them VERY far off-screen
86806 // so the line looks essentially straight from the previous point.
86807 if(x === BADNUM) {
86808 if(xLog) x = xa.c2p(di.x, true);
86809 if(x === BADNUM) return false;
86810 // If BOTH were bad log values, make the line follow a constant
86811 // exponent rather than a constant slope
86812 if(yLog && y === BADNUM) {
86813 x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) /
86814 (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS)));
86815 }
86816 x *= 1000;
86817 }
86818 if(y === BADNUM) {
86819 if(yLog) y = ya.c2p(di.y, true);
86820 if(y === BADNUM) return false;
86821 y *= 1000;
86822 }
86823 return [x, y];
86824 }
86825
86826 function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) {
86827 var dx = xFrac1 - xFrac0;
86828 var dy = yFrac1 - yFrac0;
86829 var dx0 = 0.5 - xFrac0;
86830 var dy0 = 0.5 - yFrac0;
86831 var norm2 = dx * dx + dy * dy;
86832 var dot = dx * dx0 + dy * dy0;
86833 if(dot > 0 && dot < norm2) {
86834 var cross = dx0 * dy - dy0 * dx;
86835 if(cross * cross < norm2) return true;
86836 }
86837 }
86838
86839 var latestXFrac, latestYFrac;
86840 // if we're off-screen, increase tolerance over baseTolerance
86841 function getTolerance(pt, nextPt) {
86842 var xFrac = pt[0] / xLen;
86843 var yFrac = pt[1] / yLen;
86844 var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1);
86845 if(offScreenFraction && (latestXFrac !== undefined) &&
86846 crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac)
86847 ) {
86848 offScreenFraction = 0;
86849 }
86850 if(offScreenFraction && nextPt &&
86851 crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen)
86852 ) {
86853 offScreenFraction = 0;
86854 }
86855
86856 return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance;
86857 }
86858
86859 function ptDist(pt1, pt2) {
86860 var dx = pt1[0] - pt2[0];
86861 var dy = pt1[1] - pt2[1];
86862 return Math.sqrt(dx * dx + dy * dy);
86863 }
86864
86865 // last bit of filtering: clip paths that are VERY far off-screen
86866 // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF)
86867
86868 var maxScreensAway = constants.maxScreensAway;
86869
86870 // find the intersections between the segment from pt1 to pt2
86871 // and the large rectangle maxScreensAway around the viewport
86872 // if one of pt1 and pt2 is inside and the other outside, there
86873 // will be only one intersection.
86874 // if both are outside there will be 0 or 2 intersections
86875 // (or 1 if it's right at a corner - we'll treat that like 0)
86876 // returns an array of intersection pts
86877 var xEdge0 = -xLen * maxScreensAway;
86878 var xEdge1 = xLen * (1 + maxScreensAway);
86879 var yEdge0 = -yLen * maxScreensAway;
86880 var yEdge1 = yLen * (1 + maxScreensAway);
86881 var edges = [
86882 [xEdge0, yEdge0, xEdge1, yEdge0],
86883 [xEdge1, yEdge0, xEdge1, yEdge1],
86884 [xEdge1, yEdge1, xEdge0, yEdge1],
86885 [xEdge0, yEdge1, xEdge0, yEdge0]
86886 ];
86887 var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt;
86888
86889 // for linear line shape, edge intersections should be linearly interpolated
86890 // spline uses this too, which isn't precisely correct but is actually pretty
86891 // good, because Catmull-Rom weights far-away points less in creating the curvature
86892 function getLinearEdgeIntersections(pt1, pt2) {
86893 var out = [];
86894 var ptCount = 0;
86895 for(var i = 0; i < 4; i++) {
86896 var edge = edges[i];
86897 var ptInt = segmentsIntersect(
86898 pt1[0], pt1[1], pt2[0], pt2[1],
86899 edge[0], edge[1], edge[2], edge[3]
86900 );
86901 if(ptInt && (!ptCount ||
86902 Math.abs(ptInt.x - out[0][0]) > 1 ||
86903 Math.abs(ptInt.y - out[0][1]) > 1
86904 )) {
86905 ptInt = [ptInt.x, ptInt.y];
86906 // if we have 2 intersections, make sure the closest one to pt1 comes first
86907 if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt);
86908 else out.push(ptInt);
86909 ptCount++;
86910 }
86911 }
86912 return out;
86913 }
86914
86915 function onlyConstrainedPoint(pt) {
86916 if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) {
86917 return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)];
86918 }
86919 }
86920
86921 function sameEdge(pt1, pt2) {
86922 if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true;
86923 if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true;
86924 }
86925
86926 // for line shapes hv and vh, movement in the two dimensions is decoupled,
86927 // so all we need to do is constrain each dimension independently
86928 function getHVEdgeIntersections(pt1, pt2) {
86929 var out = [];
86930 var ptInt1 = onlyConstrainedPoint(pt1);
86931 var ptInt2 = onlyConstrainedPoint(pt2);
86932 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
86933
86934 if(ptInt1) out.push(ptInt1);
86935 if(ptInt2) out.push(ptInt2);
86936 return out;
86937 }
86938
86939 // hvh and vhv we sometimes have to move one of the intersection points
86940 // out BEYOND the clipping rect, by a maximum of a factor of 2, so that
86941 // the midpoint line is drawn in the right place
86942 function getABAEdgeIntersections(dim, limit0, limit1) {
86943 return function(pt1, pt2) {
86944 var ptInt1 = onlyConstrainedPoint(pt1);
86945 var ptInt2 = onlyConstrainedPoint(pt2);
86946
86947 var out = [];
86948 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
86949
86950 if(ptInt1) out.push(ptInt1);
86951 if(ptInt2) out.push(ptInt2);
86952
86953 var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) -
86954 ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]);
86955 if(midShift) {
86956 var ptToAlter;
86957 if(ptInt1 && ptInt2) {
86958 ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2;
86959 } else ptToAlter = ptInt1 || ptInt2;
86960
86961 ptToAlter[dim] += midShift;
86962 }
86963
86964 return out;
86965 };
86966 }
86967
86968 var getEdgeIntersections;
86969 if(shape === 'linear' || shape === 'spline') {
86970 getEdgeIntersections = getLinearEdgeIntersections;
86971 } else if(shape === 'hv' || shape === 'vh') {
86972 getEdgeIntersections = getHVEdgeIntersections;
86973 } else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1);
86974 else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1);
86975
86976 // a segment pt1->pt2 entirely outside the nearby region:
86977 // find the corner it gets closest to touching
86978 function getClosestCorner(pt1, pt2) {
86979 var dx = pt2[0] - pt1[0];
86980 var m = (pt2[1] - pt1[1]) / dx;
86981 var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx;
86982
86983 if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1];
86984 else return [m > 0 ? xEdge1 : xEdge0, yEdge0];
86985 }
86986
86987 function updateEdge(pt) {
86988 var x = pt[0];
86989 var y = pt[1];
86990 var xSame = x === pts[pti - 1][0];
86991 var ySame = y === pts[pti - 1][1];
86992 // duplicate point?
86993 if(xSame && ySame) return;
86994 if(pti > 1) {
86995 // backtracking along an edge?
86996 var xSame2 = x === pts[pti - 2][0];
86997 var ySame2 = y === pts[pti - 2][1];
86998 if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) {
86999 if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add
87000 else pts[pti - 1] = pt; // not exact: replace the prev pt
87001 } else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) {
87002 if(xSame2) pti--;
87003 else pts[pti - 1] = pt;
87004 } else pts[pti++] = pt;
87005 } else pts[pti++] = pt;
87006 }
87007
87008 function updateEdgesForReentry(pt) {
87009 // if we're outside the nearby region and going back in,
87010 // we may need to loop around a corner point
87011 if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) {
87012 updateEdge([lastXEdge, lastYEdge]);
87013 }
87014 updateEdge(pt);
87015 lastFarPt = null;
87016 lastXEdge = lastYEdge = 0;
87017 }
87018
87019 function addPt(pt) {
87020 latestXFrac = pt[0] / xLen;
87021 latestYFrac = pt[1] / yLen;
87022 // Are we more than maxScreensAway off-screen any direction?
87023 // if so, clip to this box, but in such a way that on-screen
87024 // drawing is unchanged
87025 xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0;
87026 yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0;
87027 if(xEdge || yEdge) {
87028 if(!pti) {
87029 // to get fills right - if first point is far, push it toward the
87030 // screen in whichever direction(s) are far
87031
87032 pts[pti++] = [xEdge || pt[0], yEdge || pt[1]];
87033 } else if(lastFarPt) {
87034 // both this point and the last are outside the nearby region
87035 // check if we're crossing the nearby region
87036 var intersections = getEdgeIntersections(lastFarPt, pt);
87037 if(intersections.length > 1) {
87038 updateEdgesForReentry(intersections[0]);
87039 pts[pti++] = intersections[1];
87040 }
87041 } else {
87042 // we're leaving the nearby region - add the point where we left it
87043
87044 edgePt = getEdgeIntersections(pts[pti - 1], pt)[0];
87045 pts[pti++] = edgePt;
87046 }
87047
87048 var lastPt = pts[pti - 1];
87049 if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) {
87050 // we've gone out beyond a new corner: add the corner too
87051 // so that the next point will take the right winding
87052 if(lastFarPt) {
87053 if(lastXEdge !== xEdge && lastYEdge !== yEdge) {
87054 if(lastXEdge && lastYEdge) {
87055 // we've gone around to an opposite corner - we
87056 // need to add the correct extra corner
87057 // in order to get the right winding
87058 updateEdge(getClosestCorner(lastFarPt, pt));
87059 } else {
87060 // we're coming from a far edge - the extra corner
87061 // we need is determined uniquely by the sectors
87062 updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]);
87063 }
87064 } else if(lastXEdge && lastYEdge) {
87065 updateEdge([lastXEdge, lastYEdge]);
87066 }
87067 }
87068 updateEdge([xEdge, yEdge]);
87069 } else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) {
87070 // we're coming from an edge or far corner to an edge - again the
87071 // extra corner we need is uniquely determined by the sectors
87072 updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]);
87073 }
87074 lastFarPt = pt;
87075 lastXEdge = xEdge;
87076 lastYEdge = yEdge;
87077 } else {
87078 if(lastFarPt) {
87079 // this point is in range but the previous wasn't: add its entry pt first
87080 updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]);
87081 }
87082
87083 pts[pti++] = pt;
87084 }
87085 }
87086
87087 // loop over ALL points in this trace
87088 for(i = 0; i < len; i++) {
87089 clusterStartPt = getPt(i);
87090 if(!clusterStartPt) continue;
87091
87092 pti = 0;
87093 lastFarPt = null;
87094 addPt(clusterStartPt);
87095
87096 // loop over one segment of the trace
87097 for(i++; i < len; i++) {
87098 clusterHighPt = getPt(i);
87099 if(!clusterHighPt) {
87100 if(connectGaps) continue;
87101 else break;
87102 }
87103
87104 // can't decimate if nonlinear line shape
87105 // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again
87106 // but spline would be verrry awkward to decimate
87107 if(!linear || !opts.simplify) {
87108 addPt(clusterHighPt);
87109 continue;
87110 }
87111
87112 var nextPt = getPt(i + 1);
87113
87114 clusterRefDist = ptDist(clusterHighPt, clusterStartPt);
87115
87116 // #3147 - always include the very first and last points for fills
87117 if(!(fill && (pti === 0 || pti === len - 1)) &&
87118 clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue;
87119
87120 clusterUnitVector = [
87121 (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist,
87122 (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist
87123 ];
87124
87125 clusterLowPt = clusterStartPt;
87126 clusterHighVal = clusterRefDist;
87127 clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0;
87128 clusterHighFirst = false;
87129 clusterEndPt = clusterHighPt;
87130
87131 // loop over one cluster of points that collapse onto one line
87132 for(i++; i < d.length; i++) {
87133 thisPt = nextPt;
87134 nextPt = getPt(i + 1);
87135 if(!thisPt) {
87136 if(connectGaps) continue;
87137 else break;
87138 }
87139 thisVector = [
87140 thisPt[0] - clusterStartPt[0],
87141 thisPt[1] - clusterStartPt[1]
87142 ];
87143 // cross product (or dot with normal to the cluster vector)
87144 thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0];
87145 clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation);
87146 clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation);
87147
87148 if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break;
87149
87150 clusterEndPt = thisPt;
87151 thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1];
87152
87153 if(thisVal > clusterHighVal) {
87154 clusterHighVal = thisVal;
87155 clusterHighPt = thisPt;
87156 clusterHighFirst = false;
87157 } else if(thisVal < clusterLowVal) {
87158 clusterLowVal = thisVal;
87159 clusterLowPt = thisPt;
87160 clusterHighFirst = true;
87161 }
87162 }
87163
87164 // insert this cluster into pts
87165 // we've already inserted the start pt, now check if we have high and low pts
87166 if(clusterHighFirst) {
87167 addPt(clusterHighPt);
87168 if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt);
87169 } else {
87170 if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt);
87171 if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt);
87172 }
87173 // and finally insert the end pt
87174 addPt(clusterEndPt);
87175
87176 // have we reached the end of this segment?
87177 if(i >= d.length || !thisPt) break;
87178
87179 // otherwise we have an out-of-cluster point to insert as next clusterStartPt
87180 addPt(thisPt);
87181 clusterStartPt = thisPt;
87182 }
87183
87184 // to get fills right - repeat what we did at the start
87185 if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]);
87186
87187 segments.push(pts.slice(0, pti));
87188 }
87189
87190 return segments;
87191};
87192
87193},{"../../constants/numerical":158,"../../lib":178,"./constants":393}],404:[function(_dereq_,module,exports){
87194/**
87195* Copyright 2012-2020, Plotly, Inc.
87196* All rights reserved.
87197*
87198* This source code is licensed under the MIT license found in the
87199* LICENSE file in the root directory of this source tree.
87200*/
87201
87202
87203'use strict';
87204
87205
87206// common to 'scatter' and 'scatterternary'
87207module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) {
87208 var shape = coerce('line.shape');
87209 if(shape === 'spline') coerce('line.smoothing');
87210};
87211
87212},{}],405:[function(_dereq_,module,exports){
87213/**
87214* Copyright 2012-2020, Plotly, Inc.
87215* All rights reserved.
87216*
87217* This source code is licensed under the MIT license found in the
87218* LICENSE file in the root directory of this source tree.
87219*/
87220
87221'use strict';
87222
87223var LINKEDFILLS = {tonextx: 1, tonexty: 1, tonext: 1};
87224
87225module.exports = function linkTraces(gd, plotinfo, cdscatter) {
87226 var trace, i, group, prevtrace, groupIndex;
87227
87228 // first sort traces to keep stacks & filled-together groups together
87229 var groupIndices = {};
87230 var needsSort = false;
87231 var prevGroupIndex = -1;
87232 var nextGroupIndex = 0;
87233 var prevUnstackedGroupIndex = -1;
87234 for(i = 0; i < cdscatter.length; i++) {
87235 trace = cdscatter[i][0].trace;
87236 group = trace.stackgroup || '';
87237 if(group) {
87238 if(group in groupIndices) {
87239 groupIndex = groupIndices[group];
87240 } else {
87241 groupIndex = groupIndices[group] = nextGroupIndex;
87242 nextGroupIndex++;
87243 }
87244 } else if(trace.fill in LINKEDFILLS && prevUnstackedGroupIndex >= 0) {
87245 groupIndex = prevUnstackedGroupIndex;
87246 } else {
87247 groupIndex = prevUnstackedGroupIndex = nextGroupIndex;
87248 nextGroupIndex++;
87249 }
87250
87251 if(groupIndex < prevGroupIndex) needsSort = true;
87252 trace._groupIndex = prevGroupIndex = groupIndex;
87253 }
87254
87255 var cdscatterSorted = cdscatter.slice();
87256 if(needsSort) {
87257 cdscatterSorted.sort(function(a, b) {
87258 var traceA = a[0].trace;
87259 var traceB = b[0].trace;
87260 return (traceA._groupIndex - traceB._groupIndex) ||
87261 (traceA.index - traceB.index);
87262 });
87263 }
87264
87265 // now link traces to each other
87266 var prevtraces = {};
87267 for(i = 0; i < cdscatterSorted.length; i++) {
87268 trace = cdscatterSorted[i][0].trace;
87269 group = trace.stackgroup || '';
87270
87271 // Note: The check which ensures all cdscatter here are for the same axis and
87272 // are either cartesian or scatterternary has been removed. This code assumes
87273 // the passed scattertraces have been filtered to the proper plot types and
87274 // the proper subplots.
87275 if(trace.visible === true) {
87276 trace._nexttrace = null;
87277
87278 if(trace.fill in LINKEDFILLS) {
87279 prevtrace = prevtraces[group];
87280 trace._prevtrace = prevtrace || null;
87281
87282 if(prevtrace) {
87283 prevtrace._nexttrace = trace;
87284 }
87285 }
87286
87287 trace._ownfill = (trace.fill && (
87288 trace.fill.substr(0, 6) === 'tozero' ||
87289 trace.fill === 'toself' ||
87290 (trace.fill.substr(0, 2) === 'to' && !trace._prevtrace)
87291 ));
87292
87293 prevtraces[group] = trace;
87294 } else {
87295 trace._prevtrace = trace._nexttrace = trace._ownfill = null;
87296 }
87297 }
87298
87299 return cdscatterSorted;
87300};
87301
87302},{}],406:[function(_dereq_,module,exports){
87303/**
87304* Copyright 2012-2020, Plotly, Inc.
87305* All rights reserved.
87306*
87307* This source code is licensed under the MIT license found in the
87308* LICENSE file in the root directory of this source tree.
87309*/
87310
87311
87312'use strict';
87313
87314var isNumeric = _dereq_('fast-isnumeric');
87315
87316
87317// used in the drawing step for 'scatter' and 'scattegeo' and
87318// in the convert step for 'scatter3d'
87319module.exports = function makeBubbleSizeFn(trace) {
87320 var marker = trace.marker;
87321 var sizeRef = marker.sizeref || 1;
87322 var sizeMin = marker.sizemin || 0;
87323
87324 // for bubble charts, allow scaling the provided value linearly
87325 // and by area or diameter.
87326 // Note this only applies to the array-value sizes
87327
87328 var baseFn = (marker.sizemode === 'area') ?
87329 function(v) { return Math.sqrt(v / sizeRef); } :
87330 function(v) { return v / sizeRef; };
87331
87332 // TODO add support for position/negative bubbles?
87333 // TODO add 'sizeoffset' attribute?
87334 return function(v) {
87335 var baseSize = baseFn(v / 2);
87336
87337 // don't show non-numeric and negative sizes
87338 return (isNumeric(baseSize) && (baseSize > 0)) ?
87339 Math.max(baseSize, sizeMin) :
87340 0;
87341 };
87342};
87343
87344},{"fast-isnumeric":18}],407:[function(_dereq_,module,exports){
87345/**
87346* Copyright 2012-2020, Plotly, Inc.
87347* All rights reserved.
87348*
87349* This source code is licensed under the MIT license found in the
87350* LICENSE file in the root directory of this source tree.
87351*/
87352
87353
87354'use strict';
87355
87356module.exports = {
87357 container: 'marker',
87358 min: 'cmin',
87359 max: 'cmax'
87360};
87361
87362},{}],408:[function(_dereq_,module,exports){
87363/**
87364* Copyright 2012-2020, Plotly, Inc.
87365* All rights reserved.
87366*
87367* This source code is licensed under the MIT license found in the
87368* LICENSE file in the root directory of this source tree.
87369*/
87370
87371'use strict';
87372
87373var Color = _dereq_('../../components/color');
87374var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
87375var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
87376
87377var subTypes = _dereq_('./subtypes');
87378
87379/*
87380 * opts: object of flags to control features not all marker users support
87381 * noLine: caller does not support marker lines
87382 * gradient: caller supports gradients
87383 * noSelect: caller does not support selected/unselected attribute containers
87384 */
87385module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
87386 var isBubble = subTypes.isBubble(traceIn);
87387 var lineColor = (traceIn.line || {}).color;
87388 var defaultMLC;
87389
87390 opts = opts || {};
87391
87392 // marker.color inherit from line.color (even if line.color is an array)
87393 if(lineColor) defaultColor = lineColor;
87394
87395 coerce('marker.symbol');
87396 coerce('marker.opacity', isBubble ? 0.7 : 1);
87397 coerce('marker.size');
87398
87399 coerce('marker.color', defaultColor);
87400 if(hasColorscale(traceIn, 'marker')) {
87401 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
87402 }
87403
87404 if(!opts.noSelect) {
87405 coerce('selected.marker.color');
87406 coerce('unselected.marker.color');
87407 coerce('selected.marker.size');
87408 coerce('unselected.marker.size');
87409 }
87410
87411 if(!opts.noLine) {
87412 // if there's a line with a different color than the marker, use
87413 // that line color as the default marker line color
87414 // (except when it's an array)
87415 // mostly this is for transparent markers to behave nicely
87416 if(lineColor && !Array.isArray(lineColor) && (traceOut.marker.color !== lineColor)) {
87417 defaultMLC = lineColor;
87418 } else if(isBubble) defaultMLC = Color.background;
87419 else defaultMLC = Color.defaultLine;
87420
87421 coerce('marker.line.color', defaultMLC);
87422 if(hasColorscale(traceIn, 'marker.line')) {
87423 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'});
87424 }
87425
87426 coerce('marker.line.width', isBubble ? 1 : 0);
87427 }
87428
87429 if(isBubble) {
87430 coerce('marker.sizeref');
87431 coerce('marker.sizemin');
87432 coerce('marker.sizemode');
87433 }
87434
87435 if(opts.gradient) {
87436 var gradientType = coerce('marker.gradient.type');
87437 if(gradientType !== 'none') {
87438 coerce('marker.gradient.color');
87439 }
87440 }
87441};
87442
87443},{"../../components/color":52,"../../components/colorscale/defaults":62,"../../components/colorscale/helpers":63,"./subtypes":413}],409:[function(_dereq_,module,exports){
87444/**
87445* Copyright 2012-2020, Plotly, Inc.
87446* All rights reserved.
87447*
87448* This source code is licensed under the MIT license found in the
87449* LICENSE file in the root directory of this source tree.
87450*/
87451
87452
87453'use strict';
87454
87455var d3 = _dereq_('d3');
87456
87457var Registry = _dereq_('../../registry');
87458var Lib = _dereq_('../../lib');
87459var ensureSingle = Lib.ensureSingle;
87460var identity = Lib.identity;
87461var Drawing = _dereq_('../../components/drawing');
87462
87463var subTypes = _dereq_('./subtypes');
87464var linePoints = _dereq_('./line_points');
87465var linkTraces = _dereq_('./link_traces');
87466var polygonTester = _dereq_('../../lib/polygon').tester;
87467
87468module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transitionOpts, makeOnCompleteCallback) {
87469 var join, onComplete;
87470
87471 // If transition config is provided, then it is only a partial replot and traces not
87472 // updated are removed.
87473 var isFullReplot = !transitionOpts;
87474 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
87475
87476 // Link traces so the z-order of fill layers is correct
87477 var cdscatterSorted = linkTraces(gd, plotinfo, cdscatter);
87478
87479 join = scatterLayer.selectAll('g.trace')
87480 .data(cdscatterSorted, function(d) { return d[0].trace.uid; });
87481
87482 // Append new traces:
87483 join.enter().append('g')
87484 .attr('class', function(d) {
87485 return 'trace scatter trace' + d[0].trace.uid;
87486 })
87487 .style('stroke-miterlimit', 2);
87488 join.order();
87489
87490 createFills(gd, join, plotinfo);
87491
87492 if(hasTransition) {
87493 if(makeOnCompleteCallback) {
87494 // If it was passed a callback to register completion, make a callback. If
87495 // this is created, then it must be executed on completion, otherwise the
87496 // pos-transition redraw will not execute:
87497 onComplete = makeOnCompleteCallback();
87498 }
87499
87500 var transition = d3.transition()
87501 .duration(transitionOpts.duration)
87502 .ease(transitionOpts.easing)
87503 .each('end', function() {
87504 onComplete && onComplete();
87505 })
87506 .each('interrupt', function() {
87507 onComplete && onComplete();
87508 });
87509
87510 transition.each(function() {
87511 // Must run the selection again since otherwise enters/updates get grouped together
87512 // and these get executed out of order. Except we need them in order!
87513 scatterLayer.selectAll('g.trace').each(function(d, i) {
87514 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
87515 });
87516 });
87517 } else {
87518 join.each(function(d, i) {
87519 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
87520 });
87521 }
87522
87523 if(isFullReplot) {
87524 join.exit().remove();
87525 }
87526
87527 // remove paths that didn't get used
87528 scatterLayer.selectAll('path:not([d])').remove();
87529};
87530
87531function createFills(gd, traceJoin, plotinfo) {
87532 traceJoin.each(function(d) {
87533 var fills = ensureSingle(d3.select(this), 'g', 'fills');
87534 Drawing.setClipUrl(fills, plotinfo.layerClipId, gd);
87535
87536 var trace = d[0].trace;
87537
87538 var fillData = [];
87539 if(trace._ownfill) fillData.push('_ownFill');
87540 if(trace._nexttrace) fillData.push('_nextFill');
87541
87542 var fillJoin = fills.selectAll('g').data(fillData, identity);
87543
87544 fillJoin.enter().append('g');
87545
87546 fillJoin.exit()
87547 .each(function(d) { trace[d] = null; })
87548 .remove();
87549
87550 fillJoin.order().each(function(d) {
87551 // make a path element inside the fill group, just so
87552 // we can give it its own data later on and the group can
87553 // keep its simple '_*Fill' data
87554 trace[d] = ensureSingle(d3.select(this), 'path', 'js-fill');
87555 });
87556 });
87557}
87558
87559function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transitionOpts) {
87560 var i;
87561
87562 // Since this has been reorganized and we're executing this on individual traces,
87563 // we need to pass it the full list of cdscatter as well as this trace's index (idx)
87564 // since it does an internal n^2 loop over comparisons with other traces:
87565 selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll);
87566
87567 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
87568
87569 function transition(selection) {
87570 return hasTransition ? selection.transition() : selection;
87571 }
87572
87573 var xa = plotinfo.xaxis;
87574 var ya = plotinfo.yaxis;
87575
87576 var trace = cdscatter[0].trace;
87577 var line = trace.line;
87578 var tr = d3.select(element);
87579
87580 var errorBarGroup = ensureSingle(tr, 'g', 'errorbars');
87581 var lines = ensureSingle(tr, 'g', 'lines');
87582 var points = ensureSingle(tr, 'g', 'points');
87583 var text = ensureSingle(tr, 'g', 'text');
87584
87585 // error bars are at the bottom
87586 Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts);
87587
87588 if(trace.visible !== true) return;
87589
87590 transition(tr).style('opacity', trace.opacity);
87591
87592 // BUILD LINES AND FILLS
87593 var ownFillEl3, tonext;
87594 var ownFillDir = trace.fill.charAt(trace.fill.length - 1);
87595 if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = '';
87596
87597 // store node for tweaking by selectPoints
87598 cdscatter[0][plotinfo.isRangePlot ? 'nodeRangePlot3' : 'node3'] = tr;
87599
87600 var prevRevpath = '';
87601 var prevPolygons = [];
87602 var prevtrace = trace._prevtrace;
87603
87604 if(prevtrace) {
87605 prevRevpath = prevtrace._prevRevpath || '';
87606 tonext = prevtrace._nextFill;
87607 prevPolygons = prevtrace._polygons;
87608 }
87609
87610 var thispath;
87611 var thisrevpath;
87612 // fullpath is all paths for this curve, joined together straight
87613 // across gaps, for filling
87614 var fullpath = '';
87615 // revpath is fullpath reversed, for fill-to-next
87616 var revpath = '';
87617 // functions for converting a point array to a path
87618 var pathfn, revpathbase, revpathfn;
87619 // variables used before and after the data join
87620 var pt0, lastSegment, pt1, thisPolygons;
87621
87622 // initialize line join data / method
87623 var segments = [];
87624 var makeUpdate = Lib.noop;
87625
87626 ownFillEl3 = trace._ownFill;
87627
87628 if(subTypes.hasLines(trace) || trace.fill !== 'none') {
87629 if(tonext) {
87630 // This tells .style which trace to use for fill information:
87631 tonext.datum(cdscatter);
87632 }
87633
87634 if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) {
87635 pathfn = Drawing.steps(line.shape);
87636 revpathbase = Drawing.steps(
87637 line.shape.split('').reverse().join('')
87638 );
87639 } else if(line.shape === 'spline') {
87640 pathfn = revpathbase = function(pts) {
87641 var pLast = pts[pts.length - 1];
87642 if(pts.length > 1 && pts[0][0] === pLast[0] && pts[0][1] === pLast[1]) {
87643 // identical start and end points: treat it as a
87644 // closed curve so we don't get a kink
87645 return Drawing.smoothclosed(pts.slice(1), line.smoothing);
87646 } else {
87647 return Drawing.smoothopen(pts, line.smoothing);
87648 }
87649 };
87650 } else {
87651 pathfn = revpathbase = function(pts) {
87652 return 'M' + pts.join('L');
87653 };
87654 }
87655
87656 revpathfn = function(pts) {
87657 // note: this is destructive (reverses pts in place) so can't use pts after this
87658 return revpathbase(pts.reverse());
87659 };
87660
87661 segments = linePoints(cdscatter, {
87662 xaxis: xa,
87663 yaxis: ya,
87664 connectGaps: trace.connectgaps,
87665 baseTolerance: Math.max(line.width || 1, 3) / 4,
87666 shape: line.shape,
87667 simplify: line.simplify,
87668 fill: trace.fill
87669 });
87670
87671 // since we already have the pixel segments here, use them to make
87672 // polygons for hover on fill
87673 // TODO: can we skip this if hoveron!=fills? That would mean we
87674 // need to redraw when you change hoveron...
87675 thisPolygons = trace._polygons = new Array(segments.length);
87676 for(i = 0; i < segments.length; i++) {
87677 trace._polygons[i] = polygonTester(segments[i]);
87678 }
87679
87680 if(segments.length) {
87681 pt0 = segments[0][0];
87682 lastSegment = segments[segments.length - 1];
87683 pt1 = lastSegment[lastSegment.length - 1];
87684 }
87685
87686 makeUpdate = function(isEnter) {
87687 return function(pts) {
87688 thispath = pathfn(pts);
87689 thisrevpath = revpathfn(pts);
87690 if(!fullpath) {
87691 fullpath = thispath;
87692 revpath = thisrevpath;
87693 } else if(ownFillDir) {
87694 fullpath += 'L' + thispath.substr(1);
87695 revpath = thisrevpath + ('L' + revpath.substr(1));
87696 } else {
87697 fullpath += 'Z' + thispath;
87698 revpath = thisrevpath + 'Z' + revpath;
87699 }
87700
87701 if(subTypes.hasLines(trace) && pts.length > 1) {
87702 var el = d3.select(this);
87703
87704 // This makes the coloring work correctly:
87705 el.datum(cdscatter);
87706
87707 if(isEnter) {
87708 transition(el.style('opacity', 0)
87709 .attr('d', thispath)
87710 .call(Drawing.lineGroupStyle))
87711 .style('opacity', 1);
87712 } else {
87713 var sel = transition(el);
87714 sel.attr('d', thispath);
87715 Drawing.singleLineStyle(cdscatter, sel);
87716 }
87717 }
87718 };
87719 };
87720 }
87721
87722 var lineJoin = lines.selectAll('.js-line').data(segments);
87723
87724 transition(lineJoin.exit())
87725 .style('opacity', 0)
87726 .remove();
87727
87728 lineJoin.each(makeUpdate(false));
87729
87730 lineJoin.enter().append('path')
87731 .classed('js-line', true)
87732 .style('vector-effect', 'non-scaling-stroke')
87733 .call(Drawing.lineGroupStyle)
87734 .each(makeUpdate(true));
87735
87736 Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd);
87737
87738 function clearFill(selection) {
87739 transition(selection).attr('d', 'M0,0Z');
87740 }
87741
87742 if(segments.length) {
87743 if(ownFillEl3) {
87744 ownFillEl3.datum(cdscatter);
87745 if(pt0 && pt1) {
87746 if(ownFillDir) {
87747 if(ownFillDir === 'y') {
87748 pt0[1] = pt1[1] = ya.c2p(0, true);
87749 } else if(ownFillDir === 'x') {
87750 pt0[0] = pt1[0] = xa.c2p(0, true);
87751 }
87752
87753 // fill to zero: full trace path, plus extension of
87754 // the endpoints to the appropriate axis
87755 // For the sake of animations, wrap the points around so that
87756 // the points on the axes are the first two points. Otherwise
87757 // animations get a little crazy if the number of points changes.
87758 transition(ownFillEl3).attr('d', 'M' + pt1 + 'L' + pt0 + 'L' + fullpath.substr(1))
87759 .call(Drawing.singleFillStyle);
87760 } else {
87761 // fill to self: just join the path to itself
87762 transition(ownFillEl3).attr('d', fullpath + 'Z')
87763 .call(Drawing.singleFillStyle);
87764 }
87765 }
87766 } else if(tonext) {
87767 if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
87768 // fill to next: full trace path, plus the previous path reversed
87769 if(trace.fill === 'tonext') {
87770 // tonext: for use by concentric shapes, like manually constructed
87771 // contours, we just add the two paths closed on themselves.
87772 // This makes strange results if one path is *not* entirely
87773 // inside the other, but then that is a strange usage.
87774 transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
87775 .call(Drawing.singleFillStyle);
87776 } else {
87777 // tonextx/y: for now just connect endpoints with lines. This is
87778 // the correct behavior if the endpoints are at the same value of
87779 // y/x, but if they *aren't*, we should ideally do more complicated
87780 // things depending on whether the new endpoint projects onto the
87781 // existing curve or off the end of it
87782 transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
87783 .call(Drawing.singleFillStyle);
87784 }
87785 trace._polygons = trace._polygons.concat(prevPolygons);
87786 } else {
87787 clearFill(tonext);
87788 trace._polygons = null;
87789 }
87790 }
87791 trace._prevRevpath = revpath;
87792 trace._prevPolygons = thisPolygons;
87793 } else {
87794 if(ownFillEl3) clearFill(ownFillEl3);
87795 else if(tonext) clearFill(tonext);
87796 trace._polygons = trace._prevRevpath = trace._prevPolygons = null;
87797 }
87798
87799
87800 function visFilter(d) {
87801 return d.filter(function(v) { return !v.gap && v.vis; });
87802 }
87803
87804 function visFilterWithGaps(d) {
87805 return d.filter(function(v) { return v.vis; });
87806 }
87807
87808 function gapFilter(d) {
87809 return d.filter(function(v) { return !v.gap; });
87810 }
87811
87812 function keyFunc(d) {
87813 return d.id;
87814 }
87815
87816 // Returns a function if the trace is keyed, otherwise returns undefined
87817 function getKeyFunc(trace) {
87818 if(trace.ids) {
87819 return keyFunc;
87820 }
87821 }
87822
87823 function hideFilter() {
87824 return false;
87825 }
87826
87827 function makePoints(points, text, cdscatter) {
87828 var join, selection, hasNode;
87829
87830 var trace = cdscatter[0].trace;
87831 var showMarkers = subTypes.hasMarkers(trace);
87832 var showText = subTypes.hasText(trace);
87833
87834 var keyFunc = getKeyFunc(trace);
87835 var markerFilter = hideFilter;
87836 var textFilter = hideFilter;
87837
87838 if(showMarkers || showText) {
87839 var showFilter = identity;
87840 // if we're stacking, "infer zero" gap mode gets markers in the
87841 // gap points - because we've inferred a zero there - but other
87842 // modes (currently "interpolate", later "interrupt" hopefully)
87843 // we don't draw generated markers
87844 var stackGroup = trace.stackgroup;
87845 var isInferZero = stackGroup && (
87846 gd._fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup].stackgaps === 'infer zero');
87847 if(trace.marker.maxdisplayed || trace._needsCull) {
87848 showFilter = isInferZero ? visFilterWithGaps : visFilter;
87849 } else if(stackGroup && !isInferZero) {
87850 showFilter = gapFilter;
87851 }
87852
87853 if(showMarkers) markerFilter = showFilter;
87854 if(showText) textFilter = showFilter;
87855 }
87856
87857 // marker points
87858
87859 selection = points.selectAll('path.point');
87860
87861 join = selection.data(markerFilter, keyFunc);
87862
87863 var enter = join.enter().append('path')
87864 .classed('point', true);
87865
87866 if(hasTransition) {
87867 enter
87868 .call(Drawing.pointStyle, trace, gd)
87869 .call(Drawing.translatePoints, xa, ya)
87870 .style('opacity', 0)
87871 .transition()
87872 .style('opacity', 1);
87873 }
87874
87875 join.order();
87876
87877 var styleFns;
87878 if(showMarkers) {
87879 styleFns = Drawing.makePointStyleFns(trace);
87880 }
87881
87882 join.each(function(d) {
87883 var el = d3.select(this);
87884 var sel = transition(el);
87885 hasNode = Drawing.translatePoint(d, sel, xa, ya);
87886
87887 if(hasNode) {
87888 Drawing.singlePointStyle(d, sel, trace, styleFns, gd);
87889
87890 if(plotinfo.layerClipId) {
87891 Drawing.hideOutsideRangePoint(d, sel, xa, ya, trace.xcalendar, trace.ycalendar);
87892 }
87893
87894 if(trace.customdata) {
87895 el.classed('plotly-customdata', d.data !== null && d.data !== undefined);
87896 }
87897 } else {
87898 sel.remove();
87899 }
87900 });
87901
87902 if(hasTransition) {
87903 join.exit().transition()
87904 .style('opacity', 0)
87905 .remove();
87906 } else {
87907 join.exit().remove();
87908 }
87909
87910 // text points
87911 selection = text.selectAll('g');
87912 join = selection.data(textFilter, keyFunc);
87913
87914 // each text needs to go in its own 'g' in case
87915 // it gets converted to mathjax
87916 join.enter().append('g').classed('textpoint', true).append('text');
87917
87918 join.order();
87919
87920 join.each(function(d) {
87921 var g = d3.select(this);
87922 var sel = transition(g.select('text'));
87923 hasNode = Drawing.translatePoint(d, sel, xa, ya);
87924
87925 if(hasNode) {
87926 if(plotinfo.layerClipId) {
87927 Drawing.hideOutsideRangePoint(d, g, xa, ya, trace.xcalendar, trace.ycalendar);
87928 }
87929 } else {
87930 g.remove();
87931 }
87932 });
87933
87934 join.selectAll('text')
87935 .call(Drawing.textPointStyle, trace, gd)
87936 .each(function(d) {
87937 // This just *has* to be totally custom becuase of SVG text positioning :(
87938 // It's obviously copied from translatePoint; we just can't use that
87939 var x = xa.c2p(d.x);
87940 var y = ya.c2p(d.y);
87941
87942 d3.select(this).selectAll('tspan.line').each(function() {
87943 transition(d3.select(this)).attr({x: x, y: y});
87944 });
87945 });
87946
87947 join.exit().remove();
87948 }
87949
87950 points.datum(cdscatter);
87951 text.datum(cdscatter);
87952 makePoints(points, text, cdscatter);
87953
87954 // lastly, clip points groups of `cliponaxis !== false` traces
87955 // on `plotinfo._hasClipOnAxisFalse === true` subplots
87956 var hasClipOnAxisFalse = trace.cliponaxis === false;
87957 var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId;
87958 Drawing.setClipUrl(points, clipUrl, gd);
87959 Drawing.setClipUrl(text, clipUrl, gd);
87960}
87961
87962function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) {
87963 var xa = plotinfo.xaxis;
87964 var ya = plotinfo.yaxis;
87965 var xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c));
87966 var yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c));
87967
87968 var trace = cdscatter[0].trace;
87969 if(!subTypes.hasMarkers(trace)) return;
87970 // if marker.maxdisplayed is used, select a maximum of
87971 // mnum markers to show, from the set that are in the viewport
87972 var mnum = trace.marker.maxdisplayed;
87973
87974 // TODO: remove some as we get away from the viewport?
87975 if(mnum === 0) return;
87976
87977 var cd = cdscatter.filter(function(v) {
87978 return v.x >= xr[0] && v.x <= xr[1] && v.y >= yr[0] && v.y <= yr[1];
87979 });
87980 var inc = Math.ceil(cd.length / mnum);
87981 var tnum = 0;
87982 cdscatterAll.forEach(function(cdj, j) {
87983 var tracei = cdj[0].trace;
87984 if(subTypes.hasMarkers(tracei) &&
87985 tracei.marker.maxdisplayed > 0 && j < idx) {
87986 tnum++;
87987 }
87988 });
87989
87990 // if multiple traces use maxdisplayed, stagger which markers we
87991 // display this formula offsets successive traces by 1/3 of the
87992 // increment, adding an extra small amount after each triplet so
87993 // it's not quite periodic
87994 var i0 = Math.round(tnum * inc / 3 + Math.floor(tnum / 3) * inc / 7.1);
87995
87996 // for error bars: save in cd which markers to show
87997 // so we don't have to repeat this
87998 cdscatter.forEach(function(v) { delete v.vis; });
87999 cd.forEach(function(v, i) {
88000 if(Math.round((i + i0) % inc) === 0) v.vis = true;
88001 });
88002}
88003
88004},{"../../components/drawing":74,"../../lib":178,"../../lib/polygon":190,"../../registry":269,"./line_points":403,"./link_traces":405,"./subtypes":413,"d3":16}],410:[function(_dereq_,module,exports){
88005/**
88006* Copyright 2012-2020, Plotly, Inc.
88007* All rights reserved.
88008*
88009* This source code is licensed under the MIT license found in the
88010* LICENSE file in the root directory of this source tree.
88011*/
88012
88013
88014'use strict';
88015
88016var subtypes = _dereq_('./subtypes');
88017
88018module.exports = function selectPoints(searchInfo, selectionTester) {
88019 var cd = searchInfo.cd;
88020 var xa = searchInfo.xaxis;
88021 var ya = searchInfo.yaxis;
88022 var selection = [];
88023 var trace = cd[0].trace;
88024 var i;
88025 var di;
88026 var x;
88027 var y;
88028
88029 var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
88030 if(hasOnlyLines) return [];
88031
88032 if(selectionTester === false) { // clear selection
88033 for(i = 0; i < cd.length; i++) {
88034 cd[i].selected = 0;
88035 }
88036 } else {
88037 for(i = 0; i < cd.length; i++) {
88038 di = cd[i];
88039 x = xa.c2p(di.x);
88040 y = ya.c2p(di.y);
88041
88042 if((di.i !== null) && selectionTester.contains([x, y], false, i, searchInfo)) {
88043 selection.push({
88044 pointNumber: di.i,
88045 x: xa.c2d(di.x),
88046 y: ya.c2d(di.y)
88047 });
88048 di.selected = 1;
88049 } else {
88050 di.selected = 0;
88051 }
88052 }
88053 }
88054
88055 return selection;
88056};
88057
88058},{"./subtypes":413}],411:[function(_dereq_,module,exports){
88059/**
88060* Copyright 2012-2020, Plotly, Inc.
88061* All rights reserved.
88062*
88063* This source code is licensed under the MIT license found in the
88064* LICENSE file in the root directory of this source tree.
88065*/
88066
88067'use strict';
88068
88069var perStackAttrs = ['orientation', 'groupnorm', 'stackgaps'];
88070
88071module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) {
88072 var stackOpts = layout._scatterStackOpts;
88073
88074 var stackGroup = coerce('stackgroup');
88075 if(stackGroup) {
88076 // use independent stacking options per subplot
88077 var subplot = traceOut.xaxis + traceOut.yaxis;
88078 var subplotStackOpts = stackOpts[subplot];
88079 if(!subplotStackOpts) subplotStackOpts = stackOpts[subplot] = {};
88080
88081 var groupOpts = subplotStackOpts[stackGroup];
88082 var firstTrace = false;
88083 if(groupOpts) {
88084 groupOpts.traces.push(traceOut);
88085 } else {
88086 groupOpts = subplotStackOpts[stackGroup] = {
88087 // keep track of trace indices for use during stacking calculations
88088 // this will be filled in during `calc` and used during `crossTraceCalc`
88089 // so it's OK if we don't recreate it during a non-calc edit
88090 traceIndices: [],
88091 // Hold on to the whole set of prior traces
88092 // First one is most important, so we can clear defaults
88093 // there if we find explicit values only in later traces.
88094 // We're only going to *use* the values stored in groupOpts,
88095 // but for the editor and validate we want things self-consistent
88096 // The full set of traces is used only to fix `fill` default if
88097 // we find `orientation: 'h'` beyond the first trace
88098 traces: [traceOut]
88099 };
88100 firstTrace = true;
88101 }
88102 // TODO: how is this going to work with groupby transforms?
88103 // in principle it should be OK I guess, as long as explicit group styles
88104 // don't override explicit base-trace styles?
88105
88106 var dflts = {
88107 orientation: (traceOut.x && !traceOut.y) ? 'h' : 'v'
88108 };
88109
88110 for(var i = 0; i < perStackAttrs.length; i++) {
88111 var attr = perStackAttrs[i];
88112 var attrFound = attr + 'Found';
88113 if(!groupOpts[attrFound]) {
88114 var traceHasAttr = traceIn[attr] !== undefined;
88115 var isOrientation = attr === 'orientation';
88116 if(traceHasAttr || firstTrace) {
88117 groupOpts[attr] = coerce(attr, dflts[attr]);
88118
88119 if(isOrientation) {
88120 groupOpts.fillDflt = groupOpts[attr] === 'h' ?
88121 'tonextx' : 'tonexty';
88122 }
88123
88124 if(traceHasAttr) {
88125 // Note: this will show a value here even if it's invalid
88126 // in which case it will revert to default.
88127 groupOpts[attrFound] = true;
88128
88129 // Note: only one trace in the stack will get a _fullData
88130 // entry for a given stack-wide attribute. If no traces
88131 // (or the first trace) specify that attribute, the
88132 // first trace will get it. If the first trace does NOT
88133 // specify it but some later trace does, then it gets
88134 // removed from the first trace and only included in the
88135 // one that specified it. This is mostly important for
88136 // editors (that want to see the full values to know
88137 // what settings are available) and Plotly.react diffing.
88138 // Editors may want to use fullLayout._scatterStackOpts
88139 // directly and make these settings available from all
88140 // traces in the stack... then set the new value into
88141 // the first trace, and clear all later traces.
88142 if(!firstTrace) {
88143 delete groupOpts.traces[0][attr];
88144
88145 // orientation can affect default fill of previous traces
88146 if(isOrientation) {
88147 for(var j = 0; j < groupOpts.traces.length - 1; j++) {
88148 var trace2 = groupOpts.traces[j];
88149 if(trace2._input.fill !== trace2.fill) {
88150 trace2.fill = groupOpts.fillDflt;
88151 }
88152 }
88153 }
88154 }
88155 }
88156 }
88157 }
88158 }
88159 return groupOpts;
88160 }
88161};
88162
88163},{}],412:[function(_dereq_,module,exports){
88164/**
88165* Copyright 2012-2020, Plotly, Inc.
88166* All rights reserved.
88167*
88168* This source code is licensed under the MIT license found in the
88169* LICENSE file in the root directory of this source tree.
88170*/
88171
88172
88173'use strict';
88174
88175var d3 = _dereq_('d3');
88176var Drawing = _dereq_('../../components/drawing');
88177var Registry = _dereq_('../../registry');
88178
88179function style(gd) {
88180 var s = d3.select(gd).selectAll('g.trace.scatter');
88181
88182 s.style('opacity', function(d) {
88183 return d[0].trace.opacity;
88184 });
88185
88186 s.selectAll('g.points').each(function(d) {
88187 var sel = d3.select(this);
88188 var trace = d.trace || d[0].trace;
88189 stylePoints(sel, trace, gd);
88190 });
88191
88192 s.selectAll('g.text').each(function(d) {
88193 var sel = d3.select(this);
88194 var trace = d.trace || d[0].trace;
88195 styleText(sel, trace, gd);
88196 });
88197
88198 s.selectAll('g.trace path.js-line')
88199 .call(Drawing.lineGroupStyle);
88200
88201 s.selectAll('g.trace path.js-fill')
88202 .call(Drawing.fillGroupStyle);
88203
88204 Registry.getComponentMethod('errorbars', 'style')(s);
88205}
88206
88207function stylePoints(sel, trace, gd) {
88208 Drawing.pointStyle(sel.selectAll('path.point'), trace, gd);
88209}
88210
88211function styleText(sel, trace, gd) {
88212 Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
88213}
88214
88215function styleOnSelect(gd, cd, sel) {
88216 var trace = cd[0].trace;
88217
88218 if(trace.selectedpoints) {
88219 Drawing.selectedPointStyle(sel.selectAll('path.point'), trace);
88220 Drawing.selectedTextStyle(sel.selectAll('text'), trace);
88221 } else {
88222 stylePoints(sel, trace, gd);
88223 styleText(sel, trace, gd);
88224 }
88225}
88226
88227module.exports = {
88228 style: style,
88229 stylePoints: stylePoints,
88230 styleText: styleText,
88231 styleOnSelect: styleOnSelect
88232};
88233
88234},{"../../components/drawing":74,"../../registry":269,"d3":16}],413:[function(_dereq_,module,exports){
88235/**
88236* Copyright 2012-2020, Plotly, Inc.
88237* All rights reserved.
88238*
88239* This source code is licensed under the MIT license found in the
88240* LICENSE file in the root directory of this source tree.
88241*/
88242
88243
88244'use strict';
88245
88246var Lib = _dereq_('../../lib');
88247
88248module.exports = {
88249 hasLines: function(trace) {
88250 return trace.visible && trace.mode &&
88251 trace.mode.indexOf('lines') !== -1;
88252 },
88253
88254 hasMarkers: function(trace) {
88255 return trace.visible && (
88256 (trace.mode && trace.mode.indexOf('markers') !== -1) ||
88257 // until splom implements 'mode'
88258 trace.type === 'splom'
88259 );
88260 },
88261
88262 hasText: function(trace) {
88263 return trace.visible && trace.mode &&
88264 trace.mode.indexOf('text') !== -1;
88265 },
88266
88267 isBubble: function(trace) {
88268 return Lib.isPlainObject(trace.marker) &&
88269 Lib.isArrayOrTypedArray(trace.marker.size);
88270 }
88271};
88272
88273},{"../../lib":178}],414:[function(_dereq_,module,exports){
88274/**
88275* Copyright 2012-2020, Plotly, Inc.
88276* All rights reserved.
88277*
88278* This source code is licensed under the MIT license found in the
88279* LICENSE file in the root directory of this source tree.
88280*/
88281
88282
88283'use strict';
88284
88285var Lib = _dereq_('../../lib');
88286
88287/*
88288 * opts: object of flags to control features not all text users support
88289 * noSelect: caller does not support selected/unselected attribute containers
88290 */
88291module.exports = function(traceIn, traceOut, layout, coerce, opts) {
88292 opts = opts || {};
88293
88294 coerce('textposition');
88295 Lib.coerceFont(coerce, 'textfont', layout.font);
88296
88297 if(!opts.noSelect) {
88298 coerce('selected.textfont.color');
88299 coerce('unselected.textfont.color');
88300 }
88301};
88302
88303},{"../../lib":178}],415:[function(_dereq_,module,exports){
88304/**
88305* Copyright 2012-2020, Plotly, Inc.
88306* All rights reserved.
88307*
88308* This source code is licensed under the MIT license found in the
88309* LICENSE file in the root directory of this source tree.
88310*/
88311
88312'use strict';
88313
88314var Lib = _dereq_('../../lib');
88315var Registry = _dereq_('../../registry');
88316
88317module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) {
88318 var x = coerce('x');
88319 var y = coerce('y');
88320 var len;
88321
88322 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
88323 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
88324
88325 if(x) {
88326 var xlen = Lib.minRowLength(x);
88327 if(y) {
88328 len = Math.min(xlen, Lib.minRowLength(y));
88329 } else {
88330 len = xlen;
88331 coerce('y0');
88332 coerce('dy');
88333 }
88334 } else {
88335 if(!y) return 0;
88336
88337 len = Lib.minRowLength(y);
88338 coerce('x0');
88339 coerce('dx');
88340 }
88341
88342 traceOut._length = len;
88343
88344 return len;
88345};
88346
88347},{"../../lib":178,"../../registry":269}],416:[function(_dereq_,module,exports){
88348/**
88349* Copyright 2012-2020, Plotly, Inc.
88350* All rights reserved.
88351*
88352* This source code is licensed under the MIT license found in the
88353* LICENSE file in the root directory of this source tree.
88354*/
88355
88356'use strict';
88357
88358var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
88359var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
88360var scatterAttrs = _dereq_('../scatter/attributes');
88361var baseAttrs = _dereq_('../../plots/attributes');
88362var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
88363var dash = _dereq_('../../components/drawing/attributes').dash;
88364
88365var extendFlat = _dereq_('../../lib/extend').extendFlat;
88366
88367var scatterMarkerAttrs = scatterAttrs.marker;
88368var scatterLineAttrs = scatterAttrs.line;
88369var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
88370
88371module.exports = {
88372 a: {
88373 valType: 'data_array',
88374 editType: 'calc',
88375
88376 },
88377 b: {
88378 valType: 'data_array',
88379 editType: 'calc',
88380
88381 },
88382 c: {
88383 valType: 'data_array',
88384 editType: 'calc',
88385
88386 },
88387 sum: {
88388 valType: 'number',
88389
88390 dflt: 0,
88391 min: 0,
88392 editType: 'calc',
88393
88394 },
88395 mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}),
88396 text: extendFlat({}, scatterAttrs.text, {
88397
88398 }),
88399 texttemplate: texttemplateAttrs({editType: 'plot'}, {
88400 keys: ['a', 'b', 'c', 'text']
88401 }),
88402 hovertext: extendFlat({}, scatterAttrs.hovertext, {
88403
88404 }),
88405 line: {
88406 color: scatterLineAttrs.color,
88407 width: scatterLineAttrs.width,
88408 dash: dash,
88409 shape: extendFlat({}, scatterLineAttrs.shape,
88410 {values: ['linear', 'spline']}),
88411 smoothing: scatterLineAttrs.smoothing,
88412 editType: 'calc'
88413 },
88414 connectgaps: scatterAttrs.connectgaps,
88415 cliponaxis: scatterAttrs.cliponaxis,
88416 fill: extendFlat({}, scatterAttrs.fill, {
88417 values: ['none', 'toself', 'tonext'],
88418 dflt: 'none',
88419
88420 }),
88421 fillcolor: scatterAttrs.fillcolor,
88422 marker: extendFlat({
88423 symbol: scatterMarkerAttrs.symbol,
88424 opacity: scatterMarkerAttrs.opacity,
88425 maxdisplayed: scatterMarkerAttrs.maxdisplayed,
88426 size: scatterMarkerAttrs.size,
88427 sizeref: scatterMarkerAttrs.sizeref,
88428 sizemin: scatterMarkerAttrs.sizemin,
88429 sizemode: scatterMarkerAttrs.sizemode,
88430 line: extendFlat({
88431 width: scatterMarkerLineAttrs.width,
88432 editType: 'calc'
88433 },
88434 colorScaleAttrs('marker.line')
88435 ),
88436 gradient: scatterMarkerAttrs.gradient,
88437 editType: 'calc'
88438 },
88439 colorScaleAttrs('marker')
88440 ),
88441
88442 textfont: scatterAttrs.textfont,
88443 textposition: scatterAttrs.textposition,
88444
88445 selected: scatterAttrs.selected,
88446 unselected: scatterAttrs.unselected,
88447
88448 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
88449 flags: ['a', 'b', 'c', 'text', 'name']
88450 }),
88451 hoveron: scatterAttrs.hoveron,
88452 hovertemplate: hovertemplateAttrs(),
88453};
88454
88455},{"../../components/colorscale/attributes":59,"../../components/drawing/attributes":73,"../../lib/extend":173,"../../plots/attributes":219,"../../plots/template_attributes":264,"../scatter/attributes":389}],417:[function(_dereq_,module,exports){
88456/**
88457* Copyright 2012-2020, Plotly, Inc.
88458* All rights reserved.
88459*
88460* This source code is licensed under the MIT license found in the
88461* LICENSE file in the root directory of this source tree.
88462*/
88463
88464
88465'use strict';
88466
88467var isNumeric = _dereq_('fast-isnumeric');
88468
88469var calcColorscale = _dereq_('../scatter/colorscale_calc');
88470var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata');
88471var calcSelection = _dereq_('../scatter/calc_selection');
88472var calcMarkerSize = _dereq_('../scatter/calc').calcMarkerSize;
88473
88474var dataArrays = ['a', 'b', 'c'];
88475var arraysToFill = {a: ['b', 'c'], b: ['a', 'c'], c: ['a', 'b']};
88476
88477module.exports = function calc(gd, trace) {
88478 var ternary = gd._fullLayout[trace.subplot];
88479 var displaySum = ternary.sum;
88480 var normSum = trace.sum || displaySum;
88481 var arrays = {a: trace.a, b: trace.b, c: trace.c};
88482
88483 var i, j, dataArray, newArray, fillArray1, fillArray2;
88484
88485 // fill in one missing component
88486 for(i = 0; i < dataArrays.length; i++) {
88487 dataArray = dataArrays[i];
88488 if(arrays[dataArray]) continue;
88489
88490 fillArray1 = arrays[arraysToFill[dataArray][0]];
88491 fillArray2 = arrays[arraysToFill[dataArray][1]];
88492 newArray = new Array(fillArray1.length);
88493 for(j = 0; j < fillArray1.length; j++) {
88494 newArray[j] = normSum - fillArray1[j] - fillArray2[j];
88495 }
88496 arrays[dataArray] = newArray;
88497 }
88498
88499 // make the calcdata array
88500 var serieslen = trace._length;
88501 var cd = new Array(serieslen);
88502 var a, b, c, norm, x, y;
88503 for(i = 0; i < serieslen; i++) {
88504 a = arrays.a[i];
88505 b = arrays.b[i];
88506 c = arrays.c[i];
88507 if(isNumeric(a) && isNumeric(b) && isNumeric(c)) {
88508 a = +a;
88509 b = +b;
88510 c = +c;
88511 norm = displaySum / (a + b + c);
88512 if(norm !== 1) {
88513 a *= norm;
88514 b *= norm;
88515 c *= norm;
88516 }
88517 // map a, b, c onto x and y where the full scale of y
88518 // is [0, sum], and x is [-sum, sum]
88519 // TODO: this makes `a` always the top, `b` the bottom left,
88520 // and `c` the bottom right. Do we want options to rearrange
88521 // these?
88522 y = a;
88523 x = c - b;
88524 cd[i] = {x: x, y: y, a: a, b: b, c: c};
88525 } else cd[i] = {x: false, y: false};
88526 }
88527
88528 calcMarkerSize(trace, serieslen);
88529 calcColorscale(gd, trace);
88530 arraysToCalcdata(cd, trace);
88531 calcSelection(cd, trace);
88532
88533 return cd;
88534};
88535
88536},{"../scatter/arrays_to_calcdata":388,"../scatter/calc":390,"../scatter/calc_selection":391,"../scatter/colorscale_calc":392,"fast-isnumeric":18}],418:[function(_dereq_,module,exports){
88537/**
88538* Copyright 2012-2020, Plotly, Inc.
88539* All rights reserved.
88540*
88541* This source code is licensed under the MIT license found in the
88542* LICENSE file in the root directory of this source tree.
88543*/
88544
88545
88546'use strict';
88547
88548var Lib = _dereq_('../../lib');
88549
88550var constants = _dereq_('../scatter/constants');
88551var subTypes = _dereq_('../scatter/subtypes');
88552var handleMarkerDefaults = _dereq_('../scatter/marker_defaults');
88553var handleLineDefaults = _dereq_('../scatter/line_defaults');
88554var handleLineShapeDefaults = _dereq_('../scatter/line_shape_defaults');
88555var handleTextDefaults = _dereq_('../scatter/text_defaults');
88556var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults');
88557
88558var attributes = _dereq_('./attributes');
88559
88560
88561module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
88562 function coerce(attr, dflt) {
88563 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
88564 }
88565
88566 var a = coerce('a');
88567 var b = coerce('b');
88568 var c = coerce('c');
88569 var len;
88570
88571 // allow any one array to be missing, len is the minimum length of those
88572 // present. Note that after coerce data_array's are either Arrays (which
88573 // are truthy even if empty) or undefined. As in scatter, an empty array
88574 // is different from undefined, because it can signify that this data is
88575 // not known yet but expected in the future
88576 if(a) {
88577 len = a.length;
88578 if(b) {
88579 len = Math.min(len, b.length);
88580 if(c) len = Math.min(len, c.length);
88581 } else if(c) len = Math.min(len, c.length);
88582 else len = 0;
88583 } else if(b && c) {
88584 len = Math.min(b.length, c.length);
88585 }
88586
88587 if(!len) {
88588 traceOut.visible = false;
88589 return;
88590 }
88591
88592 traceOut._length = len;
88593
88594 coerce('sum');
88595
88596 coerce('text');
88597 coerce('hovertext');
88598 if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
88599
88600 var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines';
88601 coerce('mode', defaultMode);
88602
88603 if(subTypes.hasLines(traceOut)) {
88604 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
88605 handleLineShapeDefaults(traceIn, traceOut, coerce);
88606 coerce('connectgaps');
88607 }
88608
88609 if(subTypes.hasMarkers(traceOut)) {
88610 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
88611 }
88612
88613 if(subTypes.hasText(traceOut)) {
88614 coerce('texttemplate');
88615 handleTextDefaults(traceIn, traceOut, layout, coerce);
88616 }
88617
88618 var dfltHoverOn = [];
88619
88620 if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
88621 coerce('cliponaxis');
88622 coerce('marker.maxdisplayed');
88623 dfltHoverOn.push('points');
88624 }
88625
88626 coerce('fill');
88627 if(traceOut.fill !== 'none') {
88628 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
88629 if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
88630 }
88631
88632 if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
88633 dfltHoverOn.push('fills');
88634 }
88635 coerce('hoveron', dfltHoverOn.join('+') || 'points');
88636
88637 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
88638};
88639
88640},{"../../lib":178,"../scatter/constants":393,"../scatter/fillcolor_defaults":397,"../scatter/line_defaults":402,"../scatter/line_shape_defaults":404,"../scatter/marker_defaults":408,"../scatter/subtypes":413,"../scatter/text_defaults":414,"./attributes":416}],419:[function(_dereq_,module,exports){
88641/**
88642* Copyright 2012-2020, Plotly, Inc.
88643* All rights reserved.
88644*
88645* This source code is licensed under the MIT license found in the
88646* LICENSE file in the root directory of this source tree.
88647*/
88648
88649'use strict';
88650
88651module.exports = function eventData(out, pt, trace, cd, pointNumber) {
88652 if(pt.xa) out.xaxis = pt.xa;
88653 if(pt.ya) out.yaxis = pt.ya;
88654
88655 if(cd[pointNumber]) {
88656 var cdi = cd[pointNumber];
88657
88658 // N.B. These are the normalized coordinates.
88659 out.a = cdi.a;
88660 out.b = cdi.b;
88661 out.c = cdi.c;
88662 } else {
88663 // for fill-hover only
88664 out.a = pt.a;
88665 out.b = pt.b;
88666 out.c = pt.c;
88667 }
88668
88669 return out;
88670};
88671
88672},{}],420:[function(_dereq_,module,exports){
88673/**
88674* Copyright 2012-2020, Plotly, Inc.
88675* All rights reserved.
88676*
88677* This source code is licensed under the MIT license found in the
88678* LICENSE file in the root directory of this source tree.
88679*/
88680
88681'use strict';
88682
88683var Axes = _dereq_('../../plots/cartesian/axes');
88684
88685module.exports = function formatLabels(cdi, trace, fullLayout) {
88686 var labels = {};
88687
88688 var subplot = fullLayout[trace.subplot]._subplot;
88689 labels.aLabel = Axes.tickText(subplot.aaxis, cdi.a, true).text;
88690 labels.bLabel = Axes.tickText(subplot.baxis, cdi.b, true).text;
88691 labels.cLabel = Axes.tickText(subplot.caxis, cdi.c, true).text;
88692
88693 return labels;
88694};
88695
88696},{"../../plots/cartesian/axes":222}],421:[function(_dereq_,module,exports){
88697/**
88698* Copyright 2012-2020, Plotly, Inc.
88699* All rights reserved.
88700*
88701* This source code is licensed under the MIT license found in the
88702* LICENSE file in the root directory of this source tree.
88703*/
88704
88705'use strict';
88706
88707var scatterHover = _dereq_('../scatter/hover');
88708
88709module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
88710 var scatterPointData = scatterHover(pointData, xval, yval, hovermode);
88711 if(!scatterPointData || scatterPointData[0].index === false) return;
88712
88713 var newPointData = scatterPointData[0];
88714
88715 // if hovering on a fill, we don't show any point data so the label is
88716 // unchanged from what scatter gives us - except that it needs to
88717 // be constrained to the trianglular plot area, not just the rectangular
88718 // area defined by the synthetic x and y axes
88719 // TODO: in some cases the vertical middle of the shape is not within
88720 // the triangular viewport at all, so the label can become disconnected
88721 // from the shape entirely. But calculating what portion of the shape
88722 // is actually visible, as constrained by the diagonal axis lines, is not
88723 // so easy and anyway we lost the information we would have needed to do
88724 // this inside scatterHover.
88725 if(newPointData.index === undefined) {
88726 var yFracUp = 1 - (newPointData.y0 / pointData.ya._length);
88727 var xLen = pointData.xa._length;
88728 var xMin = xLen * yFracUp / 2;
88729 var xMax = xLen - xMin;
88730 newPointData.x0 = Math.max(Math.min(newPointData.x0, xMax), xMin);
88731 newPointData.x1 = Math.max(Math.min(newPointData.x1, xMax), xMin);
88732 return scatterPointData;
88733 }
88734
88735 var cdi = newPointData.cd[newPointData.index];
88736 var trace = newPointData.trace;
88737 var subplot = newPointData.subplot;
88738
88739 newPointData.a = cdi.a;
88740 newPointData.b = cdi.b;
88741 newPointData.c = cdi.c;
88742
88743 newPointData.xLabelVal = undefined;
88744 newPointData.yLabelVal = undefined;
88745
88746 var fullLayout = {};
88747 fullLayout[trace.subplot] = {_subplot: subplot};
88748 var labels = trace._module.formatLabels(cdi, trace, fullLayout);
88749 newPointData.aLabel = labels.aLabel;
88750 newPointData.bLabel = labels.bLabel;
88751 newPointData.cLabel = labels.cLabel;
88752
88753 var hoverinfo = cdi.hi || trace.hoverinfo;
88754 var text = [];
88755 function textPart(ax, val) {
88756 text.push(ax._hovertitle + ': ' + val);
88757 }
88758 if(!trace.hovertemplate) {
88759 var parts = hoverinfo.split('+');
88760 if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c'];
88761 if(parts.indexOf('a') !== -1) textPart(subplot.aaxis, newPointData.aLabel);
88762 if(parts.indexOf('b') !== -1) textPart(subplot.baxis, newPointData.bLabel);
88763 if(parts.indexOf('c') !== -1) textPart(subplot.caxis, newPointData.cLabel);
88764 }
88765 newPointData.extraText = text.join('<br>');
88766 newPointData.hovertemplate = trace.hovertemplate;
88767 return scatterPointData;
88768};
88769
88770},{"../scatter/hover":400}],422:[function(_dereq_,module,exports){
88771/**
88772* Copyright 2012-2020, Plotly, Inc.
88773* All rights reserved.
88774*
88775* This source code is licensed under the MIT license found in the
88776* LICENSE file in the root directory of this source tree.
88777*/
88778
88779'use strict';
88780
88781module.exports = {
88782 attributes: _dereq_('./attributes'),
88783 supplyDefaults: _dereq_('./defaults'),
88784 colorbar: _dereq_('../scatter/marker_colorbar'),
88785 formatLabels: _dereq_('./format_labels'),
88786 calc: _dereq_('./calc'),
88787 plot: _dereq_('./plot'),
88788 style: _dereq_('../scatter/style').style,
88789 styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
88790 hoverPoints: _dereq_('./hover'),
88791 selectPoints: _dereq_('../scatter/select'),
88792 eventData: _dereq_('./event_data'),
88793
88794 moduleType: 'trace',
88795 name: 'scatterternary',
88796 basePlotModule: _dereq_('../../plots/ternary'),
88797 categories: ['ternary', 'symbols', 'showLegend', 'scatter-like'],
88798 meta: {
88799
88800
88801 }
88802};
88803
88804},{"../../plots/ternary":265,"../scatter/marker_colorbar":407,"../scatter/select":410,"../scatter/style":412,"./attributes":416,"./calc":417,"./defaults":418,"./event_data":419,"./format_labels":420,"./hover":421,"./plot":423}],423:[function(_dereq_,module,exports){
88805/**
88806* Copyright 2012-2020, Plotly, Inc.
88807* All rights reserved.
88808*
88809* This source code is licensed under the MIT license found in the
88810* LICENSE file in the root directory of this source tree.
88811*/
88812
88813
88814'use strict';
88815
88816var scatterPlot = _dereq_('../scatter/plot');
88817
88818module.exports = function plot(gd, ternary, moduleCalcData) {
88819 var plotContainer = ternary.plotContainer;
88820
88821 // remove all nodes inside the scatter layer
88822 plotContainer.select('.scatterlayer').selectAll('*').remove();
88823
88824 // mimic cartesian plotinfo
88825 var plotinfo = {
88826 xaxis: ternary.xaxis,
88827 yaxis: ternary.yaxis,
88828 plot: plotContainer,
88829 layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null
88830 };
88831
88832 var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer');
88833
88834 scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer);
88835};
88836
88837},{"../scatter/plot":409}],424:[function(_dereq_,module,exports){
88838/**
88839* Copyright 2012-2020, Plotly, Inc.
88840* All rights reserved.
88841*
88842* This source code is licensed under the MIT license found in the
88843* LICENSE file in the root directory of this source tree.
88844*/
88845
88846'use strict';
88847
88848var boxAttrs = _dereq_('../box/attributes');
88849var extendFlat = _dereq_('../../lib/extend').extendFlat;
88850
88851module.exports = {
88852 y: boxAttrs.y,
88853 x: boxAttrs.x,
88854 x0: boxAttrs.x0,
88855 y0: boxAttrs.y0,
88856 name: extendFlat({}, boxAttrs.name, {
88857
88858 }),
88859 orientation: extendFlat({}, boxAttrs.orientation, {
88860
88861 }),
88862
88863 bandwidth: {
88864 valType: 'number',
88865 min: 0,
88866
88867 editType: 'calc',
88868
88869 },
88870
88871 scalegroup: {
88872 valType: 'string',
88873
88874 dflt: '',
88875 editType: 'calc',
88876
88877 },
88878 scalemode: {
88879 valType: 'enumerated',
88880 values: ['width', 'count'],
88881 dflt: 'width',
88882
88883 editType: 'calc',
88884
88885 },
88886
88887 spanmode: {
88888 valType: 'enumerated',
88889 values: ['soft', 'hard', 'manual'],
88890 dflt: 'soft',
88891
88892 editType: 'calc',
88893
88894 },
88895 span: {
88896 valType: 'info_array',
88897 items: [
88898 {valType: 'any', editType: 'calc'},
88899 {valType: 'any', editType: 'calc'}
88900 ],
88901
88902 editType: 'calc',
88903
88904 },
88905
88906 line: {
88907 color: {
88908 valType: 'color',
88909
88910 editType: 'style',
88911
88912 },
88913 width: {
88914 valType: 'number',
88915
88916 min: 0,
88917 dflt: 2,
88918 editType: 'style',
88919
88920 },
88921 editType: 'plot'
88922 },
88923 fillcolor: boxAttrs.fillcolor,
88924
88925 points: extendFlat({}, boxAttrs.boxpoints, {
88926
88927 }),
88928 jitter: extendFlat({}, boxAttrs.jitter, {
88929
88930 }),
88931 pointpos: extendFlat({}, boxAttrs.pointpos, {
88932
88933 }),
88934
88935 width: extendFlat({}, boxAttrs.width, {
88936
88937 }),
88938
88939 marker: boxAttrs.marker,
88940 text: boxAttrs.text,
88941 hovertext: boxAttrs.hovertext,
88942 hovertemplate: boxAttrs.hovertemplate,
88943
88944 box: {
88945 visible: {
88946 valType: 'boolean',
88947 dflt: false,
88948
88949 editType: 'plot',
88950
88951 },
88952 width: {
88953 valType: 'number',
88954 min: 0,
88955 max: 1,
88956 dflt: 0.25,
88957
88958 editType: 'plot',
88959
88960 },
88961 fillcolor: {
88962 valType: 'color',
88963
88964 editType: 'style',
88965
88966 },
88967 line: {
88968 color: {
88969 valType: 'color',
88970
88971 editType: 'style',
88972
88973 },
88974 width: {
88975 valType: 'number',
88976 min: 0,
88977
88978 editType: 'style',
88979
88980 },
88981 editType: 'style'
88982 },
88983 editType: 'plot'
88984 },
88985
88986 meanline: {
88987 visible: {
88988 valType: 'boolean',
88989 dflt: false,
88990
88991 editType: 'plot',
88992
88993 },
88994 color: {
88995 valType: 'color',
88996
88997 editType: 'style',
88998
88999 },
89000 width: {
89001 valType: 'number',
89002 min: 0,
89003
89004 editType: 'style',
89005
89006 },
89007 editType: 'plot'
89008 },
89009
89010 side: {
89011 valType: 'enumerated',
89012 values: ['both', 'positive', 'negative'],
89013 dflt: 'both',
89014
89015 editType: 'calc',
89016
89017 },
89018
89019 offsetgroup: boxAttrs.offsetgroup,
89020 alignmentgroup: boxAttrs.alignmentgroup,
89021
89022 selected: boxAttrs.selected,
89023 unselected: boxAttrs.unselected,
89024
89025 hoveron: {
89026 valType: 'flaglist',
89027 flags: ['violins', 'points', 'kde'],
89028 dflt: 'violins+points+kde',
89029 extras: ['all'],
89030
89031 editType: 'style',
89032
89033 }
89034};
89035
89036},{"../../lib/extend":173,"../box/attributes":296}],425:[function(_dereq_,module,exports){
89037/**
89038* Copyright 2012-2020, Plotly, Inc.
89039* All rights reserved.
89040*
89041* This source code is licensed under the MIT license found in the
89042* LICENSE file in the root directory of this source tree.
89043*/
89044
89045'use strict';
89046
89047var Lib = _dereq_('../../lib');
89048var Axes = _dereq_('../../plots/cartesian/axes');
89049var boxCalc = _dereq_('../box/calc');
89050var helpers = _dereq_('./helpers');
89051var BADNUM = _dereq_('../../constants/numerical').BADNUM;
89052
89053module.exports = function calc(gd, trace) {
89054 var cd = boxCalc(gd, trace);
89055
89056 if(cd[0].t.empty) return cd;
89057
89058 var fullLayout = gd._fullLayout;
89059 var valAxis = Axes.getFromId(
89060 gd,
89061 trace[trace.orientation === 'h' ? 'xaxis' : 'yaxis']
89062 );
89063
89064 var spanMin = Infinity;
89065 var spanMax = -Infinity;
89066 var maxKDE = 0;
89067 var maxCount = 0;
89068
89069 for(var i = 0; i < cd.length; i++) {
89070 var cdi = cd[i];
89071 var vals = cdi.pts.map(helpers.extractVal);
89072
89073 var bandwidth = cdi.bandwidth = calcBandwidth(trace, cdi, vals);
89074 var span = cdi.span = calcSpan(trace, cdi, valAxis, bandwidth);
89075
89076 if(cdi.min === cdi.max && bandwidth === 0) {
89077 // if span is zero and bandwidth is zero, we want a violin with zero width
89078 span = cdi.span = [cdi.min, cdi.max];
89079 cdi.density = [{v: 1, t: span[0]}];
89080 cdi.bandwidth = bandwidth;
89081 maxKDE = Math.max(maxKDE, 1);
89082 } else {
89083 // step that well covers the bandwidth and is multiple of span distance
89084 var dist = span[1] - span[0];
89085 var n = Math.ceil(dist / (bandwidth / 3));
89086 var step = dist / n;
89087
89088 if(!isFinite(step) || !isFinite(n)) {
89089 Lib.error('Something went wrong with computing the violin span');
89090 cd[0].t.empty = true;
89091 return cd;
89092 }
89093
89094 var kde = helpers.makeKDE(cdi, trace, vals);
89095 cdi.density = new Array(n);
89096
89097 for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) {
89098 var v = kde(t);
89099 cdi.density[k] = {v: v, t: t};
89100 maxKDE = Math.max(maxKDE, v);
89101 }
89102 }
89103
89104 maxCount = Math.max(maxCount, vals.length);
89105 spanMin = Math.min(spanMin, span[0]);
89106 spanMax = Math.max(spanMax, span[1]);
89107 }
89108
89109 var extremes = Axes.findExtremes(valAxis, [spanMin, spanMax], {padded: true});
89110 trace._extremes[valAxis._id] = extremes;
89111
89112 if(trace.width) {
89113 cd[0].t.maxKDE = maxKDE;
89114 } else {
89115 var violinScaleGroupStats = fullLayout._violinScaleGroupStats;
89116 var scaleGroup = trace.scalegroup;
89117 var groupStats = violinScaleGroupStats[scaleGroup];
89118
89119 if(groupStats) {
89120 groupStats.maxKDE = Math.max(groupStats.maxKDE, maxKDE);
89121 groupStats.maxCount = Math.max(groupStats.maxCount, maxCount);
89122 } else {
89123 violinScaleGroupStats[scaleGroup] = {
89124 maxKDE: maxKDE,
89125 maxCount: maxCount
89126 };
89127 }
89128 }
89129
89130 cd[0].t.labels.kde = Lib._(gd, 'kde:');
89131
89132 return cd;
89133};
89134
89135// Default to Silveman's rule of thumb
89136// - https://stats.stackexchange.com/a/6671
89137// - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator
89138// - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py
89139function silvermanRule(len, ssd, iqr) {
89140 var a = Math.min(ssd, iqr / 1.349);
89141 return 1.059 * a * Math.pow(len, -0.2);
89142}
89143
89144function calcBandwidth(trace, cdi, vals) {
89145 var span = cdi.max - cdi.min;
89146
89147 // If span is zero
89148 if(!span) {
89149 if(trace.bandwidth) {
89150 return trace.bandwidth;
89151 } else {
89152 // if span is zero and no bandwidth is specified
89153 // it returns zero bandwidth which is a special case
89154 return 0;
89155 }
89156 }
89157
89158 // Limit how small the bandwidth can be.
89159 //
89160 // Silverman's rule of thumb can be "very" small
89161 // when IQR does a poor job at describing the spread
89162 // of the distribution.
89163 // We also want to limit custom bandwidths
89164 // to not blow up kde computations.
89165
89166 if(trace.bandwidth) {
89167 return Math.max(trace.bandwidth, span / 1e4);
89168 } else {
89169 var len = vals.length;
89170 var ssd = Lib.stdev(vals, len - 1, cdi.mean);
89171 return Math.max(
89172 silvermanRule(len, ssd, cdi.q3 - cdi.q1),
89173 span / 100
89174 );
89175 }
89176}
89177
89178function calcSpan(trace, cdi, valAxis, bandwidth) {
89179 var spanmode = trace.spanmode;
89180 var spanIn = trace.span || [];
89181 var spanTight = [cdi.min, cdi.max];
89182 var spanLoose = [cdi.min - 2 * bandwidth, cdi.max + 2 * bandwidth];
89183 var spanOut;
89184
89185 function calcSpanItem(index) {
89186 var s = spanIn[index];
89187 var sc = valAxis.type === 'multicategory' ?
89188 valAxis.r2c(s) :
89189 valAxis.d2c(s, 0, trace[cdi.valLetter + 'calendar']);
89190 return sc === BADNUM ? spanLoose[index] : sc;
89191 }
89192
89193 if(spanmode === 'soft') {
89194 spanOut = spanLoose;
89195 } else if(spanmode === 'hard') {
89196 spanOut = spanTight;
89197 } else {
89198 spanOut = [calcSpanItem(0), calcSpanItem(1)];
89199 }
89200
89201 // to reuse the equal-range-item block
89202 var dummyAx = {
89203 type: 'linear',
89204 range: spanOut
89205 };
89206 Axes.setConvert(dummyAx);
89207 dummyAx.cleanRange();
89208
89209 return spanOut;
89210}
89211
89212},{"../../constants/numerical":158,"../../lib":178,"../../plots/cartesian/axes":222,"../box/calc":297,"./helpers":428}],426:[function(_dereq_,module,exports){
89213/**
89214* Copyright 2012-2020, Plotly, Inc.
89215* All rights reserved.
89216*
89217* This source code is licensed under the MIT license found in the
89218* LICENSE file in the root directory of this source tree.
89219*/
89220
89221'use strict';
89222
89223var setPositionOffset = _dereq_('../box/cross_trace_calc').setPositionOffset;
89224var orientations = ['v', 'h'];
89225
89226module.exports = function crossTraceCalc(gd, plotinfo) {
89227 var calcdata = gd.calcdata;
89228 var xa = plotinfo.xaxis;
89229 var ya = plotinfo.yaxis;
89230
89231 for(var i = 0; i < orientations.length; i++) {
89232 var orientation = orientations[i];
89233 var posAxis = orientation === 'h' ? ya : xa;
89234 var violinList = [];
89235
89236 for(var j = 0; j < calcdata.length; j++) {
89237 var cd = calcdata[j];
89238 var t = cd[0].t;
89239 var trace = cd[0].trace;
89240
89241 if(trace.visible === true && trace.type === 'violin' &&
89242 !t.empty &&
89243 trace.orientation === orientation &&
89244 trace.xaxis === xa._id &&
89245 trace.yaxis === ya._id
89246 ) {
89247 violinList.push(j);
89248 }
89249 }
89250
89251 setPositionOffset('violin', gd, violinList, posAxis);
89252 }
89253};
89254
89255},{"../box/cross_trace_calc":298}],427:[function(_dereq_,module,exports){
89256/**
89257* Copyright 2012-2020, Plotly, Inc.
89258* All rights reserved.
89259*
89260* This source code is licensed under the MIT license found in the
89261* LICENSE file in the root directory of this source tree.
89262*/
89263
89264'use strict';
89265
89266var Lib = _dereq_('../../lib');
89267var Color = _dereq_('../../components/color');
89268
89269var boxDefaults = _dereq_('../box/defaults');
89270var attributes = _dereq_('./attributes');
89271
89272module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
89273 function coerce(attr, dflt) {
89274 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
89275 }
89276 function coerce2(attr, dflt) {
89277 return Lib.coerce2(traceIn, traceOut, attributes, attr, dflt);
89278 }
89279
89280 boxDefaults.handleSampleDefaults(traceIn, traceOut, coerce, layout);
89281 if(traceOut.visible === false) return;
89282
89283 coerce('bandwidth');
89284 coerce('side');
89285
89286 var width = coerce('width');
89287 if(!width) {
89288 coerce('scalegroup', traceOut.name);
89289 coerce('scalemode');
89290 }
89291
89292 var span = coerce('span');
89293 var spanmodeDflt;
89294 if(Array.isArray(span)) spanmodeDflt = 'manual';
89295 coerce('spanmode', spanmodeDflt);
89296
89297 var lineColor = coerce('line.color', (traceIn.marker || {}).color || defaultColor);
89298 var lineWidth = coerce('line.width');
89299 var fillColor = coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
89300
89301 boxDefaults.handlePointsDefaults(traceIn, traceOut, coerce, {prefix: ''});
89302
89303 var boxWidth = coerce2('box.width');
89304 var boxFillColor = coerce2('box.fillcolor', fillColor);
89305 var boxLineColor = coerce2('box.line.color', lineColor);
89306 var boxLineWidth = coerce2('box.line.width', lineWidth);
89307 var boxVisible = coerce('box.visible', Boolean(boxWidth || boxFillColor || boxLineColor || boxLineWidth));
89308 if(!boxVisible) traceOut.box = {visible: false};
89309
89310 var meanLineColor = coerce2('meanline.color', lineColor);
89311 var meanLineWidth = coerce2('meanline.width', lineWidth);
89312 var meanLineVisible = coerce('meanline.visible', Boolean(meanLineColor || meanLineWidth));
89313 if(!meanLineVisible) traceOut.meanline = {visible: false};
89314};
89315
89316},{"../../components/color":52,"../../lib":178,"../box/defaults":299,"./attributes":424}],428:[function(_dereq_,module,exports){
89317/**
89318* Copyright 2012-2020, Plotly, Inc.
89319* All rights reserved.
89320*
89321* This source code is licensed under the MIT license found in the
89322* LICENSE file in the root directory of this source tree.
89323*/
89324
89325'use strict';
89326
89327var Lib = _dereq_('../../lib');
89328
89329// Maybe add kernels more down the road,
89330// but note that the default `spanmode: 'soft'` bounds might have
89331// to become kernel-dependent
89332var kernels = {
89333 gaussian: function(v) {
89334 return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * v * v);
89335 }
89336};
89337
89338exports.makeKDE = function(calcItem, trace, vals) {
89339 var len = vals.length;
89340 var kernel = kernels.gaussian;
89341 var bandwidth = calcItem.bandwidth;
89342 var factor = 1 / (len * bandwidth);
89343
89344 // don't use Lib.aggNums to skip isNumeric checks
89345 return function(x) {
89346 var sum = 0;
89347 for(var i = 0; i < len; i++) {
89348 sum += kernel((x - vals[i]) / bandwidth);
89349 }
89350 return factor * sum;
89351 };
89352};
89353
89354exports.getPositionOnKdePath = function(calcItem, trace, valuePx) {
89355 var posLetter, valLetter;
89356
89357 if(trace.orientation === 'h') {
89358 posLetter = 'y';
89359 valLetter = 'x';
89360 } else {
89361 posLetter = 'x';
89362 valLetter = 'y';
89363 }
89364
89365 var pointOnPath = Lib.findPointOnPath(
89366 calcItem.path,
89367 valuePx,
89368 valLetter,
89369 {pathLength: calcItem.pathLength}
89370 );
89371
89372 var posCenterPx = calcItem.posCenterPx;
89373 var posOnPath0 = pointOnPath[posLetter];
89374 var posOnPath1 = trace.side === 'both' ?
89375 2 * posCenterPx - posOnPath0 :
89376 posCenterPx;
89377
89378 return [posOnPath0, posOnPath1];
89379};
89380
89381exports.getKdeValue = function(calcItem, trace, valueDist) {
89382 var vals = calcItem.pts.map(exports.extractVal);
89383 var kde = exports.makeKDE(calcItem, trace, vals);
89384 return kde(valueDist) / calcItem.posDensityScale;
89385};
89386
89387exports.extractVal = function(o) { return o.v; };
89388
89389},{"../../lib":178}],429:[function(_dereq_,module,exports){
89390/**
89391* Copyright 2012-2020, Plotly, Inc.
89392* All rights reserved.
89393*
89394* This source code is licensed under the MIT license found in the
89395* LICENSE file in the root directory of this source tree.
89396*/
89397
89398'use strict';
89399
89400var Lib = _dereq_('../../lib');
89401var Axes = _dereq_('../../plots/cartesian/axes');
89402var boxHoverPoints = _dereq_('../box/hover');
89403var helpers = _dereq_('./helpers');
89404
89405module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer) {
89406 var cd = pointData.cd;
89407 var trace = cd[0].trace;
89408 var hoveron = trace.hoveron;
89409 var hasHoveronViolins = hoveron.indexOf('violins') !== -1;
89410 var hasHoveronKDE = hoveron.indexOf('kde') !== -1;
89411 var closeData = [];
89412 var closePtData;
89413 var violinLineAttrs;
89414
89415 if(hasHoveronViolins || hasHoveronKDE) {
89416 var closeBoxData = boxHoverPoints.hoverOnBoxes(pointData, xval, yval, hovermode);
89417
89418 if(hasHoveronKDE && closeBoxData.length > 0) {
89419 var xa = pointData.xa;
89420 var ya = pointData.ya;
89421 var pLetter, vLetter, pAxis, vAxis, vVal;
89422
89423 if(trace.orientation === 'h') {
89424 vVal = xval;
89425 pLetter = 'y';
89426 pAxis = ya;
89427 vLetter = 'x';
89428 vAxis = xa;
89429 } else {
89430 vVal = yval;
89431 pLetter = 'x';
89432 pAxis = xa;
89433 vLetter = 'y';
89434 vAxis = ya;
89435 }
89436
89437 var di = cd[pointData.index];
89438
89439 if(vVal >= di.span[0] && vVal <= di.span[1]) {
89440 var kdePointData = Lib.extendFlat({}, pointData);
89441 var vValPx = vAxis.c2p(vVal, true);
89442 var kdeVal = helpers.getKdeValue(di, trace, vVal);
89443 var pOnPath = helpers.getPositionOnKdePath(di, trace, vValPx);
89444 var paOffset = pAxis._offset;
89445 var paLength = pAxis._length;
89446
89447 kdePointData[pLetter + '0'] = pOnPath[0];
89448 kdePointData[pLetter + '1'] = pOnPath[1];
89449 kdePointData[vLetter + '0'] = kdePointData[vLetter + '1'] = vValPx;
89450 kdePointData[vLetter + 'Label'] = vLetter + ': ' + Axes.hoverLabelText(vAxis, vVal) + ', ' + cd[0].t.labels.kde + ' ' + kdeVal.toFixed(3);
89451
89452 // move the spike to the KDE point
89453 kdePointData.spikeDistance = closeBoxData[0].spikeDistance;
89454 var spikePosAttr = pLetter + 'Spike';
89455 kdePointData[spikePosAttr] = closeBoxData[0][spikePosAttr];
89456 closeBoxData[0].spikeDistance = undefined;
89457 closeBoxData[0][spikePosAttr] = undefined;
89458
89459 // no hovertemplate support yet
89460 kdePointData.hovertemplate = false;
89461
89462 closeData.push(kdePointData);
89463
89464 violinLineAttrs = {stroke: pointData.color};
89465 violinLineAttrs[pLetter + '1'] = Lib.constrain(paOffset + pOnPath[0], paOffset, paOffset + paLength);
89466 violinLineAttrs[pLetter + '2'] = Lib.constrain(paOffset + pOnPath[1], paOffset, paOffset + paLength);
89467 violinLineAttrs[vLetter + '1'] = violinLineAttrs[vLetter + '2'] = vAxis._offset + vValPx;
89468 }
89469 }
89470
89471 if(hasHoveronViolins) {
89472 closeData = closeData.concat(closeBoxData);
89473 }
89474 }
89475
89476 if(hoveron.indexOf('points') !== -1) {
89477 closePtData = boxHoverPoints.hoverOnPoints(pointData, xval, yval);
89478 }
89479
89480 // update violin line (if any)
89481 var violinLine = hoverLayer.selectAll('.violinline-' + trace.uid)
89482 .data(violinLineAttrs ? [0] : []);
89483 violinLine.enter().append('line')
89484 .classed('violinline-' + trace.uid, true)
89485 .attr('stroke-width', 1.5);
89486 violinLine.exit().remove();
89487 violinLine.attr(violinLineAttrs);
89488
89489 // same combine logic as box hoverPoints
89490 if(hovermode === 'closest') {
89491 if(closePtData) return [closePtData];
89492 return closeData;
89493 }
89494 if(closePtData) {
89495 closeData.push(closePtData);
89496 return closeData;
89497 }
89498 return closeData;
89499};
89500
89501},{"../../lib":178,"../../plots/cartesian/axes":222,"../box/hover":301,"./helpers":428}],430:[function(_dereq_,module,exports){
89502/**
89503* Copyright 2012-2020, Plotly, Inc.
89504* All rights reserved.
89505*
89506* This source code is licensed under the MIT license found in the
89507* LICENSE file in the root directory of this source tree.
89508*/
89509
89510'use strict';
89511
89512module.exports = {
89513 attributes: _dereq_('./attributes'),
89514 layoutAttributes: _dereq_('./layout_attributes'),
89515 supplyDefaults: _dereq_('./defaults'),
89516 crossTraceDefaults: _dereq_('../box/defaults').crossTraceDefaults,
89517 supplyLayoutDefaults: _dereq_('./layout_defaults'),
89518 calc: _dereq_('./calc'),
89519 crossTraceCalc: _dereq_('./cross_trace_calc'),
89520 plot: _dereq_('./plot'),
89521 style: _dereq_('./style'),
89522 styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
89523 hoverPoints: _dereq_('./hover'),
89524 selectPoints: _dereq_('../box/select'),
89525
89526 moduleType: 'trace',
89527 name: 'violin',
89528 basePlotModule: _dereq_('../../plots/cartesian'),
89529 categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'violinLayout', 'zoomScale'],
89530 meta: {
89531
89532 }
89533};
89534
89535},{"../../plots/cartesian":235,"../box/defaults":299,"../box/select":306,"../scatter/style":412,"./attributes":424,"./calc":425,"./cross_trace_calc":426,"./defaults":427,"./hover":429,"./layout_attributes":431,"./layout_defaults":432,"./plot":433,"./style":434}],431:[function(_dereq_,module,exports){
89536/**
89537* Copyright 2012-2020, Plotly, Inc.
89538* All rights reserved.
89539*
89540* This source code is licensed under the MIT license found in the
89541* LICENSE file in the root directory of this source tree.
89542*/
89543
89544'use strict';
89545
89546var boxLayoutAttrs = _dereq_('../box/layout_attributes');
89547var extendFlat = _dereq_('../../lib').extendFlat;
89548
89549module.exports = {
89550 violinmode: extendFlat({}, boxLayoutAttrs.boxmode, {
89551
89552 }),
89553 violingap: extendFlat({}, boxLayoutAttrs.boxgap, {
89554
89555 }),
89556 violingroupgap: extendFlat({}, boxLayoutAttrs.boxgroupgap, {
89557
89558 })
89559};
89560
89561},{"../../lib":178,"../box/layout_attributes":303}],432:[function(_dereq_,module,exports){
89562/**
89563* Copyright 2012-2020, Plotly, Inc.
89564* All rights reserved.
89565*
89566* This source code is licensed under the MIT license found in the
89567* LICENSE file in the root directory of this source tree.
89568*/
89569
89570'use strict';
89571
89572var Lib = _dereq_('../../lib');
89573var layoutAttributes = _dereq_('./layout_attributes');
89574var boxLayoutDefaults = _dereq_('../box/layout_defaults');
89575
89576module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
89577 function coerce(attr, dflt) {
89578 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
89579 }
89580 boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin');
89581};
89582
89583},{"../../lib":178,"../box/layout_defaults":304,"./layout_attributes":431}],433:[function(_dereq_,module,exports){
89584/**
89585* Copyright 2012-2020, Plotly, Inc.
89586* All rights reserved.
89587*
89588* This source code is licensed under the MIT license found in the
89589* LICENSE file in the root directory of this source tree.
89590*/
89591
89592'use strict';
89593
89594var d3 = _dereq_('d3');
89595var Lib = _dereq_('../../lib');
89596var Drawing = _dereq_('../../components/drawing');
89597
89598var boxPlot = _dereq_('../box/plot');
89599var linePoints = _dereq_('../scatter/line_points');
89600var helpers = _dereq_('./helpers');
89601
89602module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
89603 var fullLayout = gd._fullLayout;
89604 var xa = plotinfo.xaxis;
89605 var ya = plotinfo.yaxis;
89606
89607 function makePath(pts) {
89608 var segments = linePoints(pts, {
89609 xaxis: xa,
89610 yaxis: ya,
89611 connectGaps: true,
89612 baseTolerance: 0.75,
89613 shape: 'spline',
89614 simplify: true,
89615 linearized: true
89616 });
89617 return Drawing.smoothopen(segments[0], 1);
89618 }
89619
89620 Lib.makeTraceGroups(violinLayer, cdViolins, 'trace violins').each(function(cd) {
89621 var plotGroup = d3.select(this);
89622 var cd0 = cd[0];
89623 var t = cd0.t;
89624 var trace = cd0.trace;
89625
89626 if(trace.visible !== true || t.empty) {
89627 plotGroup.remove();
89628 return;
89629 }
89630
89631 var bPos = t.bPos;
89632 var bdPos = t.bdPos;
89633 var valAxis = plotinfo[t.valLetter + 'axis'];
89634 var posAxis = plotinfo[t.posLetter + 'axis'];
89635 var hasBothSides = trace.side === 'both';
89636 var hasPositiveSide = hasBothSides || trace.side === 'positive';
89637 var hasNegativeSide = hasBothSides || trace.side === 'negative';
89638
89639 var violins = plotGroup.selectAll('path.violin').data(Lib.identity);
89640
89641 violins.enter().append('path')
89642 .style('vector-effect', 'non-scaling-stroke')
89643 .attr('class', 'violin');
89644
89645 violins.exit().remove();
89646
89647 violins.each(function(d) {
89648 var pathSel = d3.select(this);
89649 var density = d.density;
89650 var len = density.length;
89651 var posCenter = posAxis.c2l(d.pos + bPos, true);
89652 var posCenterPx = posAxis.l2p(posCenter);
89653
89654 var scale;
89655 if(trace.width) {
89656 scale = t.maxKDE / bdPos;
89657 } else {
89658 var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup];
89659 scale = trace.scalemode === 'count' ?
89660 (groupStats.maxKDE / bdPos) * (groupStats.maxCount / d.pts.length) :
89661 groupStats.maxKDE / bdPos;
89662 }
89663
89664 var pathPos, pathNeg, path;
89665 var i, k, pts, pt;
89666
89667 if(hasPositiveSide) {
89668 pts = new Array(len);
89669 for(i = 0; i < len; i++) {
89670 pt = pts[i] = {};
89671 pt[t.posLetter] = posCenter + (density[i].v / scale);
89672 pt[t.valLetter] = valAxis.c2l(density[i].t, true);
89673 }
89674 pathPos = makePath(pts);
89675 }
89676
89677 if(hasNegativeSide) {
89678 pts = new Array(len);
89679 for(k = 0, i = len - 1; k < len; k++, i--) {
89680 pt = pts[k] = {};
89681 pt[t.posLetter] = posCenter - (density[i].v / scale);
89682 pt[t.valLetter] = valAxis.c2l(density[i].t, true);
89683 }
89684 pathNeg = makePath(pts);
89685 }
89686
89687 if(hasBothSides) {
89688 path = pathPos + 'L' + pathNeg.substr(1) + 'Z';
89689 } else {
89690 var startPt = [posCenterPx, valAxis.c2p(density[0].t)];
89691 var endPt = [posCenterPx, valAxis.c2p(density[len - 1].t)];
89692
89693 if(trace.orientation === 'h') {
89694 startPt.reverse();
89695 endPt.reverse();
89696 }
89697
89698 if(hasPositiveSide) {
89699 path = 'M' + startPt + 'L' + pathPos.substr(1) + 'L' + endPt;
89700 } else {
89701 path = 'M' + endPt + 'L' + pathNeg.substr(1) + 'L' + startPt;
89702 }
89703 }
89704 pathSel.attr('d', path);
89705
89706 // save a few things used in getPositionOnKdePath, getKdeValue
89707 // on hover and for meanline draw block below
89708 d.posCenterPx = posCenterPx;
89709 d.posDensityScale = scale * bdPos;
89710 d.path = pathSel.node();
89711 d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1);
89712 });
89713
89714 var boxAttrs = trace.box;
89715 var boxWidth = boxAttrs.width;
89716 var boxLineWidth = (boxAttrs.line || {}).width;
89717 var bdPosScaled;
89718 var bPosPxOffset;
89719
89720 if(hasBothSides) {
89721 bdPosScaled = bdPos * boxWidth;
89722 bPosPxOffset = 0;
89723 } else if(hasPositiveSide) {
89724 bdPosScaled = [0, bdPos * boxWidth / 2];
89725 bPosPxOffset = boxLineWidth * {x: 1, y: -1}[t.posLetter];
89726 } else {
89727 bdPosScaled = [bdPos * boxWidth / 2, 0];
89728 bPosPxOffset = boxLineWidth * {x: -1, y: 1}[t.posLetter];
89729 }
89730
89731 // inner box
89732 boxPlot.plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, {
89733 bPos: bPos,
89734 bdPos: bdPosScaled,
89735 bPosPxOffset: bPosPxOffset
89736 });
89737
89738 // meanline insider box
89739 boxPlot.plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, {
89740 bPos: bPos,
89741 bdPos: bdPosScaled,
89742 bPosPxOffset: bPosPxOffset
89743 });
89744
89745 var fn;
89746 if(!trace.box.visible && trace.meanline.visible) {
89747 fn = Lib.identity;
89748 }
89749
89750 // N.B. use different class name than boxPlot.plotBoxMean,
89751 // to avoid selectAll conflict
89752 var meanPaths = plotGroup.selectAll('path.meanline').data(fn || []);
89753 meanPaths.enter().append('path')
89754 .attr('class', 'meanline')
89755 .style('fill', 'none')
89756 .style('vector-effect', 'non-scaling-stroke');
89757 meanPaths.exit().remove();
89758 meanPaths.each(function(d) {
89759 var v = valAxis.c2p(d.mean, true);
89760 var p = helpers.getPositionOnKdePath(d, trace, v);
89761
89762 d3.select(this).attr('d',
89763 trace.orientation === 'h' ?
89764 'M' + v + ',' + p[0] + 'V' + p[1] :
89765 'M' + p[0] + ',' + v + 'H' + p[1]
89766 );
89767 });
89768
89769 boxPlot.plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
89770 });
89771};
89772
89773},{"../../components/drawing":74,"../../lib":178,"../box/plot":305,"../scatter/line_points":403,"./helpers":428,"d3":16}],434:[function(_dereq_,module,exports){
89774/**
89775* Copyright 2012-2020, Plotly, Inc.
89776* All rights reserved.
89777*
89778* This source code is licensed under the MIT license found in the
89779* LICENSE file in the root directory of this source tree.
89780*/
89781
89782'use strict';
89783
89784var d3 = _dereq_('d3');
89785var Color = _dereq_('../../components/color');
89786var stylePoints = _dereq_('../scatter/style').stylePoints;
89787
89788module.exports = function style(gd) {
89789 var s = d3.select(gd).selectAll('g.trace.violins');
89790
89791 s.style('opacity', function(d) { return d[0].trace.opacity; });
89792
89793 s.each(function(d) {
89794 var trace = d[0].trace;
89795 var sel = d3.select(this);
89796 var box = trace.box || {};
89797 var boxLine = box.line || {};
89798 var meanline = trace.meanline || {};
89799 var meanLineWidth = meanline.width;
89800
89801 sel.selectAll('path.violin')
89802 .style('stroke-width', trace.line.width + 'px')
89803 .call(Color.stroke, trace.line.color)
89804 .call(Color.fill, trace.fillcolor);
89805
89806 sel.selectAll('path.box')
89807 .style('stroke-width', boxLine.width + 'px')
89808 .call(Color.stroke, boxLine.color)
89809 .call(Color.fill, box.fillcolor);
89810
89811 var meanLineStyle = {
89812 'stroke-width': meanLineWidth + 'px',
89813 'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
89814 };
89815
89816 sel.selectAll('path.mean')
89817 .style(meanLineStyle)
89818 .call(Color.stroke, meanline.color);
89819
89820 sel.selectAll('path.meanline')
89821 .style(meanLineStyle)
89822 .call(Color.stroke, meanline.color);
89823
89824 stylePoints(sel, trace, gd);
89825 });
89826};
89827
89828},{"../../components/color":52,"../scatter/style":412,"d3":16}],435:[function(_dereq_,module,exports){
89829/**
89830* Copyright 2012-2020, Plotly, Inc.
89831* All rights reserved.
89832*
89833* This source code is licensed under the MIT license found in the
89834* LICENSE file in the root directory of this source tree.
89835*/
89836
89837'use strict';
89838
89839// package version injected by `npm run preprocess`
89840exports.version = '1.54.0';
89841
89842},{}]},{},[11])(11)
89843});